diff --git a/.agents/skills/vcdoc-arf-sync/SKILL.md b/.agents/skills/vcdoc-arf-sync/SKILL.md new file mode 100644 index 0000000..cd51e19 --- /dev/null +++ b/.agents/skills/vcdoc-arf-sync/SKILL.md @@ -0,0 +1,150 @@ +--- +name: vcdoc-arf-sync +description: > + How to sync Thai VC ARF content from vc-document into the vcdoc Docusaurus + site. Covers the directory layout (docs/thai-vc-arf + docs/en/thai-vc-arf, + which replaced the old docs/trust-guide) and the required HTML->MDX comment + conversion. Use when importing/copying ARF markdown from vc-document, when a + sync reintroduces raw HTML comments or a duplicate docs/th/thai-vc-arf/, or + when the build fails with MDX "Unexpected character `!`" errors. +license: MIT +--- + +# vcdoc ARF sync & format conversion + +The vcdoc site (this repo) publishes the Thai VC ARF. The canonical source of +truth is the `vc-document` repo. Content is copied from vc-document into this +repo, but vc-document's files are NOT Docusaurus-ready as-is: they use a +different directory layout and raw HTML comments. Both must be fixed on import. + +## Directory layout + +vcdoc is a SINGLE-locale site (`defaultLocale: 'th'`, `locales: ['th']`, +`routeBasePath: '/'`). Thai content lives FLAT at the docs root. + +English is still SUPPORTED — but as a literal `docs/en/thai-vc-arf/` folder +served under `/en/`, not as a Docusaurus i18n locale. It holds a one-page +English summary (not a full translation), linked from the navbar +("VC stack (EN)" -> `/en/thai-vc-arf/minimal-interoperability-reference`). +Never delete `docs/en/` when working on the single-locale layout: + +``` +docs/ + thai-vc-arf/ # Thai chapters (22 .md files + images/) + _category_.json # label + link.slug "/" (homepage = Thai index) + 00-minimal-interoperability-reference.md + ... (chapters 1-16) + images/ + en/ + _category_.json + thai-vc-arf/ + _category_.json + 00-minimal-interoperability-reference.md +``` + +vc-document uses a locale-prefixed layout (`docs/th/thai-vc-arf/` and +`docs/en/thai-vc-arf/`). The sync must STRIP the `th/` prefix for Thai content: + +- `vc-document/th/thai-vc-arf/*` -> `docs/thai-vc-arf/*` (drop the `th/`) +- `vc-document/en/thai-vc-arf/*` -> `docs/en/thai-vc-arf/*` (keep the `en/`) + +### Directory rename: trust-guide -> thai-vc-arf + +- The OLD content directory was `docs/trust-guide/` (the original Trust + Framework manual). It is gone — replaced by `thai-vc-arf`. +- vc-document still versions the legacy trust-guide as + `versioned_docs/version-2026-09/trust-guide/`. vcdoc does NOT use Docusaurus + versioning — never copy `versioned_docs/`, `versioned_sidebars/`, + `versions.json`, `i18n/`, or vc-document's `src/theme/` swizzles into vcdoc. + +### Forbidden: the locale-prefixed duplicate + +Do NOT create `docs/th/thai-vc-arf/`. vc-document keeps Thai under a `th/` +locale prefix; vcdoc does not. Copying that path as-is produces a duplicate of +every Thai file (the exact bloat this skill exists to prevent). The canonical +Thai path in THIS repo is `docs/thai-vc-arf/`. + +## Format conversion (required for the build) + +vc-document files use HTML comments; Docusaurus MDX cannot compile them, and +the build fails with: + +``` +MDX compilation failed ... Unexpected character `!` (U+0021) before name +``` + +Two changes are needed on import: + +1. Convert top-level HTML comments `` to MDX comments `{/* ... */}`. +2. Add frontmatter to Thai `.md` files (the English file gets NO frontmatter): + +``` +--- +description: "

— Thai VC ARF 2.0 DRAFT 0" +--- +``` + +The `description` is the file's first `# ` heading plus the suffix +` — Thai VC ARF 2.0 DRAFT 0`. Exception: `README.md` uses the fixed value +`Thai VC ARF — 2.0 DRAFT 0 — Thai VC ARF 2.0 DRAFT 0`. + +### Comment conversion rules (must be exact) + +Convert ONLY top-level comments. Comments do not nest; a `` that +appears INSIDE an outer comment is literal text and must stay as ``. + +Do NOT touch: + +- Mermaid sequence arrows `-->>` (they contain `-->` as a prefix substring). +- Flow text such as `P1 --> P2 --> P3`. +- Literal comment examples inside comments, e.g. the README bullet + `` `` ``. + +Reference implementation (balanced, non-nested): + +```python +def convert_comments(text): + depth = 0 + out, i = [], 0 + while i < len(text): + if text.startswith('', i): + out.append('*/}' if depth == 1 else '-->') + depth = max(0, depth - 1) + i += 3 + else: + out.append(text[i]) + i += 1 + assert depth == 0, 'unbalanced comments' + return ''.join(out) +``` + +Then for Thai files: `'---\ndescription: "{desc}"\n---\n\n' + convert_comments(raw)`. +For the English file: `convert_comments(raw)` only (no frontmatter). + +## Verification + +After any sync or format change, confirm: + +``` +npm run build # must exit 0 +npm run typecheck # must exit 0 +``` + +- Homepage `/` must serve the Thai index titled "Thai VC ARF — 2.0 DRAFT 0". +- Thai docs must be at `/thai-vc-arf/...` (NOT `/th/thai-vc-arf/...`). +- Broken-link/anchors warnings are EXPECTED: the ARF markdown references + vc-document-only files (`../research/*`, `../policy/*`, `../public-site/*`, + `../../00-glossary.md`) that are not present here. The config intentionally + uses `onBrokenLinks: 'warn'`. + +## Known gotchas + +- A prior sync did two things wrong: it copied content to BOTH + `docs/th/thai-vc-arf/` and `docs/thai-vc-arf/`, AND it overwrote converted + files with raw HTML-comment versions. Check for both after every sync. +- `grep -c '-->'` over-counts: mermaid `-->>` arrows contain `-->`. Use the + balanced converter above, not a naive string replace. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..500c5d4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,89 @@ +# Validate the Docusaurus site on every push and pull request. +# +# This is the CI half of the pipeline (separate from deploy.yml, which is CD). +# It typechecks, builds, statically validates the build output, and then runs +# browser-based E2E tests that verify locale rendering and client-side mermaid +# hydration — both of which only happen in a real browser. +# +# Best practices applied: +# - npm ci (not npm install) for reproducible installs from package-lock.json +# - Node version pinned to match "engines.node" (>=20) in package.json +# - npm cache via actions/setup-node, plus a Playwright browser cache +# - concurrency group so a new push cancels the stale in-flight run +# - build/ and Playwright artifacts uploaded for inspection on failure +name: Validate Docusaurus + +on: + push: + branches: + - main + - dev + pull_request: + +# Read-only checkout is all this workflow needs. +permissions: + contents: read + +# Cancel the previous run on the same ref when a newer push arrives. +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Build site + run: npm run build + + # Static checks on build/: every doc generated, locale layout, no raw + # HTML comments, mermaid blocks present in source. + - name: Validate build output + run: npm run validate:build + + # Cache the Playwright browser binaries (keyed on the lockfile so a dep + # bump invalidates it). Saves ~150MB download on subsequent runs. + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }} + + # --with-deps installs the OS libraries Chromium needs on Ubuntu. + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + + # Serves build/ and verifies locale rendering + mermaid hydration. + - name: Browser E2E tests + run: npm run test:e2e + + - name: Upload site build + if: always() + uses: actions/upload-artifact@v4 + with: + name: site-build + path: build + retention-days: 7 + + - name: Upload Playwright report on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: playwright-report/ + retention-days: 7 diff --git a/.gitignore b/.gitignore index b2d6de3..58472f7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ .docusaurus .cache-loader +# Playwright +/test-results/ +/playwright-report/ + # Misc .DS_Store .env.local diff --git a/README.md b/README.md index b2948fd..74ac761 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # VC Document -> เอกสารและมาตรฐาน **Verifiable Credential (VC)** โดย ETDA — คู่มือ Trust Framework ของระบบ VC Ecosystem ประเทศไทย +> **Thai VC ARF — 2.0 DRAFT 0** กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย **🌐 เว็บไซต์ (อ่านเอกสารฉบับเต็ม):** **https://etda.github.io/vcdoc/** @@ -10,25 +10,13 @@ ## เกี่ยวกับโปรเจกต์นี้ -โปรเจกต์นี้คือเว็บไซต์เอกสาร (documentation site) ที่รวบรวม **คู่มือ Trust Framework** ของระบบ Verifiable Credential ในประเทศไทย -อธิบายว่ากรอบความน่าเชื่อถือ (Trust Framework) ของ ETDA ทำให้ทุกฝ่ายในระบบ VC เชื่อใจกันได้อย่างไร -ตั้งแต่การ **ออกเอกสารดิจิทัล (Issuance)** ไปจนถึงการ **แสดงเอกสารให้ผู้ตรวจสอบ (Presentation)** - -เนื้อหาแบ่งเป็น 9 ส่วนหลัก เรียงตามลำดับที่ควรอ่าน: - -| บท | หัวข้อ | เหมาะกับใคร | -|----|--------|-------------| -| 1 | Concepts & Roles — แนวคิดและบทบาท | ผู้เริ่มต้นทุกคน | -| 2 | Architecture — สถาปัตยกรรมภาพรวม | ผู้เริ่มต้น / สถาปนิกระบบ | -| 3 | Issuance Flow — ขั้นตอนการออกเอกสาร | นักพัฒนาฝั่งผู้ออกบัตร (Issuer) | -| 4 | Presentation Flow — ขั้นตอนการแสดงเอกสาร | นักพัฒนาฝั่งผู้ตรวจสอบ (Verifier) | -| 5 | Trust Infrastructure — โครงสร้างความน่าเชื่อถือ | นักพัฒนา / ผู้ดูแลระบบ | -| 6 | Security & Privacy — ความปลอดภัยและความเป็นส่วนตัว | ทุกฝ่าย | -| 7 | Governance & Conformance — การกำกับดูแลและการรับรอง | องค์กรที่ต้องการเข้าร่วมระบบ | -| 8 | FAQ — คำถามที่พบบ่อย | ทุกคน | -| 9 | Appendix — ภาคผนวก (สรุปกฎความปลอดภัย) | อ้างอิงเร็ว | - -> 💡 แนะนำ: ผู้เริ่มต้นอ่านบท 1–2 ก่อน, ผู้ออกบัตรเริ่มบท 3, ผู้ตรวจสอบเริ่มบท 4, องค์กรที่อยากเข้าร่วมดูบท 7 +เว็บไซต์นี้แสดง [Thai VC ARF ฉบับภาษาไทย](docs/thai-vc-arf/README.md) เป็นเนื้อหาหลัก ครอบคลุมบทสรุป บทที่ 1–16 และรูปประกอบ โดยแทนที่คู่มือ Trust Framework เดิมทั้งหมด + +- **เวอร์ชันเว็บไซต์:** `2.0 DRAFT 0` แสดงในเมนูเวอร์ชันของ Docusaurus +- **ต้นทางบทภาษาไทย:** `vc-document/th/thai-vc-arf/` +- [English summary](docs/en/thai-vc-arf/00-minimal-interoperability-reference.md) — คัดลอกจาก `vc-document/en/thai-vc-arf/`; แหล่งต้นทางมีบทสรุปภาษาอังกฤษหนึ่งหน้า ไม่ใช่คำแปลทุกบท + +ไฟล์ Markdown ที่นำเข้าปรับรูปแบบคอมเมนต์จาก HTML เป็น MDX เพื่อให้ Docusaurus สร้างเว็บไซต์ได้ โดยไม่แก้เนื้อหาหลักที่แสดงแก่ผู้อ่าน ## เทคโนโลยีที่ใช้ diff --git a/docs/en/_category_.json b/docs/en/_category_.json new file mode 100644 index 0000000..8171b1d --- /dev/null +++ b/docs/en/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "English", + "position": 2 +} diff --git a/docs/en/thai-vc-arf/00-minimal-interoperability-reference.md b/docs/en/thai-vc-arf/00-minimal-interoperability-reference.md new file mode 100644 index 0000000..e5b0889 --- /dev/null +++ b/docs/en/thai-vc-arf/00-minimal-interoperability-reference.md @@ -0,0 +1,163 @@ +# Thailand's VC stack + +{/* +AGENT NOTES (hidden — not rendered on the site): + +This page summarises the Thai chapters, which are the single +source of truth. Where this page and the Thai text differ, the Thai text is +correct. Each item links to the Thai chapter that defines it. + +Source chapters (paths under th/thai-vc-arf/ unless noted): +- Issuing a credential: 05-standards-and-compliance.md, 08-implementation-guidelines.md, 08.1-issuance-full-flow-detail.md +- Presenting a credential: 05-standards-and-compliance.md, 08-implementation-guidelines.md, 08.2-oid4vp-full-flow-detail.md +- The Trusted Lists: 00-minimal-interoperability-reference.md, 12.1-trust-list-publication-profile.md, th/research/42-trusted-list-lote-jwt-format.md (JWT structure); th/research/41-trustlist-serving-filecdn-etag-binary.md (CDN access) +- Revocation and status: 13-vc-status-and-revocation.md, 12-key-management-and-trustlist-deployment.md, 10-wallet-unit-attestation.md + +Full source of truth (Thai): ARF index (th/thai-vc-arf/README.md). +*/} + +This page describes Thailand's credential stack, in the terms a counterpart system uses to check compatibility. + +## Current PoC stage + +This page describes the planned specification. The current proof of concept implements only: + +1. A single LoTE that holds issuers, verifiers, and wallet providers. The target profile instead uses three separate signed JWTs. +2. A wallet app that trusts entities only through the Trusted List, with no certificates. Entities publish a public key in the Trusted List, either as a `did:web` reference or as a public key string. +3. Wallet Instance Attestation (WIA) is not implemented yet. + +## Short version + +The five building blocks: + +- **Credential format:** The system supports multiple formats. v1.1 used JSON-LD per W3C VC Data Model, which is still supported, and adds IETF SD-JWT VC (`dc+sd-jwt`) as the recommended format for new issuance. +- **Issuance protocol:** OpenID for Verifiable Credential Issuance (OID4VCI) 1.0 Final, pre-authorized code flow. +- **Presentation protocol:** OpenID for Verifiable Presentations (OID4VP) 1.0 Final, with DCQL queries. +- **Trust:** three related Trusted Lists with LoTE-inspired payloads in signed JWTs, served over a CDN. All three trust models use the relevant Trusted List; Trust Model 3 does not require a CA chain. +- **Credential status:** IETF Token Status List, published by each issuer. + +For a counterpart assessing convergence: Thailand uses the OID4VCI, OID4VP, and SD-JWT VC stack. Recognition of a Thai Trusted List entry in the EU requires a trust agreement, role and assurance mapping, a bridge list, and conformance evidence. + +## Issuing a credential + +Issuance runs OID4VCI 1.0 Final, pre-authorized code flow, and issues only SD-JWT VC (`dc+sd-jwt`). + +Before the wallet asks the user for the OTP, it checks the issuer against the Trusted List (TC-3 early). If the issuer is not `active` for that credential type, the flow stops. + +At assurance level IAL2.3 and above, the access token is bound to a wallet key with DPoP (RFC 9449). + +The credential request carries the wallet's attestation, signed by the wallet provider. The issuer checks the provider on the Trusted List (TC-2) and validates the attestation before signing. + +Before storing the VC, the wallet checks the issuer signature, the validity window, and the status bit (TC-3 final and TC-4). + +```mermaid +sequenceDiagram + participant U as User + participant W as Wallet + participant I as Issuer + participant TL as Trusted List + participant WP as Wallet Provider + participant SL as Status List + + Note over U,I: Request and offer + U->>I: requests credential (portal) + I->>U: offer QR + OTP by SMS + + Note over W,TL: Discovery + TC-3 early + W->>I: GET /.well-known/openid-credential-issuer + I-->>W: metadata: dc+sd-jwt configurations + W->>TL: TC-3 early: issuer active + type allowed + W->>U: asks for OTP (tx_code) + + Note over W,I: OTP + token (DPoP) + W->>I: POST /token + OTP + DPoP + I-->>W: access_token (DPoP-bound) + c_nonce + + Note over W,WP: Credential + TC-2 + W->>I: POST /credential + proof + wallet_attestation + I->>WP: TC-2: provider in list + WIA status + + Note over W,SL: Issue + TC-3/TC-4 + I->>W: issues SD-JWT VC (dc+sd-jwt) + W->>TL: TC-3 final: signature + validity + W->>SL: TC-4: read status bit + SL-->>W: bit 0 (valid) +``` + +## Presenting a credential + +Presentation runs OID4VP 1.0 Final with DCQL. + +Before showing a consent screen, the wallet checks the verifier against the Trusted List (TC-1): is the verifier `active`, and are the requested claims and purpose within what it registered? If not, the wallet sends nothing. + +The verifier then checks three things: + +- TC-2: the wallet provider is on the Trusted List and the wallet attestation is valid and fresh. This check is mandatory for Thai government wallets. For a private wallet, it depends on the verifier's confidence level (standard or enhanced). +- TC-3: the issuer is on the Trusted List, and the issuer's DID resolves to the key that signed the credential. +- TC-4: the status bit is read from the issuer's status list. + +The verifier must also check the issuer's status, scope, and the credential's own status before accepting it. + +Identifiers: organisations use `did:web`, holders use `did:jwk`, and Thai identity rails such as NDID resolve as custom methods through the ETDA Trust Gateway (the universal resolver). + +```mermaid +sequenceDiagram + participant U as User + participant W as Wallet + participant V as Verifier + participant TL as Trusted List + participant R as DID Resolver + participant SL as Status List + + Note over U,TL: Request + TC-1 wallet check + U->>W: scans authorization QR (DCQL) + W->>TL: TC-1: verifier role / status / scope + TL-->>W: active / allowed attributes/purposes + + Note over W,V: Consent + presentation + W->>U: consent: purpose + claims + U-->>W: approves (selective disclosure) + W->>V: vp_token (presentation + WIA-PoP) + + Note over V,TL: TC-2 wallet provider + V->>TL: TC-2: wallet provider in list + TL-->>V: provider active / WIA valid + + Note over V,TL: TC-3 issuer + keys (DID) + V->>R: TC-3: resolve issuer DID (kid) + R-->>V: DID document / verification key + V->>TL: TC-3: issuer entry + key match + + Note over V,SL: TC-4 status + result + V->>SL: TC-4: read status bit + SL-->>V: bit 0 (valid) + V->>W: granted / or 400 invalid_request +``` + +## The Trusted List + +ETDA audits organisations that act as issuers, verifiers, or wallet providers. The target profile publishes three related Trusted Lists as signed compact JWTs with LoTE-inspired payloads: `iss.txt`, `ver.txt`, and `wp.txt` on the [ETDA CDN profile](../../th/thai-vc-arf/12.1-trust-list-publication-profile.md). Each `.txt` file contains a compact JWT. Consumers fetch the relevant whole file on demand, without an API key, and validate ETDA's signature before using it. + +The [decoded JWT examples](../../th/research/42-trusted-list-lote-jwt-format.md) use `LoTE.TrustedEntitiesList[]` for organisations and `TrustedEntityServices[]` for services. The issuer example is a three-part compact JWS with one organisation and three issuance services. It shows public keys, a certificate, and identifiers. Organisation and service status and authorised credential types remain to be defined in the Thai profile, along with their validation rules, before a consumer can make an authorisation decision. + +Participants fetch the relevant whole-file Trusted List lazily when a connection needs a trust decision. They use `ETag` for revalidation, without an API key, background polling, or delta API. They fail closed if the file or its signature cannot be verified, or if required status and scope cannot be established. Entity-level revocation reaches each participant at its next lazy pull. + +Consumers determine an entity's authorisation from the Trusted List and obtain signature-verification keys from its linked DID document where applicable. ETDA signs the list from FIPS 140-3 Level 3 HSMs under an M-of-N quorum, with an offline emergency key. + +### Trusted List examples + +The JWTs below illustrate the LoTE structure. Their signatures remain unverified, and the issuer JWT lacks required status and scope fields. They cannot be used as authorisation test vectors. [Source analysis](../../th/research/42-trusted-list-lote-jwt-format.md). + +1. Issuer: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJpYXQiOjE3ODkwMzA2MDIsIng1YyI6WyJNSUlCUGpDQjVxQURBZ0VDQWhRelVlbmtacnB3TGFjVEhqMDd4RXo4bENpOFV6QUtCZ2dxaGtqT1BRUURBakFQTVEwd0N3WURWUVFEREFSRlZFUkJNQjRYRFRJMk1EZ3hNekUxTVRNME4xb1hEVEkzTURneE16RTFNVE0wTjFvd0R6RU5NQXNHQTFVRUF3d0VSVlJFUVRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkVKNnRGamJVVEJQQnRlcGFsWjVGUktMVDZxVnFrbzFvTk1naUlvdXI4RmFxUHAyQUxPbk90bGpwZnl4NmdoZ3YvV3lVWkgwRlhNdDluTTN2dTdzd25paklEQWVNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUc4Z1dWdjZBL2VjU2ZhWFJ2T014Q0kyR0FWYlNlTDk3ZmwyT2R4NCtwU1pBaUIwbXZ5MTB4VzN5dTJDc0ZnSkRqc1BaSTZBV3ZKeGd2VlptQXJMQTlhY1FRPT0iXSwieDV0I1MyNTYiOiJsY1BDaTAxaVFfbkIyQWRZRzVKcEMxTlh3dEVncm9GczhVT2hma21GM2ZzIn0.eyJMb1RFIjp7Ikxpc3RBbmRTY2hlbWVJbmZvcm1hdGlvbiI6eyJMb1RFVmVyc2lvbklkZW50aWZpZXIiOjEsIkxvVEVTZXF1ZW5jZU51bWJlciI6MiwiTG9URVR5cGUiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL0xvVEVUeXBlL0VVUHViRUFBUHJvdmlkZXJzTGlzdCIsIlNjaGVtZU9wZXJhdG9yTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJFbGVjdHJvbmljIFRyYW5zYWN0aW9ucyBEZXZlbG9wbWVudCBBZ2VuY3kgKEVUREEpIn1dLCJTY2hlbWVJbmZvcm1hdGlvblVSSSI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwczovL3d3dy5ldGRhLm9yLnRoIn1dLCJTdGF0dXNEZXRlcm1pbmF0aW9uQXBwcm9hY2giOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvVGhhaUlzc3VlcnNMaXN0L1N0YXR1c0RldG4vRVREQSIsIlNjaGVtZVR5cGVDb21tdW5pdHlSdWxlcyI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvVGhhaUlzc3VlcnMvc2NoZW1lcnVsZXMvRVREQSJ9XSwiU2NoZW1lVGVycml0b3J5IjoiVEgiLCJTY2hlbWVOYW1lIjpbeyJsYW5nIjoiZW4iLCJ2YWx1ZSI6IlRoYWkgSXNzdWVycyBMaXN0In1dLCJIaXN0b3JpY2FsSW5mb3JtYXRpb25QZXJpb2QiOjM2NSwiRGlzdHJpYnV0aW9uUG9pbnRzIjpbXSwiU2NoZW1lRXh0ZW5zaW9ucyI6W10sIkxpc3RJc3N1ZURhdGVUaW1lIjoiMjAyNi0wOS0xMFQwODo1Njo0Mi45NzdaIiwiTmV4dFVwZGF0ZSI6IjIwMjYtMDktMTFUMDg6NTY6NDIuOTc3WiJ9LCJUcnVzdGVkRW50aXRpZXNMaXN0IjpbeyJUcnVzdGVkRW50aXR5SW5mb3JtYXRpb24iOnsiVEVOYW1lIjpbeyJsYW5nIjoiZW4iLCJ2YWx1ZSI6IkVsZWN0cm9uaWMgVHJhbnNhY3Rpb25zIERldmVsb3BtZW50IEFnZW5jeSAoRVREQSkifV19LCJUcnVzdGVkRW50aXR5U2VydmljZXMiOlt7IlNlcnZpY2VJbmZvcm1hdGlvbiI6eyJTZXJ2aWNlVHlwZUlkZW50aWZpZXIiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1N2Y1R5cGUvUHViRUFBL0lzc3VhbmNlIiwiU2VydmljZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiUm9sZSBtb2RlbCBjZXJ0aWZpY2F0ZSJ9XSwiU2VydmljZURpZ2l0YWxJZGVudGl0eSI6eyJQdWJsaWNLZXlWYWx1ZXMiOlt7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4IjoiaEZCU0ZTZXJRblBMMVBOVXA5TnZRTDlqUUJPamJmSU1ScGRwUUEyaUFnSSJ9XSwiT3RoZXJJZHMiOlsiZGlkOmtleTp6Nk1rb01rdTFlYVlGRDdyR25oUzlyRDJiR3l5ZHZQbUUzVWlXNTdNUVlkQVlqSlIiXX19fSx7IlNlcnZpY2VJbmZvcm1hdGlvbiI6eyJTZXJ2aWNlVHlwZUlkZW50aWZpZXIiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1N2Y1R5cGUvUHViRUFBL0lzc3VhbmNlIiwiU2VydmljZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiRW1wbG95ZWVJRCBkZW1vIn1dLCJTZXJ2aWNlRGlnaXRhbElkZW50aXR5Ijp7Ik90aGVySWRzIjpbXSwiWDUwOVNLSXMiOltdLCJQdWJsaWNLZXlWYWx1ZXMiOltdLCJYNTA5Q2VydGlmaWNhdGVzIjpbeyJ2YWwiOiJNSUlCbERDQ0FVYWdBd0lCQWdJVWIxYjlLVHdDTEVidHR6Wit2VXdTRGtYS2tOTXdCUVlESzJWd01EWXhGVEFUQmdOVkJBTU1ERUZqYldVZ1VtOXZkQ0JEUVRFTE1Ba0dBMVVFQmd3Q1EwZ3hFREFPQmdOVkJBb01CMEZqYldVZ1EyOHdIaGNOTWpZd09ESTRNREF3TURBd1doY05NamN3T0RNd01EQXdNREF3V2pBMk1SVXdFd1lEVlFRRERBeEJZMjFsSUZKdmIzUWdRMEV4Q3pBSkJnTlZCQVlNQWtOSU1SQXdEZ1lEVlFRS0RBZEJZMjFsSUVOdk1Db3dCUVlESzJWd0F5RUFKY0FXbk91Y0NjMU1FNVhOMjNtSDBlcmpNTnRkWi83YlpJeXdXNmZMZ1dpalpqQmtNQjhHQTFVZEl3UVlNQmFBRk9MTlNYSmZaVncwYklKNDdLY1RyWFdiR1AvZk1BNEdBMVVkRHdFQi93UUVBd0lCQmpBZEJnTlZIUTRFRmdRVTRzMUpjbDlsWERSc2duanNweE90ZFpzWS85OHdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFGQmdNclpYQURRUUQvZ3g5a1MzSCt2WVdQcCtCS0I2dmRTMmZMY1hqSDk0dTNrdmgyc3Z4Z2xqMnU4SVY4em5lQTVudjhzNWZyNlNCMTdOclBaMXl6cHZkYU1JRzVqRXdNIiwic3BlY1JlZiI6IiIsImVuY29kaW5nIjoiIn1dLCJYNTA5U3ViamVjdE5hbWVzIjpbXX19fSx7IlNlcnZpY2VJbmZvcm1hdGlvbiI6eyJTZXJ2aWNlVHlwZUlkZW50aWZpZXIiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1N2Y1R5cGUvUHViRUFBL0lzc3VhbmNlIiwiU2VydmljZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiUHJvY2l2aXMgRGVtbyBQSUQgSXNzdWVyIn1dLCJTZXJ2aWNlRGlnaXRhbElkZW50aXR5Ijp7IlB1YmxpY0tleVZhbHVlcyI6W3sia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJwMFk0MVhZU2RxMGljOGV2Wi1tT1RPenllQlhWOE1ZbnRIMGJfSEJqb00wIiwieSI6IlU3ODVFbnZyR3VNSlVnWDNLVGdrUzV6c2RxQjhfVWV0ZW1HM1NIcEJxZjAifV0sIk90aGVySWRzIjpbImRpZDp3ZWI6aXNzdWVyLnRvbnloZXJlLndvcms6c3NpOmRpZC13ZWI6djE6NDdhNzRjMTctZGU1Ny00ODc2LWFkYTUtZDcwNzg2YWU4MDM4Il19fX1dfV19fQ.OGq2Sj4pbb1csy_kOzWa_gbeMPbBaL6MhWv-yQLykWrQ56gabFZaJfM3eZmyhUYJRfYp-7kevUCBxaRpLBvhKQ +2. Verifier: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJpYXQiOjE3ODkwMzUwMzMsIng1YyI6WyJNSUlCUGpDQjVxQURBZ0VDQWhRelVlbmtacnB3TGFjVEhqMDd4RXo4bENpOFV6QUtCZ2dxaGtqT1BRUURBakFQTVEwd0N3WURWUVFEREFSRlZFUkJNQjRYRFRJMk1EZ3hNekUxTVRNME4xb1hEVEkzTURneE16RTFNVE0wTjFvd0R6RU5NQXNHQTFVRUF3d0VSVlJFUVRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkVKNnRGamJVVEJQQnRlcGFsWjVGUktMVDZxVnFrbzFvTk1naUlvdXI4RmFxUHAyQUxPbk90bGpwZnl4NmdoZ3YvV3lVWkgwRlhNdDluTTN2dTdzd25paklEQWVNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUc4Z1dWdjZBL2VjU2ZhWFJ2T014Q0kyR0FWYlNlTDk3ZmwyT2R4NCtwU1pBaUIwbXZ5MTB4VzN5dTJDc0ZnSkRqc1BaSTZBV3ZKeGd2VlptQXJMQTlhY1FRPT0iXSwieDV0I1MyNTYiOiJsY1BDaTAxaVFfbkIyQWRZRzVKcEMxTlh3dEVncm9GczhVT2hma21GM2ZzIn0.eyJMb1RFIjp7Ikxpc3RBbmRTY2hlbWVJbmZvcm1hdGlvbiI6eyJMb1RFVmVyc2lvbklkZW50aWZpZXIiOjEsIkxvVEVTZXF1ZW5jZU51bWJlciI6MiwiTG9URVR5cGUiOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvTG9URVR5cGUvVGhhaVZlcmlmaWVyc0xpc3QiLCJTY2hlbWVPcGVyYXRvck5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiRWxlY3Ryb25pYyBUcmFuc2FjdGlvbnMgRGV2ZWxvcG1lbnQgQWdlbmN5IChFVERBKSJ9XSwiU2NoZW1lSW5mb3JtYXRpb25VUkkiOlt7ImxhbmciOiJlbiIsInVyaVZhbHVlIjoiaHR0cHM6Ly93d3cuZXRkYS5vci50aCJ9XSwiU3RhdHVzRGV0ZXJtaW5hdGlvbkFwcHJvYWNoIjoiaHR0cDovL3VyaS5ldGRhLm9yLnRoLzE5NjAyL1RoYWlWZXJpZmllcnNMaXN0L1N0YXR1c0RldG4vRVREQSIsIlNjaGVtZVR5cGVDb21tdW5pdHlSdWxlcyI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvVGhhaVZlcmlmaWVycy9zY2hlbWVydWxlcy9FVERBIn1dLCJTY2hlbWVUZXJyaXRvcnkiOiJUSCIsIlNjaGVtZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiVGhhaSBWZXJpZmllcnMgTGlzdCJ9XSwiSGlzdG9yaWNhbEluZm9ybWF0aW9uUGVyaW9kIjozNjUsIkRpc3RyaWJ1dGlvblBvaW50cyI6W10sIlNjaGVtZUV4dGVuc2lvbnMiOltdLCJMaXN0SXNzdWVEYXRlVGltZSI6IjIwMjYtMDktMTBUMTA6MTA6MzMuOTI4WiIsIk5leHRVcGRhdGUiOiIyMDI2LTA5LTExVDEwOjEwOjMzLjkyOFoifSwiVHJ1c3RlZEVudGl0aWVzTGlzdCI6W3siVHJ1c3RlZEVudGl0eUluZm9ybWF0aW9uIjp7IlRFTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJFbGVjdHJvbmljIFRyYW5zYWN0aW9ucyBEZXZlbG9wbWVudCBBZ2VuY3kgKEVUREEpIn1dfSwiVHJ1c3RlZEVudGl0eVNlcnZpY2VzIjpbeyJTZXJ2aWNlSW5mb3JtYXRpb24iOnsiU2VydmljZVR5cGVJZGVudGlmaWVyIjoiaHR0cDovL3VyaS5ldGRhLm9yLnRoLzE5NjAyL1N2Y1R5cGUvVmVyaWZpZXIiLCJTZXJ2aWNlTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJQcm9jaXZpcyBEZW1vIEJhbmsgVmVyaWZpZXIifV0sIlNlcnZpY2VEaWdpdGFsSWRlbnRpdHkiOnsiUHVibGljS2V5VmFsdWVzIjpbeyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IjVYemtpV0s4UWl1Y3N3SENLakk1S3dyM3JXZkZ6SDlBWElhN0FwMXpDeTgiLCJ5IjoidU9XYThGZU5nbTlyc0hiZmpBczZxTVZnb21Fa1JoMHl1QS1JRjhQUXFwRSJ9XSwiT3RoZXJJZHMiOlsiZGlkOndlYjp2ZXJpZmllci50b255aGVyZS53b3JrOnNzaTpkaWQtd2ViOnYxOmU1NDNlYTBlLTBhMTAtNGZmNS1iOGZhLTE0MTRmMGYzYWI5YyJdfX19XX1dfX0.u92cxz56yft9MttrtaOGJ7keqWsV8GX1BLfNQaMHToaQKnyrqcWBgkfrVQ5aRJHtociOro9EwGhWQrbiwHB-ag +3. Wallet Provider: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJpYXQiOjE3ODg3ODExNDgsIng1YyI6WyJNSUlCUGpDQjVxQURBZ0VDQWhRelVlbmtacnB3TGFjVEhqMDd4RXo4bENpOFV6QUtCZ2dxaGtqT1BRUURBakFQTVEwd0N3WURWUVFEREFSRlZFUkJNQjRYRFRJMk1EZ3hNekUxTVRNME4xb1hEVEkzTURneE16RTFNVE0wTjFvd0R6RU5NQXNHQTFVRUF3d0VSVlJFUVRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkVKNnRGamJVVEJQQnRlcGFsWjVGUktMVDZxVnFrbzFvTk1naUlvdXI4RmFxUHAyQUxPbk90bGpwZnl4NmdoZ3YvV3lVWkgwRlhNdDluTTN2dTdzd25paklEQWVNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUc4Z1dWdjZBL2VjU2ZhWFJ2T014Q0kyR0FWYlNlTDk3ZmwyT2R4NCtwU1pBaUIwbXZ5MTB4VzN5dTJDc0ZnSkRqc1BaSTZBV3ZKeGd2VlptQXJMQTlhY1FRPT0iXSwieDV0I1MyNTYiOiJsY1BDaTAxaVFfbkIyQWRZRzVKcEMxTlh3dEVncm9GczhVT2hma21GM2ZzIn0.eyJMb1RFIjp7Ikxpc3RBbmRTY2hlbWVJbmZvcm1hdGlvbiI6eyJMb1RFVmVyc2lvbklkZW50aWZpZXIiOjEsIkxvVEVTZXF1ZW5jZU51bWJlciI6MSwiTG9URVR5cGUiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL0xvVEVUeXBlL0VVV2FsbGV0UHJvdmlkZXJzTGlzdCIsIlNjaGVtZU9wZXJhdG9yTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJFbGVjdHJvbmljIFRyYW5zYWN0aW9ucyBEZXZlbG9wbWVudCBBZ2VuY3kgKEVUREEpIn1dLCJTY2hlbWVJbmZvcm1hdGlvblVSSSI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwczovL3d3dy5ldGRhLm9yLnRoIn1dLCJTdGF0dXNEZXRlcm1pbmF0aW9uQXBwcm9hY2giOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1dhbGxldFByb3ZpZGVyc0xpc3QvU3RhdHVzRGV0bi9FVSIsIlNjaGVtZVR5cGVDb21tdW5pdHlSdWxlcyI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1dhbGxldFByb3ZpZGVyc0xpc3Qvc2NoZW1lcnVsZXMvRVUifV0sIlNjaGVtZVRlcnJpdG9yeSI6IlRIIiwiU2NoZW1lTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJXYWxsZXQgUHJvdmlkZXJzIExpc3QifV0sIkhpc3RvcmljYWxJbmZvcm1hdGlvblBlcmlvZCI6MzY1LCJEaXN0cmlidXRpb25Qb2ludHMiOltdLCJTY2hlbWVFeHRlbnNpb25zIjpbXSwiTGlzdElzc3VlRGF0ZVRpbWUiOiIyMDI2LTA5LTA3VDExOjM5OjA4LjM5NVoiLCJOZXh0VXBkYXRlIjoiMjAyNi0wOS0wOFQxMTozOTowOC4zOTVaIn0sIlRydXN0ZWRFbnRpdGllc0xpc3QiOltdfX0.Izh_JYqPikXvdYGKdYJY6I26VuWuLtWwCX1eKtvqxnVSMRsxxtV_K7Wx6lbp_1C7RemaFT52ZrAOZa5t1SmZ6g + +## Revocation and status + +Each issuer publishes its own Token Status List (`draft-ietf-oauth-status-list-18`). Status bits: 0 valid, 1 revoked, 2 suspended, 3 rotated (key rotation). + +Issuers target publication of a change within 15 minutes; the full process may take longer. Verifiers must validate the status URL against the source defined by the Thai profile, check the credential-supplied URL against that source, and read the status bit on every presentation (TC-4). The current JWT examples do not show a status-URL field. [Source analysis](../../th/research/42-trusted-list-lote-jwt-format.md). + +Revocation also happens above the credential level. If ETDA suspends or withdraws an issuer, all of that issuer's credentials stop verifying without any per-credential write, because the issuer's role record is no longer `active`. If a wallet provider revokes a wallet attestation, issuers that bound credentials to that wallet unit must revoke them in turn. Every credential also dies at `validUntil` without any of the above. + +Emergency revocation reaches each entity at its next lazy pull, with no background polling and no push channel. diff --git a/docs/en/thai-vc-arf/_category_.json b/docs/en/thai-vc-arf/_category_.json new file mode 100644 index 0000000..2c4e134 --- /dev/null +++ b/docs/en/thai-vc-arf/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Thailand's VC stack", + "position": 1, + "link": { + "type": "doc", + "id": "en/thai-vc-arf/minimal-interoperability-reference" + } +} diff --git a/docs/thai-vc-arf/00-minimal-interoperability-reference.md b/docs/thai-vc-arf/00-minimal-interoperability-reference.md new file mode 100644 index 0000000..665bab7 --- /dev/null +++ b/docs/thai-vc-arf/00-minimal-interoperability-reference.md @@ -0,0 +1,190 @@ +--- +description: "ชุด VC ของประเทศไทย — สรุปหนึ่งหน้า — Thai VC ARF 2.0 DRAFT 0" +--- + +# ชุด VC ของประเทศไทย — สรุปหนึ่งหน้า + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — บทสรุปหนึ่งหน้า จัดทำขึ้นเพื่อให้ระบบของคู่ภาคี (counterpart system) ใช้ตรวจสอบความสอดคล้องกัน มิได้เป็นบทในรายงานต้นฉบับของ สพธอ. +> **แหล่งข้อมูล:** [Thai VC ARF](README.md) — รายงานทางเทคนิคของสำนักงานพัฒนาธุรกรรมทางอิเล็กทรอนิกส์ (สพธอ.) เวอร์ชัน 1.1 และส่วนขยายเวอร์ชัน 2.0/2.1 ซึ่งเป็นแหล่งข้อมูลอ้างอิงหลักของเอกสารฉบับนี้ +> **เอกสารที่เกี่ยวข้อง:** [§5 มาตรฐานและข้อปฏิบัติ](05-standards-and-compliance.md) · [§8 Implementation Guidelines](08-implementation-guidelines.md) · [§8.1 OID4VCI Full Flow](08.1-issuance-full-flow-detail.md) · [§8.2 OID4VP Full Flow](08.2-oid4vp-full-flow-detail.md) · [§6.5 กลไกการสร้างความน่าเชื่อถือ](06.5-trust-building-mechanism.md) · [§7 การทำงานร่วมกันข้ามระบบนิเวศ](07-cross-ecosystem-interoperability.md) · [§9 Trust Model 3](09-trust-model-3.md) · [§12 Key Management และ Trusted List Deployment](12-key-management-and-trustlist-deployment.md) · [§13 VC Status และ Revocation](13-vc-status-and-revocation.md) · [research/41 การให้บริการ Trusted List ผ่าน CDN](../research/41-trustlist-serving-filecdn-etag-binary.md) +*/} + +--- + +{/* +บันทึกสำหรับ agent (ซ่อน — ไม่แสดงบนเว็บไซต์): + +บทความนี้สรุปบทภาษาไทยใน th/thai-vc-arf/ ซึ่งเป็นแหล่งข้อมูลอ้างอิงหลัก +กรณีบทความนี้กับบทภาษาไทยขัดกัน ให้ยึดบทภาษาไทย + +บทต้นทาง (path ภายใต้ th/thai-vc-arf/ เว้นแต่ระบุไว้เป็นอย่างอื่น): +- การออก VC: 05-standards-and-compliance.md, 08-implementation-guidelines.md, 08.1-issuance-full-flow-detail.md +- การแสดง VC: 05-standards-and-compliance.md, 08-implementation-guidelines.md, 08.2-oid4vp-full-flow-detail.md +- Trusted List: 06.5-trust-building-mechanism.md, 07-cross-ecosystem-interoperability.md, 09-trust-model-3.md, 12-key-management-and-trustlist-deployment.md, th/research/41-trustlist-serving-filecdn-etag-binary.md +- การเพิกถอนและสถานะ: 13-vc-status-and-revocation.md, 12-key-management-and-trustlist-deployment.md, 10-wallet-unit-attestation.md + +ดัชนีแหล่งข้อมูลอ้างอิงหลัก (ภาษาไทย): th/thai-vc-arf/README.md +ฉบับภาษาอังกฤษของบทสรุปนี้: en/thai-vc-arf/00-minimal-interoperability-reference.md +*/} + +หน้านี้อธิบายชุดมาตรฐาน VC ของประเทศไทย ในมุมที่ระบบคู่ภาคีใช้ตรวจสอบความเข้ากันได้ + +## สถานะ PoC ปัจจุบัน + +หน้านี้กำหนดรูปแบบเป้าหมายสำหรับการพัฒนา ต้นแบบ (PoC) ปัจจุบันมี 3 ส่วน: + +1. LoTE (List of Trusted Entities) รายการเดียวที่บรรจุผู้ออก ผู้ตรวจ และผู้ให้บริการกระเป๋าทั้งหมด +2. แอปกระเป๋าที่เชื่อถือเอนทิตีผ่าน Trusted List เท่านั้น ไม่ใช้ใบรับรอง เอนทิตีแต่ละรายประกาศกุญแจสาธารณะใน Trusted List ในรูป `did:web` หรือสายอักขระกุญแจสาธารณะ +3. Wallet Instance Attestation (WIA) ยังไม่ได้ทำ + +## ฉบับย่อ + +องค์ประกอบหลัก 5 ส่วน คือ + +- **รูปแบบ VC:** ระบบรองรับหลายรูปแบบ โดย v1.1 ใช้ JSON-LD ตาม W3C VC Data Model ซึ่งยังคงรองรับ และเพิ่ม IETF SD-JWT VC (`dc+sd-jwt`) เป็นรูปแบบที่แนะนำสำหรับการออกใหม่ +- **โปรโตคอลการออก:** OpenID for Verifiable Credential Issuance (OID4VCI) 1.0 Final แบบ pre-authorized code flow +- **โปรโตคอลการแสดง:** OpenID for Verifiable Presentations (OID4VP) 1.0 Final พร้อม DCQL +- **ความน่าเชื่อถือ:** Trusted List ตาม ETSI TS 119 602 ซึ่ง สพธอ. เป็นผู้ลงลายมือชื่อและเผยแพร่ผ่าน CDN โดยไม่ใช้สายใบรับรองของ CA +- **สถานะ VC:** IETF Token Status List ซึ่งผู้ออกแต่ละรายเผยแพร่เอง + +สำหรับคู่ภาคีที่ประเมินการลู่เข้าหากัน (convergence): ประเทศไทยใช้ชุด OID4VCI + OID4VP + SD-JWT VC เดียวกับ EU แต่รายการใน Trusted List ของไทยไม่ได้รับการยอมรับใน EU โดยอัตโนมัติ การยอมรับต้องมีข้อตกลงความน่าเชื่อถือ (trust agreement) การเทียบบทบาทและระดับการรับรอง (trust mapping) รายการเชื่อม (bridge list) และหลักฐานการทดสอบความสอดคล้อง (conformance evidence) + +## การออก VC + +การออกใช้ OID4VCI 1.0 Final แบบ pre-authorized code flow ระบบยังคงรองรับ VC เดิมตาม W3C Verifiable Credentials Data Model v1.1 และเพิ่ม SD-JWT VC (`dc+sd-jwt`) สำหรับการออกเอกสารใหม่ + +ก่อนขอรหัส OTP จากผู้ใช้ กระเป๋าจะตรวจผู้ออกกับ Trusted List ก่อน (Trust Check 3 รอบแรก ย่อเป็น TC-3 รอบแรก) ถ้าผู้ออกไม่ได้ `active` สำหรับประเภท VC นั้น กระบวนการจะหยุดทันที + +ที่ระดับ IAL2.3 ขึ้นไป access token จะผูกกับกุญแจของกระเป๋าด้วย DPoP (RFC 9449) + +หากเลือกใช้ Wallet Attestation client authentication ตาม OID4VCI Appendix E, Wallet ส่ง Attestation ใน PAR หรือ Token Request พร้อม Client Attestation PoP ตามมาตรฐานที่อ้างถึง. Authorization Server ตรวจลายมือชื่อ Attestation จาก Issuer ที่เชื่อถือและตรวจ PoP. Credential Request ส่ง `proofs.jwt` เพื่อพิสูจน์การครอบครองกุญแจที่จะผูกกับ VC. Trust source และการตรวจสถานะ Wallet เพิ่มเติมเป็นไปตาม Thai profile. แผนภาพนี้ใช้ Pre-Authorized Code Flow จึงแสดง client authentication ที่ Token Request. + +ก่อนจัดเก็บ VC กระเป๋าจะตรวจลายมือชื่อของผู้ออก ช่วงเวลาที่ใช้ได้ และค่า status ใน Status List (TC-3 รอบที่สอง และ TC-4) + +```mermaid +sequenceDiagram + participant U as ผู้ใช้ + participant W as กระเป๋าเอกสารดิจิทัล + participant I as ผู้ออกเอกสาร + participant TL as Trusted List (สพธอ.) + participant WP as ผู้ให้บริการกระเป๋า + participant SL as รายการสถานะ + + Note over U,I: ขอและรับคำเชิญ (credential offer) + U->>I: ขอ VC (portal) + I->>U: QR ของคำเชิญ + OTP ทาง SMS + + Note over W,TL: ค้นหาข้อมูล + TC-3 รอบแรก + W->>I: GET /.well-known/openid-credential-issuer + I-->>W: metadata: credential configuration ของ dc+sd-jwt + W->>TL: TC-3 รอบแรก: ผู้ออก active + ประเภท VC ที่อนุญาต + W->>U: ขอรหัส OTP (tx_code) + + Note over W,I: OTP + token (TC-2 เมื่อ Thai profile กำหนด) + W->>I: POST /token + OTP, Wallet Attestation/Client Attestation PoP หากใช้กลไกนี้ + I->>I: ตรวจ Attestation จาก Issuer ที่เชื่อถือและ PoP, ตรวจสถานะเพิ่มเติมตาม Thai profile + I-->>W: Token Response: access_token + opt Issuer Metadata ระบุ Nonce Endpoint + W->>I: POST Nonce Endpoint (ไม่ใช้ Access Token) + I-->>W: 2xx + c_nonce และ Cache-Control: no-store + end + + Note over W,SL: ออก VC + TC-3/TC-4 + W->>I: POST /credential + proofs.jwt (nonce เมื่อกำหนด) + I->>W: ออก SD-JWT VC (dc+sd-jwt) + W->>TL: TC-3 รอบที่สอง: ลายมือชื่อ + ช่วงเวลาที่ใช้ได้ + W->>SL: TC-4: อ่านค่า status ที่ idx ตาม bits + SL-->>W: 0x00 (VALID) +``` + +## การแสดง VC + +การแสดงใช้ OID4VP 1.0 Final พร้อม DCQL แทน Presentation Exchange + +ก่อนแสดงหน้าขอความยินยอม กระเป๋าจะตรวจผู้ตรวจกับ Trusted List (Trust Check 1 ย่อเป็น TC-1) ว่า `active` อยู่หรือไม่ และข้อความยืนยัน (claims) กับวัตถุประสงค์ (purpose) ที่ขอ อยู่ในขอบเขตที่ลงทะเบียนไว้หรือไม่ ถ้าไม่ กระเป๋าจะไม่ส่งอะไรออกไปเลย + +จากนั้นผู้ตรวจจะตรวจ 3 ข้อ คือ + +- Trust Check 2 (TC-2): **ใน OID4VP** Wallet รัฐบาลต้องส่ง WIA และ WIA-PoP และ Verifier ต้องตรวจทั้งคู่; Wallet เอกชนเลือกส่งทั้งคู่หรือไม่ส่งทั้งคู่ได้ หากเอกชนไม่ส่งทั้งคู่ Verifier ข้าม TC-2 และตรวจ TC-3/TC-4 ต่อ หากส่งไม่ครบหรือรายการใดตรวจไม่ผ่าน ให้ปฏิเสธ VP Verifier ต้องจำแนกประเภท Wallet โดยใช้ข้อมูล Wallet Provider จากแหล่งที่เชื่อถือได้และเป็นอิสระจาก WIA ตามที่ Thai profile กำหนด; หากไม่มีข้อมูล ตรวจความสดใหม่ไม่ได้ หรือจำแนกประเภทไม่ได้ ให้ปฏิเสธ VP (ดู [§8.4.2](08-implementation-guidelines.md) และ [§12.1.4](12.1-trust-list-publication-profile.md)) กฎนี้เป็นส่วนขยาย OID4VP และไม่เปลี่ยนข้อกำหนด OID4VCI +- Trust Check 3 (TC-3): ผู้ออกอยู่ใน Trusted List และ DID ของผู้ออก resolve ได้เป็นกุญแจที่ใช้ลงลายมือชื่อใน VC +- Trust Check 4 (TC-4): อ่านค่า status ที่ index ของ VC ตามความกว้าง `bits` จาก Status List ของผู้ออก + +ลายมือชื่อถูกต้องอย่างเดียวไม่พอที่จะยอมรับ VC + +ตัวระบุ: หน่วยงานใช้ `did:web` ผู้ถือใช้ `did:jwk` ส่วนระบบตัวตนของไทย เช่น NDID ใช้ custom DID method และ resolve ผ่าน ETDA Trust Gateway ซึ่งทำหน้าที่เป็น Universal Resolver + +```mermaid +sequenceDiagram + participant U as ผู้ใช้ + participant W as กระเป๋าเอกสารดิจิทัล + participant V as ผู้ตรวจสอบเอกสาร + participant TL as Trusted List (สพธอ.) + participant R as Universal Resolver + participant SL as รายการสถานะ + + Note over U,TL: ขอข้อมูล + TC-1 ตรวจผู้ตรวจ + U->>W: สแกน QR คำขอ (DCQL) + W->>TL: TC-1: บทบาท/สถานะ/ขอบเขตของผู้ตรวจ + TL-->>W: active / ข้อความยืนยันและวัตถุประสงค์ที่อนุญาต + + Note over W,V: ความยินยอม + การนำเสนอ + W->>U: ขอความยินยอม: วัตถุประสงค์ + ข้อความยืนยัน + U-->>W: อนุมัติ (เปิดเผยข้อมูลแบบเลือกได้) + W->>V: vp_token + Note over V,TL: จำแนกประเภท Wallet + alt Wallet รัฐบาล หรือ Wallet เอกชนส่งหลักฐานครบคู่ + W->>V: WIA + WIA-PoP (ส่งครบคู่) + Note over V,TL: TC-2 ตรวจ WIA และ WIA-PoP + V->>TL: ตรวจ Wallet Provider ในรายการ + TL-->>V: Provider active / WIA ถูกต้อง + else Wallet เอกชนไม่ส่งทั้งคู่ + Note over V: ข้าม TC-2 ตรวจ TC-3 และ TC-4 ต่อ + else Wallet รัฐบาลขาดหลักฐาน หรือ Wallet เอกชนส่งไม่ครบคู่ + V->>W: ปฏิเสธ VP + end + + Note over V,TL: TC-3 ผู้ออก + กุญแจ (DID) + V->>R: TC-3: resolve DID ของผู้ออก (kid) + R-->>V: DID Document / กุญแจสำหรับตรวจ + V->>TL: TC-3: รายการผู้ออก + เทียบกุญแจ + + Note over V,SL: TC-4 สถานะ + ผลลัพธ์ + V->>SL: TC-4: อ่านค่า status ที่ idx ตาม bits + SL-->>V: 0x00 (VALID) + V->>W: ยอมรับ / หรือ 400 invalid_request +``` + +## Trusted List + +สพธอ. ตรวจรับรอง (audit) ทุกหน่วยงานที่จะเป็นผู้ออก ผู้ตรวจ หรือผู้ให้บริการกระเป๋า แล้วลงลายมือชื่อบันทึกไว้ใน Trusted List ที่เกี่ยวข้องจำนวน 3 รายการ แยกตามบทบาท และจัดทำในรูปแบบ JWT เผยแพร่ผ่าน CDN โดยไม่ต้องใช้ API key ลายมือชื่อของ สพธอ. คือเหตุผลเดียวที่ทำให้เชื่อถือรายการนี้ได้ [research/41] + +payload ของ JWT รูปแบบเป้าหมายใช้ `LoTE.ListAndSchemeInformation` สำหรับชนิดรายการ ลำดับ และเวลา ใช้ `LoTE.TrustedEntitiesList[]` สำหรับองค์กร และใช้ `TrustedEntityServices[].ServiceInformation` สำหรับแต่ละบริการ กุญแจสาธารณะหรือ DID ของบริการอยู่ใน `ServiceDigitalIdentity.PublicKeyValues[]` หรือ `OtherIds[]` ตามลำดับ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +Thai profile ต้องระบุสถานะระดับองค์กรและระดับบริการ ช่วงเวลาที่มีผล และขอบเขตสิทธิ์รายบทบาท โดยผู้ออกต้องระบุประเภท VC ที่ออกได้ และผู้ตรวจต้องระบุ claims กับวัตถุประสงค์ที่ขอได้ สถานะทั้งสองระดับต้องเป็น `active` ก่อนเชื่อถือเอนทิตี ทั้งนี้ JWT ตัวอย่างปัจจุบันยังแสดงฟิลด์เหล่านี้ไม่ครบ จึงใช้เป็น test vector สำหรับตัดสินสิทธิ์ไม่ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +ผู้เข้าร่วมดึง Trusted List ที่เกี่ยวข้องฉบับเต็มเฉพาะตอนต้องตัดสินความน่าเชื่อถือ ไม่มีการ polling อยู่เบื้องหลัง ไม่ใช้ API key, delta หรือไฟล์ CBOR และถ้าดึงหรือตรวจลายมือชื่อไม่ได้ ให้ปฏิเสธการเชื่อมต่อนั้น (fail closed) การเพิกถอนในระดับเอนทิตีจะไปถึงแต่ละเอนทิตีเมื่อดึงครั้งถัดไป [research/41] + +ผู้ตรวจต้องใช้ Trusted List เพื่อตัดสินสิทธิ์ของเอนทิตี และใช้ DID Document เพื่อรับกุญแจสำหรับตรวจลายมือชื่อ สพธอ. ลงลายมือชื่อ Trusted List จาก HSM ระดับ FIPS 140-3 Level 3 ด้วยเกณฑ์ M-of-N เช่น 3-of-5 พร้อมกุญแจฉุกเฉิน (emergency key) ที่เก็บแบบออฟไลน์ + +### ตัวอย่าง Trusted List + +JWT ตัวอย่างต่อไปนี้ใช้ศึกษาโครงสร้าง LoTE ทั้งสามรายการมีสามส่วนของ compact JWS เอกสารนี้ยังไม่ได้ตรวจลายมือชื่อหรือใบรับรอง และฟิลด์สถานะกับขอบเขตสิทธิ์ยังไม่ครบ จึงห้ามใช้เป็น test vector สำหรับตัดสินสิทธิ์ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +1. ผู้ออก: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJpYXQiOjE3ODkwMzA2MDIsIng1YyI6WyJNSUlCUGpDQjVxQURBZ0VDQWhRelVlbmtacnB3TGFjVEhqMDd4RXo4bENpOFV6QUtCZ2dxaGtqT1BRUURBakFQTVEwd0N3WURWUVFEREFSRlZFUkJNQjRYRFRJMk1EZ3hNekUxTVRNME4xb1hEVEkzTURneE16RTFNVE0wTjFvd0R6RU5NQXNHQTFVRUF3d0VSVlJFUVRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkVKNnRGamJVVEJQQnRlcGFsWjVGUktMVDZxVnFrbzFvTk1naUlvdXI4RmFxUHAyQUxPbk90bGpwZnl4NmdoZ3YvV3lVWkgwRlhNdDluTTN2dTdzd25paklEQWVNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUc4Z1dWdjZBL2VjU2ZhWFJ2T014Q0kyR0FWYlNlTDk3ZmwyT2R4NCtwU1pBaUIwbXZ5MTB4VzN5dTJDc0ZnSkRqc1BaSTZBV3ZKeGd2VlptQXJMQTlhY1FRPT0iXSwieDV0I1MyNTYiOiJsY1BDaTAxaVFfbkIyQWRZRzVKcEMxTlh3dEVncm9GczhVT2hma21GM2ZzIn0.eyJMb1RFIjp7Ikxpc3RBbmRTY2hlbWVJbmZvcm1hdGlvbiI6eyJMb1RFVmVyc2lvbklkZW50aWZpZXIiOjEsIkxvVEVTZXF1ZW5jZU51bWJlciI6MiwiTG9URVR5cGUiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL0xvVEVUeXBlL0VVUHViRUFBUHJvdmlkZXJzTGlzdCIsIlNjaGVtZU9wZXJhdG9yTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJFbGVjdHJvbmljIFRyYW5zYWN0aW9ucyBEZXZlbG9wbWVudCBBZ2VuY3kgKEVUREEpIn1dLCJTY2hlbWVJbmZvcm1hdGlvblVSSSI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwczovL3d3dy5ldGRhLm9yLnRoIn1dLCJTdGF0dXNEZXRlcm1pbmF0aW9uQXBwcm9hY2giOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvVGhhaUlzc3VlcnNMaXN0L1N0YXR1c0RldG4vRVREQSIsIlNjaGVtZVR5cGVDb21tdW5pdHlSdWxlcyI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvVGhhaUlzc3VlcnMvc2NoZW1lcnVsZXMvRVREQSJ9XSwiU2NoZW1lVGVycml0b3J5IjoiVEgiLCJTY2hlbWVOYW1lIjpbeyJsYW5nIjoiZW4iLCJ2YWx1ZSI6IlRoYWkgSXNzdWVycyBMaXN0In1dLCJIaXN0b3JpY2FsSW5mb3JtYXRpb25QZXJpb2QiOjM2NSwiRGlzdHJpYnV0aW9uUG9pbnRzIjpbXSwiU2NoZW1lRXh0ZW5zaW9ucyI6W10sIkxpc3RJc3N1ZURhdGVUaW1lIjoiMjAyNi0wOS0xMFQwODo1Njo0Mi45NzdaIiwiTmV4dFVwZGF0ZSI6IjIwMjYtMDktMTFUMDg6NTY6NDIuOTc3WiJ9LCJUcnVzdGVkRW50aXRpZXNMaXN0IjpbeyJUcnVzdGVkRW50aXR5SW5mb3JtYXRpb24iOnsiVEVOYW1lIjpbeyJsYW5nIjoiZW4iLCJ2YWx1ZSI6IkVsZWN0cm9uaWMgVHJhbnNhY3Rpb25zIERldmVsb3BtZW50IEFnZW5jeSAoRVREQSkifV19LCJUcnVzdGVkRW50aXR5U2VydmljZXMiOlt7IlNlcnZpY2VJbmZvcm1hdGlvbiI6eyJTZXJ2aWNlVHlwZUlkZW50aWZpZXIiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1N2Y1R5cGUvUHViRUFBL0lzc3VhbmNlIiwiU2VydmljZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiUm9sZSBtb2RlbCBjZXJ0aWZpY2F0ZSJ9XSwiU2VydmljZURpZ2l0YWxJZGVudGl0eSI6eyJQdWJsaWNLZXlWYWx1ZXMiOlt7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4IjoiaEZCU0ZTZXJRblBMMVBOVXA5TnZRTDlqUUJPamJmSU1ScGRwUUEyaUFnSSJ9XSwiT3RoZXJJZHMiOlsiZGlkOmtleTp6Nk1rb01rdTFlYVlGRDdyR25oUzlyRDJiR3l5ZHZQbUUzVWlXNTdNUVlkQVlqSlIiXX19fSx7IlNlcnZpY2VJbmZvcm1hdGlvbiI6eyJTZXJ2aWNlVHlwZUlkZW50aWZpZXIiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1N2Y1R5cGUvUHViRUFBL0lzc3VhbmNlIiwiU2VydmljZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiRW1wbG95ZWVJRCBkZW1vIn1dLCJTZXJ2aWNlRGlnaXRhbElkZW50aXR5Ijp7Ik90aGVySWRzIjpbXSwiWDUwOVNLSXMiOltdLCJQdWJsaWNLZXlWYWx1ZXMiOltdLCJYNTA5Q2VydGlmaWNhdGVzIjpbeyJ2YWwiOiJNSUlCbERDQ0FVYWdBd0lCQWdJVWIxYjlLVHdDTEVidHR6Wit2VXdTRGtYS2tOTXdCUVlESzJWd01EWXhGVEFUQmdOVkJBTU1ERUZqYldVZ1VtOXZkQ0JEUVRFTE1Ba0dBMVVFQmd3Q1EwZ3hFREFPQmdOVkJBb01CMEZqYldVZ1EyOHdIaGNOTWpZd09ESTRNREF3TURBd1doY05NamN3T0RNd01EQXdNREF3V2pBMk1SVXdFd1lEVlFRRERBeEJZMjFsSUZKdmIzUWdRMEV4Q3pBSkJnTlZCQVlNQWtOSU1SQXdEZ1lEVlFRS0RBZEJZMjFsSUVOdk1Db3dCUVlESzJWd0F5RUFKY0FXbk91Y0NjMU1FNVhOMjNtSDBlcmpNTnRkWi83YlpJeXdXNmZMZ1dpalpqQmtNQjhHQTFVZEl3UVlNQmFBRk9MTlNYSmZaVncwYklKNDdLY1RyWFdiR1AvZk1BNEdBMVVkRHdFQi93UUVBd0lCQmpBZEJnTlZIUTRFRmdRVTRzMUpjbDlsWERSc2duanNweE90ZFpzWS85OHdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFGQmdNclpYQURRUUQvZ3g5a1MzSCt2WVdQcCtCS0I2dmRTMmZMY1hqSDk0dTNrdmgyc3Z4Z2xqMnU4SVY4em5lQTVudjhzNWZyNlNCMTdOclBaMXl6cHZkYU1JRzVqRXdNIiwic3BlY1JlZiI6IiIsImVuY29kaW5nIjoiIn1dLCJYNTA5U3ViamVjdE5hbWVzIjpbXX19fSx7IlNlcnZpY2VJbmZvcm1hdGlvbiI6eyJTZXJ2aWNlVHlwZUlkZW50aWZpZXIiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1N2Y1R5cGUvUHViRUFBL0lzc3VhbmNlIiwiU2VydmljZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiUHJvY2l2aXMgRGVtbyBQSUQgSXNzdWVyIn1dLCJTZXJ2aWNlRGlnaXRhbElkZW50aXR5Ijp7IlB1YmxpY0tleVZhbHVlcyI6W3sia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJwMFk0MVhZU2RxMGljOGV2Wi1tT1RPenllQlhWOE1ZbnRIMGJfSEJqb00wIiwieSI6IlU3ODVFbnZyR3VNSlVnWDNLVGdrUzV6c2RxQjhfVWV0ZW1HM1NIcEJxZjAifV0sIk90aGVySWRzIjpbImRpZDp3ZWI6aXNzdWVyLnRvbnloZXJlLndvcms6c3NpOmRpZC13ZWI6djE6NDdhNzRjMTctZGU1Ny00ODc2LWFkYTUtZDcwNzg2YWU4MDM4Il19fX1dfV19fQ.OGq2Sj4pbb1csy_kOzWa_gbeMPbBaL6MhWv-yQLykWrQ56gabFZaJfM3eZmyhUYJRfYp-7kevUCBxaRpLBvhKQ +2. ผู้ตรวจ: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJpYXQiOjE3ODkwMzUwMzMsIng1YyI6WyJNSUlCUGpDQjVxQURBZ0VDQWhRelVlbmtacnB3TGFjVEhqMDd4RXo4bENpOFV6QUtCZ2dxaGtqT1BRUURBakFQTVEwd0N3WURWUVFEREFSRlZFUkJNQjRYRFRJMk1EZ3hNekUxTVRNME4xb1hEVEkzTURneE16RTFNVE0wTjFvd0R6RU5NQXNHQTFVRUF3d0VSVlJFUVRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkVKNnRGamJVVEJQQnRlcGFsWjVGUktMVDZxVnFrbzFvTk1naUlvdXI4RmFxUHAyQUxPbk90bGpwZnl4NmdoZ3YvV3lVWkgwRlhNdDluTTN2dTdzd25paklEQWVNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUc4Z1dWdjZBL2VjU2ZhWFJ2T014Q0kyR0FWYlNlTDk3ZmwyT2R4NCtwU1pBaUIwbXZ5MTB4VzN5dTJDc0ZnSkRqc1BaSTZBV3ZKeGd2VlptQXJMQTlhY1FRPT0iXSwieDV0I1MyNTYiOiJsY1BDaTAxaVFfbkIyQWRZRzVKcEMxTlh3dEVncm9GczhVT2hma21GM2ZzIn0.eyJMb1RFIjp7Ikxpc3RBbmRTY2hlbWVJbmZvcm1hdGlvbiI6eyJMb1RFVmVyc2lvbklkZW50aWZpZXIiOjEsIkxvVEVTZXF1ZW5jZU51bWJlciI6MiwiTG9URVR5cGUiOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvTG9URVR5cGUvVGhhaVZlcmlmaWVyc0xpc3QiLCJTY2hlbWVPcGVyYXRvck5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiRWxlY3Ryb25pYyBUcmFuc2FjdGlvbnMgRGV2ZWxvcG1lbnQgQWdlbmN5IChFVERBKSJ9XSwiU2NoZW1lSW5mb3JtYXRpb25VUkkiOlt7ImxhbmciOiJlbiIsInVyaVZhbHVlIjoiaHR0cHM6Ly93d3cuZXRkYS5vci50aCJ9XSwiU3RhdHVzRGV0ZXJtaW5hdGlvbkFwcHJvYWNoIjoiaHR0cDovL3VyaS5ldGRhLm9yLnRoLzE5NjAyL1RoYWlWZXJpZmllcnNMaXN0L1N0YXR1c0RldG4vRVREQSIsIlNjaGVtZVR5cGVDb21tdW5pdHlSdWxlcyI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwOi8vdXJpLmV0ZGEub3IudGgvMTk2MDIvVGhhaVZlcmlmaWVycy9zY2hlbWVydWxlcy9FVERBIn1dLCJTY2hlbWVUZXJyaXRvcnkiOiJUSCIsIlNjaGVtZU5hbWUiOlt7ImxhbmciOiJlbiIsInZhbHVlIjoiVGhhaSBWZXJpZmllcnMgTGlzdCJ9XSwiSGlzdG9yaWNhbEluZm9ybWF0aW9uUGVyaW9kIjozNjUsIkRpc3RyaWJ1dGlvblBvaW50cyI6W10sIlNjaGVtZUV4dGVuc2lvbnMiOltdLCJMaXN0SXNzdWVEYXRlVGltZSI6IjIwMjYtMDktMTBUMTA6MTA6MzMuOTI4WiIsIk5leHRVcGRhdGUiOiIyMDI2LTA5LTExVDEwOjEwOjMzLjkyOFoifSwiVHJ1c3RlZEVudGl0aWVzTGlzdCI6W3siVHJ1c3RlZEVudGl0eUluZm9ybWF0aW9uIjp7IlRFTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJFbGVjdHJvbmljIFRyYW5zYWN0aW9ucyBEZXZlbG9wbWVudCBBZ2VuY3kgKEVUREEpIn1dfSwiVHJ1c3RlZEVudGl0eVNlcnZpY2VzIjpbeyJTZXJ2aWNlSW5mb3JtYXRpb24iOnsiU2VydmljZVR5cGVJZGVudGlmaWVyIjoiaHR0cDovL3VyaS5ldGRhLm9yLnRoLzE5NjAyL1N2Y1R5cGUvVmVyaWZpZXIiLCJTZXJ2aWNlTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJQcm9jaXZpcyBEZW1vIEJhbmsgVmVyaWZpZXIifV0sIlNlcnZpY2VEaWdpdGFsSWRlbnRpdHkiOnsiUHVibGljS2V5VmFsdWVzIjpbeyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IjVYemtpV0s4UWl1Y3N3SENLakk1S3dyM3JXZkZ6SDlBWElhN0FwMXpDeTgiLCJ5IjoidU9XYThGZU5nbTlyc0hiZmpBczZxTVZnb21Fa1JoMHl1QS1JRjhQUXFwRSJ9XSwiT3RoZXJJZHMiOlsiZGlkOndlYjp2ZXJpZmllci50b255aGVyZS53b3JrOnNzaTpkaWQtd2ViOnYxOmU1NDNlYTBlLTBhMTAtNGZmNS1iOGZhLTE0MTRmMGYzYWI5YyJdfX19XX1dfX0.u92cxz56yft9MttrtaOGJ7keqWsV8GX1BLfNQaMHToaQKnyrqcWBgkfrVQ5aRJHtociOro9EwGhWQrbiwHB-ag +3. ผู้ให้บริการกระเป๋า: eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJpYXQiOjE3ODg3ODExNDgsIng1YyI6WyJNSUlCUGpDQjVxQURBZ0VDQWhRelVlbmtacnB3TGFjVEhqMDd4RXo4bENpOFV6QUtCZ2dxaGtqT1BRUURBakFQTVEwd0N3WURWUVFEREFSRlZFUkJNQjRYRFRJMk1EZ3hNekUxTVRNME4xb1hEVEkzTURneE16RTFNVE0wTjFvd0R6RU5NQXNHQTFVRUF3d0VSVlJFUVRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkVKNnRGamJVVEJQQnRlcGFsWjVGUktMVDZxVnFrbzFvTk1naUlvdXI4RmFxUHAyQUxPbk90bGpwZnl4NmdoZ3YvV3lVWkgwRlhNdDluTTN2dTdzd25paklEQWVNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUc4Z1dWdjZBL2VjU2ZhWFJ2T014Q0kyR0FWYlNlTDk3ZmwyT2R4NCtwU1pBaUIwbXZ5MTB4VzN5dTJDc0ZnSkRqc1BaSTZBV3ZKeGd2VlptQXJMQTlhY1FRPT0iXSwieDV0I1MyNTYiOiJsY1BDaTAxaVFfbkIyQWRZRzVKcEMxTlh3dEVncm9GczhVT2hma21GM2ZzIn0.eyJMb1RFIjp7Ikxpc3RBbmRTY2hlbWVJbmZvcm1hdGlvbiI6eyJMb1RFVmVyc2lvbklkZW50aWZpZXIiOjEsIkxvVEVTZXF1ZW5jZU51bWJlciI6MSwiTG9URVR5cGUiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL0xvVEVUeXBlL0VVV2FsbGV0UHJvdmlkZXJzTGlzdCIsIlNjaGVtZU9wZXJhdG9yTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJFbGVjdHJvbmljIFRyYW5zYWN0aW9ucyBEZXZlbG9wbWVudCBBZ2VuY3kgKEVUREEpIn1dLCJTY2hlbWVJbmZvcm1hdGlvblVSSSI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwczovL3d3dy5ldGRhLm9yLnRoIn1dLCJTdGF0dXNEZXRlcm1pbmF0aW9uQXBwcm9hY2giOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1dhbGxldFByb3ZpZGVyc0xpc3QvU3RhdHVzRGV0bi9FVSIsIlNjaGVtZVR5cGVDb21tdW5pdHlSdWxlcyI6W3sibGFuZyI6ImVuIiwidXJpVmFsdWUiOiJodHRwOi8vdXJpLmV0c2kub3JnLzE5NjAyL1dhbGxldFByb3ZpZGVyc0xpc3Qvc2NoZW1lcnVsZXMvRVUifV0sIlNjaGVtZVRlcnJpdG9yeSI6IlRIIiwiU2NoZW1lTmFtZSI6W3sibGFuZyI6ImVuIiwidmFsdWUiOiJXYWxsZXQgUHJvdmlkZXJzIExpc3QifV0sIkhpc3RvcmljYWxJbmZvcm1hdGlvblBlcmlvZCI6MzY1LCJEaXN0cmlidXRpb25Qb2ludHMiOltdLCJTY2hlbWVFeHRlbnNpb25zIjpbXSwiTGlzdElzc3VlRGF0ZVRpbWUiOiIyMDI2LTA5LTA3VDExOjM5OjA4LjM5NVoiLCJOZXh0VXBkYXRlIjoiMjAyNi0wOS0wOFQxMTozOTowOC4zOTVaIn0sIlRydXN0ZWRFbnRpdGllc0xpc3QiOltdfX0.Izh_JYqPikXvdYGKdYJY6I26VuWuLtWwCX1eKtvqxnVSMRsxxtV_K7Wx6lbp_1C7RemaFT52ZrAOZa5t1SmZ6g + +## การเพิกถอนและสถานะ + +ผู้ออกแต่ละรายเผยแพร่ Token Status List ของตนเอง (อ้าง revision ในบรรณานุกรม) ค่า status ที่ IETF กำหนดคือ `0x00 = VALID`, `0x01 = INVALID`, `0x02 = SUSPENDED`; `0x03 = rotated` เป็นส่วนขยาย Thai profile ไม่ใช่ความหมายที่ IETF ลงทะเบียน ค่า `bits` ใน Status List คือความกว้างของช่องต่อรายการ: ต้องมีอย่างน้อย `bits: 2` เมื่อใช้ `SUSPENDED` หรือ `rotated` + +ผู้ออกมีเป้าหมายเผยแพร่การเปลี่ยนแปลงภายใน 15 นาที โดยระยะเวลารวมของกระบวนการอาจนานกว่านั้น ผู้ตรวจต้องอ่านตำแหน่ง Status List จากข้อมูลที่ Thai profile กำหนดไว้ใน Trusted List และอ่านค่า status ที่ index ตามความกว้าง `bits` ทุกครั้งที่มีการนำเสนอ (TC-4) โดย JWT ตัวอย่างใน [research/42](../research/42-trusted-list-lote-jwt-format.md) ยังไม่แสดงฟิลด์ตำแหน่งดังกล่าว + +นอกจากระดับ VC รายใบแล้ว การเพิกถอนยังทำได้ที่ระดับที่สูงกว่า ถ้า สพธอ. ระงับหรือเพิกถอนผู้ออก VC ทุกใบของผู้ออกนั้นจะตรวจไม่ผ่านทันที โดยไม่ต้องเปลี่ยนค่า status รายใบ เพราะรายการบทบาทของผู้ออกไม่ได้ `active` อีกต่อไป ถ้าผู้ให้บริการกระเป๋าเพิกถอน wallet attestation ผู้ออกที่ผูก VC ไว้กับหน่วยกระเป๋านั้นต้องเพิกถอน VC เหล่านั้นตามไปด้วย และ VC ทุกใบจะหมดอายุที่ `validUntil` โดยไม่ต้องอาศัยกลไกใดข้างต้น + +การเพิกถอนฉุกเฉินจะไปถึงแต่ละเอนทิตีเมื่อดึงครั้งถัดไป โดยไม่มี message broker หรือการ polling เบื้องหลัง ตาม [§12.8](12-key-management-and-trustlist-deployment.md) + +--- + +**การนำทาง:** [⬆️ สารบัญ ARF](README.md) · [บทที่ 1 — ขอบข่าย ➡️](01-scope.md) diff --git a/docs/thai-vc-arf/01-scope.md b/docs/thai-vc-arf/01-scope.md new file mode 100644 index 0000000..ad45d47 --- /dev/null +++ b/docs/thai-vc-arf/01-scope.md @@ -0,0 +1,24 @@ +--- +description: "1. ขอบข่าย — Thai VC ARF 2.0 DRAFT 0" +--- + +# 1. ขอบข่าย + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารแปลงจาก PDF ต้นฉบับของ สพธอ. (Thai VC ARF v1.1, มกราคม 2568) +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + + +รายงานฉบับนี้อธิบายหลักการทำงาน วงจรชีวิต องค์ประกอบของระบบการใช้งานเอกสารรับรองดิจิทัล (verifiable credential: VC) และเอกสารสำแดงดิจิทัล (verifiable presentation: VP) โดยมีวัตถุประสงค์เพื่อเป็นแนวทางอ้างอิงในการดำเนินการ (reference implementation) และช่วยลดความซับซ้อนของการพัฒนาเชิงเทคนิค ที่อาจมีความแตกต่างกัน เพื่อให้ผู้ให้บริการระบบและผู้พัฒนาที่เกี่ยวข้องสามารถนำไปปรับใช้ในการให้บริการและการพัฒนาระบบให้มีความมั่นคงปลอดภัย มีการคุ้มครองข้อมูลส่วนบุคคลและสามารถทำงานร่วมกันได้ระหว่างระบบการใช้งานเอกสารรับรองดิจิทัลและเอกสารสำแดงดิจิทัลในประเทศไทย + +รายงานฉบับนี้ไม่ครอบคลุมถึงขั้นตอนการพิสูจน์และยืนยันตัวตนของบุคคลธรรมดาและนิติบุคคล การสร้างตัวระบุแบบกระจายศูนย์ (Decentralized Identifiers: DID) สำหรับบุคคลธรรมดาและนิติบุคคล แต่ให้ความสำคัญในส่วนขั้นตอนการทำงานร่วมกัน (Interoperability) ของ ผู้ออกเอกสาร (issuer) ผู้ถือเอกสาร (holder) และผู้ตรวจสอบเอกสาร (verifier) ซึ่งทั้งสามเป็นส่วนประกอบสำคัญ (component) ในระบบนิเวศของเอกสารรับรองดิจิทัล (VC Ecosystem) + +{/* pdf page 7 */} + +--- + +**การนำทาง:** [⬆️ สารบัญ ARF](README.md) · [บทที่ 2 — บทนิยาม ➡️](02-definitions.md) diff --git a/docs/thai-vc-arf/02-definitions.md b/docs/thai-vc-arf/02-definitions.md new file mode 100644 index 0000000..27c8300 --- /dev/null +++ b/docs/thai-vc-arf/02-definitions.md @@ -0,0 +1,236 @@ +--- +description: "2. บทนิยาม — Thai VC ARF 2.0 DRAFT 0" +--- + +# 2. บทนิยาม + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Thai VC ARF v2.0 — คงนิยามเดิมจากเวอร์ชัน 1.1 และเพิ่มนิยามสำหรับ v2.0 +> **เวอร์ชัน:** 2.0-draft (MASA-124) +> **วันที่ปรับปรุง:** 4 สิงหาคม 2569 +> **แหล่งข้อมูลเดิม:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย เวอร์ชัน 1.1 มกราคม 2568 +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) · [อภิธานศัพท์กลาง](../../00-glossary.md) +*/} + +--- + +บทนี้รวบรวมบทนิยามของคำที่ใช้ตลอดรายงานฉบับนี้ เพื่อให้ผู้อ่านตีความคำในบทอื่นได้ตรงกัน + +ความหมายของคำที่ใช้ในรายงานฉบับนี้ มีดังต่อไปนี้ + +2.1 ผู้สมัคร (applicant) หมายถึง เอนทิตีที่ยื่นขอเอกสารรับรองจากผู้ออกเอกสาร (issuer) โดยผู้สมัครอาจจะเป็นเจ้าของข้อความ (subject) ในเอกสารรับรองหรือไม่ก็ได้ + +2.2 กระเป๋าเอกสารดิจิทัล (digital document wallet) หมายถึง โปรแกรมที่จัดเก็บและช่วยให้ผู้ถือเอกสารสามารถเข้าถึงและใช้งาน VC ได้อย่างมั่นคงปลอดภัย [2] + +2.3 ผู้ให้บริการกระเป๋าเอกสารดิจิทัล (digital document wallet provider) หมายถึง เอนทิตีที่ทำหน้าที่พัฒนาและ/หรือดำเนินการเกี่ยวกับกระเป๋าเอกสารดิจิทัล ให้แก่ผู้ออกเอกสาร ผู้ถือเอกสาร หรือผู้ตรวจสอบเอกสาร + +2.4 เจ้าของข้อความ (subject) หมายถึง เอนทิตีที่ถูกกล่าวอ้างถึงในข้อความยืนยัน (claim) [2] + +2.5 ข้อความยืนยัน (claim) หมายถึง ข้อความเกี่ยวกับเจ้าของข้อความ (subject) ที่จะถูกรับรองโดยผู้ออกเอกสาร (issuer) [2] + +2.6 เอกสารรับรองดิจิทัล (verifiable credential: VC) หมายถึง ชุดของข้อความยืนยันอย่างน้อยหนึ่งรายการที่ถูกรับรองโดยผู้ออกเอกสาร (issuer) ทั้งนี้ VC มีคุณสมบัติที่สามารถตรวจพบการเปลี่ยนแปลงใด ๆ ที่เกิดกับความถูกต้องครบถ้วนของข้อมูลและตรวจสอบลายมือชื่ออิเล็กทรอนิกส์ของผู้ออกเอกสารได้ด้วยกระบวนการเข้ารหัสลับ [2] + +ข้อสังเกต: ข้อความยืนยันใน VC อาจเกี่ยวกับเจ้าของข้อความ (subject) ที่มากกว่าหนึ่งราย เช่น ข้อความยืนยันในทะเบียนสมรส + +2.7 เอกสารสำแดงดิจิทัล (verifiable presentation: VP) หมายถึง VC อย่างน้อยหนึ่งชุด ที่ผู้ถือเอกสาร (holder) ใช้แสดงต่อผู้ตรวจสอบเอกสาร (verifier) ทั้งนี้ VP มีคุณสมบัติที่สามารถตรวจพบการเปลี่ยนแปลงใด ๆ ที่เกิดกับความถูกต้องครบถ้วนของข้อมูล และตรวจสอบลายมือชื่ออิเล็กทรอนิกส์ของผู้ถือเอกสารและตรวจสอบ VC ที่เกี่ยวข้องได้ด้วยกระบวนการเข้ารหัสลับ [2] + +2.8 เอนทิตี (entity) หมายถึง สิ่งที่มีอยู่จริง เช่น บุคคล องค์กร หรืออุปกรณ์ ซึ่งถูกกล่าวอ้างในการใช้งาน VC และ VP [2] + +2.9 ผู้ออกเอกสาร (issuer) หมายถึง เอนทิตีที่ทำหน้าที่รับรองข้อความยืนยันโดยออกเป็น VC ให้แก่ผู้ถือเอกสาร [2] + +2.10 ผู้ถือเอกสาร (holder) หมายถึง เอนทิตีที่เป็นเจ้าของ VC อย่างน้อยหนึ่งชุด โดยจัดเก็บไว้ในกระเป๋าเอกสารดิจิทัล (digital document wallet) และสามารถใช้ VC สร้างเป็น VP [2] ทั้งนี้ ผู้ถือเอกสารมีอีกชื่อเรียกหนึ่งว่า ผู้ใช้งานกระเป๋าเอกสารดิจิทัล + +2.11 ผู้ตรวจสอบเอกสาร (verifier) หมายถึง เอนทิตีที่สามารถตรวจสอบความถูกต้องครบถ้วนของ VC และ VP ด้วยกระบวนการเข้ารหัสลับ รวมถึงตรวจสอบสถานะการใช้งานและความสอดคล้องตามโครงสร้างข้อมูลของ VC และ VP [2] + +2.12 ระบบทะเบียนเอกสารรับรอง (verifiable data registry) หมายถึง ระบบที่ช่วยสนับสนุนการสร้างและการตรวจสอบความถูกต้องครบถ้วนของ VC และ VP โดยข้อมูลที่มีการจัดเก็บในระบบนี้ เช่น ตัวระบุ (identifier) และกุญแจสาธารณะ (public key) ของเอนทิตีที่เกี่ยวข้อง รวมถึงรายการเพิกถอน (revocation list) และเค้าร่าง (schema) ของ VC หรือ VP [2] + +2.13 ลายมือชื่อดิจิทัล (digital signature) หมายถึง ลายมือชื่ออิเล็กทรอนิกส์ ที่ได้จากกระบวนการเข้ารหัสลับข้อมูลอิเล็กทรอนิกส์ซึ่งช่วยให้สามารถยืนยันตัวเจ้าของลายมือชื่อและตรวจพบการเปลี่ยนแปลงของข้อความและลายมือชื่ออิเล็กทรอนิกส์ได้รวมถึงการทำให้เจ้าของลายมือชื่อไม่สามารถปฏิเสธความรับผิดจากข้อความที่ตนเองลงลายมือชื่อได้ [3] + +{/* pdf page 8 */} + +2.14 ตัวระบุ (identifier) หมายถึง Uniform Resource Identifier (URI) ที่ระบุถึงเอนทิตี เอกสารรับรอง หรือ ข้อมูลอื่น ๆ ที่เกี่ยวข้อง [4] + +2.15 ตัวระบุแบบกระจายศูนย์ (decentralized identifier: DID) หมายถึง ตัวระบุ (identifier) ที่มีความคงอยู่ถาวร (persistence) ที่มีเอกลักษณ์เฉพาะในระดับสากล ซึ่งไม่จำเป็นต้องอาศัยหน่วยงานกลางผู้รับผิดชอบในการลงทะเบียน และมักถูกสร้างขึ้นและ/หรือลงทะเบียนโดยใช้วิธีการเข้ารหัสลับ [4] + +2.16 DID subject หมายถึง เอนทิตีที่ถูกระบุโดย DID และถูกอธิบายไว้ใน DID Document [4] + +2.17 DID document หมายถึง ชุดข้อมูลที่ใช้ในการอธิบาย DID subject รวมไปถึงกลไกที่เกี่ยวข้องกับ DID subject นั้น เช่น กุญแจสาธารณะที่ DID subject สามารถใช้ในการยืนยันตัวตนหรือพิสูจน์ความเกี่ยวข้องกับ DID นั้น [4] + +2.18 DID method (Decentralized Identifiers Methodologies) หมายถึง กลไกเฉพาะสำหรับแต่ละ DID ที่ใช้ในการสร้าง ดึงข้อมูล (resolve) อัปเดต และระงับการใช้งาน DID รวมไปถึง DID document ที่เกี่ยวข้องกับ DID นั้น [4] + +2.19 DID resolver หมายถึง ส่วนประกอบที่เป็นซอฟต์แวร์หรือฮาร์ดแวร์ที่ใช้ในกระบวนการ DID resolution ซึ่งทำงานโดยใช้ DID เป็นอินพุต และได้เอาต์พุตเป็น DID document [4] + +2.20 Universal Resolver หมายถึง เครื่องมือหรือบริการที่ใช้สนับสนุนการทำงานร่วมกันระหว่างหลาย DID method ในกระบวนการ DID resolution และการดึงข้อมูล (resolve) DID Document + +2.21 เค้าร่าง (schema) หมายถึง ข้อมูลที่กำหนดโครงสร้าง (structure) และรูปแบบ (format) ของ VC และ/หรือ VP โดยสามารถแบ่งออกเป็น 2 ประเภท ได้แก่ (1) เค้าร่างสำหรับการตรวจสอบข้อมูล (data verification schema) สำหรับตรวจสอบโครงสร้างและเนื้อหาของ VC และ (2) เค้าร่างสำหรับการเข้ารหัสข้อมูล (data encoding schema) สำหรับการแปลงเนื้อของ VC จากรูปแบบ (format) หนึ่งไปยังอีกรูปแบบหนึ่ง [5] + +2.22 การทำงานร่วมกัน (interoperability) หมายถึง ความสามารถ (ทางเทคนิค) ในการแลกเปลี่ยนและใช้ข้อมูลระหว่างระบบ/ส่วนประกอบตั้งแต่สองระบบขึ้นไป ซึ่งทำงานร่วมกันได้โดยผู้ดำเนินการไม่จำเป็นต้องแทรกแซงการดำเนินการในแต่ละครั้ง [IEEE+ eHealth Governance Initiative] + +{/* pdf page 9 */} + +## นิยามเพิ่มเติมสำหรับเวอร์ชัน 2.0 + +นิยามในข้อ 2.1 ถึงข้อ 2.22 คงข้อความและความหมายตาม Thai VC ARF เวอร์ชัน 1.1 ไว้ทุกประการ นิยามต่อไปนี้เป็นส่วนเพิ่มเติมสำหรับเวอร์ชัน 2.0 และต้องตีความร่วมกับอภิธานศัพท์กลาง ทั้งนี้ ในกรณีที่คำในส่วนนี้ต่างจากคำในเอกสารเวอร์ชันก่อน ให้ใช้คำในส่วนนี้เฉพาะเนื้อหาที่เพิ่มในเวอร์ชัน 2.0 + +2.23 หน่วยกระเป๋าเอกสาร (wallet unit) หมายถึง การติดตั้งหรือการทำงานหนึ่งชุดของกระเป๋าเอกสารดิจิทัล ซึ่งอยู่ภายใต้การควบคุมของผู้ใช้งานหนึ่งรายบนอุปกรณ์หรือสภาพแวดล้อมเฉพาะ [D1] + +2.24 หลักฐานรับรองหน่วยกระเป๋าเอกสาร (wallet unit attestation: WUA) หมายถึง หลักฐานอิเล็กทรอนิกส์ที่ผู้ให้บริการกระเป๋าเอกสารดิจิทัลออกให้แก่ wallet unit เพื่อรับรองคุณลักษณะของ wallet unit และผูกกุญแจสาธารณะของ wallet unit กับหลักฐานดังกล่าว ผู้ตรวจสอบต้องตรวจลายมือชื่อ ระยะเวลามีผล และสถานะของ WUA ตามข้อกำหนดที่เกี่ยวข้อง [D1] + +_ข้อสังเกต:_ เนื้อหาใหม่ของรายงานฉบับนี้ต้องใช้คำว่า WUA แทน Wallet Trust Evidence (WTE) เมื่อกล่าวถึงหลักฐานของ wallet unit ตาม EUDI ARF ทั้งนี้ การปรับคำดังกล่าวไม่เปลี่ยนความหมายของนิยามเดิมในข้อ 2.1 ถึงข้อ 2.22 + +_ข้อสังเกต:_ WUA เป็นคำรวม (umbrella term) ประกอบด้วยหลักฐานสองประเภท ได้แก่ Wallet Instance Attestation (WIA) ซึ่งรับรองความเป็นของแท้ของ wallet instance และ Key Attestation (KA) ซึ่งรับรองการจัดเก็บกุญแจใน WSCD รายละเอียดการใช้งาน การเพิกถอน และสถานะของ WIA และ KA อยู่ใน [บทที่ 10 — Wallet Unit Attestation](10-wallet-unit-attestation.md) [D1] + +2.25 รายการที่เชื่อถือได้ (trusted list) หมายถึง ชุดข้อมูลที่หน่วยงานกำกับดูแลเผยแพร่เพื่อระบุเอนทิตี บทบาท สถานะ ขอบเขตสิทธิ์ และข้อมูลอ้างอิงกุญแจหรือ certificate ที่ใช้ประกอบการตัดสินความน่าเชื่อถือ ระบบที่ใช้รายการดังกล่าวต้องตรวจสอบความถูกต้องของลายมือชื่อ แหล่งที่มา เวอร์ชัน และความสดใหม่ของรายการก่อนนำไปใช้ [D2] + +2.26 แบบจำลองความน่าเชื่อถือที่ 3 (Trust Model 3) หมายถึง แบบจำลองที่เอนทิตีจัดการคู่กุญแจของตนเอง และใช้ DID หรือข้อมูลอ้างอิงกุญแจร่วมกับ trusted list เพื่อยืนยันบทบาท สถานะ และขอบเขตสิทธิ์ โดยไม่กำหนดให้ certificate chain ที่ออกโดย CA เป็นกลไกหลักสำหรับความน่าเชื่อถือของเอนทิตี [D2] + +2.27 ชุดวิธีการเข้ารหัสลับ (cryptographic suite) หมายถึง ชุดข้อกำหนดที่ระบุอัลกอริทึม การแทนข้อมูลกุญแจ การสร้าง proof หรือ digital signature และกระบวนการตรวจสอบที่ต้องใช้ร่วมกันเพื่อคุ้มครอง authenticity และ integrity ของข้อมูล [D3] + +2.28 การบริหารจัดการกุญแจ (key management) หมายถึง กระบวนการควบคุมวงจรชีวิตของกุญแจเข้ารหัสลับ ตั้งแต่การสร้าง การจัดเก็บ การใช้งาน การสำรอง การกู้คืน การหมุนเปลี่ยน การระงับ และการทำลายกุญแจ ผู้ดำเนินการต้องกำหนดผู้รับผิดชอบและหลักฐานตรวจสอบย้อนหลังสำหรับแต่ละขั้นตอน + +2.29 การหมุนเปลี่ยนกุญแจ (key rotation) หมายถึง การเปลี่ยนกุญแจเข้ารหัสลับเดิมเป็นกุญแจใหม่ตามกำหนดเวลา เมื่อความเสี่ยงเปลี่ยนแปลง หรือเมื่อสงสัยว่ากุญแจรั่วไหล ระบบต้องรักษาความต่อเนื่องของการตรวจสอบข้อมูลที่ลงลายมือชื่อไว้ก่อนการเปลี่ยนกุญแจตามนโยบายที่กำหนด + +2.30 สถานะเอกสารรับรอง (credential status) หมายถึง ข้อมูลที่ทำให้ผู้ตรวจสอบเอกสารสามารถพิจารณาได้ว่า VC ยังใช้งานได้ ถูกระงับ หรือถูกเพิกถอน โดยไม่เปลี่ยนเนื้อหาของ VC ที่ออกแล้ว [D4] + +2.31 การเพิกถอนเอกสารรับรอง (VC revocation) หมายถึง การเปลี่ยนสถานะของ VC ให้ไม่สามารถใช้เป็นหลักฐานที่เชื่อถือได้ก่อนสิ้นสุดระยะเวลามีผล ผู้ออกเอกสารต้องเผยแพร่สถานะตามวิธีและระยะเวลาที่นโยบายกำหนด + +2.32 การเปิดเผยแบบเลือกได้ (selective disclosure) หมายถึง ความสามารถของผู้ถือเอกสารในการเลือกเปิดเผยเฉพาะข้อมูลที่จำเป็นจาก VC ต่อผู้ตรวจสอบเอกสาร [D5] + +2.33 การผูกผู้ถือเอกสาร (holder binding) หมายถึง กลไกที่ทำให้ผู้ตรวจสอบเอกสารสามารถตรวจสอบได้ว่าผู้แสดง VC หรือ VP เป็นผู้ควบคุมกุญแจหรือปัจจัยที่ผูกไว้กับเอกสารนั้น + +2.34 ข้อมูลอ้างอิงกุญแจ (key reference) หมายถึง ตัวระบุหรือข้อมูลที่ใช้ค้นหา verification method หรือกุญแจสาธารณะสำหรับตรวจสอบ digital signature หรือ proof โดยไม่เปิดเผยกุญแจส่วนตัว + +2.35 รายการสถานะ (status list) หมายถึง ข้อมูลที่เผยแพร่เพื่อแสดงสถานะของ credential หรือหลักฐานรับรองหลายรายการในโครงสร้างเดียว โดยต้องมีกลไกคุ้มครอง integrity และต้องกำหนดนโยบายความสดใหม่ของข้อมูล [D4] + +2.36 อุปกรณ์เข้ารหัสลับของกระเป๋าเอกสาร (wallet secure cryptographic device: WSCD) หมายถึง ส่วนประกอบฮาร์ดแวร์หรือซอฟต์แวร์ที่ทำหน้าที่จัดเก็บและใช้งานกุญแจเข้ารหัสลับของ wallet unit อย่างมั่นคงปลอดภัย ได้แก่ Secure Enclave StrongBox TEE และ HSM ที่ผ่านการรับรองตาม profile ที่ สพธอ. กำหนด [D1] + +2.37 แอปพลิเคชันเข้ารหัสลับของกระเป๋าเอกสาร (wallet secure cryptographic application: WSCA) หมายถึง ส่วนประกอบซอฟต์แวร์ที่ทำหน้าที่เป็นตัวกลางระหว่าง wallet unit กับ WSCD เพื่อเรียกใช้กระบวนการเข้ารหัสลับตามที่ profile กำหนด [D1] + +2.38 ทะเบียนสถานะเอกสารรับรอง (VC status registry) หมายถึง บริการที่ผู้ออกเอกสารเป็นผู้ดำเนินการเอง หรือมอบหมายให้ผู้ดำเนินการภายนอกเป็นผู้ดูแล เพื่อรับการปรับปรุงสถานะจากผู้ออกเอกสารและเผยแพร่ status list ในรูปแบบ `statuslist+jwt` พร้อมบันทึกตรวจสอบย้อนหลัง ผู้ออกเอกสารรับผิดชอบบริการนี้ โดย Thai profile ต้องกำหนดวิธีระบุตำแหน่ง Status List และกุญแจของผู้ออกเอกสารใน Trusted List ก่อนใช้ตรวจสถานะ เนื่องจาก JWT ตัวอย่างยังไม่แสดงฟิลด์ดังกล่าว [D6] [research/42](../research/42-trusted-list-lote-jwt-format.md) + +2.39 รายการที่เชื่อถือได้ (list of trusted entities: LoTE) หมายถึง คำเรียกตามมาตรฐาน ETSI TS 119 602 ซึ่งเทียบเท่ากับ trusted list ในข้อ 2.25 โดยใช้ในบริบทการทำงานร่วมกับสหภาพยุโรป [D7] + +2.40 รายการรวมรายการที่เชื่อถือ (list of trusted lists: LoTL) หมายถึง รายการที่รวบรวม trusted list ของหลายเขตอำนาจศาล เพื่อให้ผู้ตรวจสอบสามารถค้นหา trusted list ที่เกี่ยวข้องกับเอนทิตีที่อยู่ภายใต้เขตอำนาจศาลนั้นได้ [D7] + +2.41 การรับรองคุณลักษณะทางอิเล็กทรอนิกส์ของหน่วยงานภาครัฐ (public body electronic attestation of attributes: PuB-EAA) หมายถึง การรับรองคุณลักษณะทางอิเล็กทรอนิกส์ที่ออกโดยหน่วยงานของรัฐ เช่น สปสช. หรือกรมที่ดิน ภายใต้กรอบงานของ EUDI ARF [D1] + +2.42 ค่าเชื่อมโยงตัวระบุ (confirmation claim: `cnf`) หมายถึง ข้อมูลอ้างอิงใน payload ของ VC ที่ระบุกุญแจสาธารณะของผู้ถือเอกสาร เพื่อให้ผู้ตรวจสอบสามารถพิสูจน์ว่าผู้ถือควบคุมกุญแจส่วนตัวที่จับคู่กับ VC นั้นได้ [D8] + +## การใช้คำบอกระดับข้อบังคับ + +คำว่า “ต้อง” ให้มีความหมายเทียบเท่า MUST คำว่า “ควร” ให้มีความหมายเทียบเท่า SHOULD และคำว่า “อาจ” ให้มีความหมายเทียบเท่า MAY การใช้คำดังกล่าวในรายงานฉบับนี้กำหนดระดับข้อบังคับของข้อความในบทเพิ่มเติม ส่วนข้อ 2.1 ถึงข้อ 2.22 คงนิยามเดิม + +{/* METADATA (agent-only — not rendered to readers) +## สถานะของข้อความ + +- **ข้อเท็จจริง:** ข้อ 2.1 ถึงข้อ 2.22 คัดจาก Thai VC ARF เวอร์ชัน 1.1 ส่วนข้อ 2.23 ถึงข้อ 2.42 อ้างอิงมาตรฐานและเอกสารโครงการตามรายการอ้างอิง +- **ข้อวิเคราะห์:** การเพิ่มนิยามใหม่แยกจากนิยามเดิมช่วยให้ตรวจสอบได้ว่าความหมายเดิมไม่ถูกแก้ไข และลดความกำกวมระหว่าง WTE กับ WUA รวมถึงความชัดเจนระหว่าง trusted list กับ LoTL/LoTE และระหว่าง status list กับ VC status registry +- **สมมติฐาน:** Trust Model 3 ในเวอร์ชัน 2.0 ใช้ DID หรือ key reference ร่วมกับ trusted list เป็นกลไกหลัก ทั้งนี้ รายละเอียดเชิงบังคับของข้อมูลและกระบวนการต้องเป็นไปตามบทที่ 11 และ profile ที่ สพธอ. ประกาศใช้ + +## แหล่งอ้างอิงสำหรับนิยามเพิ่มเติม + +รายการอ้างอิงในบทนี้ใช้ตัวนำหน้า `D` (Definitions) ได้แก่ [D1] ถึง [D8] เพื่อไม่ให้ปนกับหมายเลขอ้างอิงในบรรณานุกรมกลาง (บทที่ 16) ส่วนเลข [2] ถึง [5] ในข้อ 2.1 ถึงข้อ 2.22 ยังอ้างถึงบรรณานุกรมกลางตามเดิม + +[D1] European Commission, “European Digital Identity Wallet Architecture and Reference Framework,” เวอร์ชัน 2.9.0, หัวข้อ Wallet Unit Attestation, https://eudi.dev/2.9.0/main/ และ https://eudi.dev/2.9.0/discussion-topics/c-wallet-unit-attestation/ (เข้าถึงเมื่อ 4 สิงหาคม 2569) + +[D2] Thailand Document Wallet Working Group, “Trust Architecture — 3 Trust Models” และ “3 Trust Architecture Models — Fact-Based Analysis,” ฉบับใน repository ณ 4 สิงหาคม 2569, [research/32](../research/32-trustlist-3models.md) และ [research/33](../research/33-trust-architecture-models-factbase-analysis.md) (เข้าถึงเมื่อ 4 สิงหาคม 2569) + +[D3] World Wide Web Consortium (W3C), “Verifiable Credential Data Integrity 1.0,” W3C Recommendation, 15 พฤษภาคม 2568, https://www.w3.org/TR/vc-data-integrity/ (เข้าถึงเมื่อ 4 สิงหาคม 2569) + +[D4] Internet Engineering Task Force (IETF), “Token Status List (TSL),” draft-ietf-oauth-status-list-21, 21 มิถุนายน 2569 (Internet-Draft; RFC Editor status: Awaiting First editor ณ 1 ตุลาคม 2569), https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ (เข้าถึงเมื่อ 1 ตุลาคม 2569); สถาปัตยกรรมโครงการอ้าง revision `-18` เป็น baseline + +[D5] World Wide Web Consortium (W3C), “Verifiable Credentials Data Model v2.0,” W3C Recommendation, 15 พฤษภาคม 2568, https://www.w3.org/TR/vc-data-model-2.0/ (เข้าถึงเมื่อ 4 สิงหาคม 2569) + +[D6] สำนักงานพัฒนาธุรกรรมทางอิเล็กทรอนิกส์ (สพธอ.) และคณะทำงาน, “VC Status Registry — แนวทางการให้บริการ status list สำหรับผู้ออกเอกสาร,” เอกสารในโครงการ ณ 4 สิงหาคม 2569, [th/usecase/09-vc-status-registry.md](../usecase/09-vc-status-registry.md) (เข้าถึงเมื่อ 4 สิงหาคม 2569) + +[D7] European Telecommunications Standards Institute (ETSI), “ETSI TS 119 602 — Electronic Signatures and Infrastructures (ESI); Lists of Trusted Entities (LoTEs),” https://www.etsi.org/deliver/etsi_ts/119600_119699/119602/ (เข้าถึงเมื่อ 4 สิงหาคม 2569) + +[D8] European Commission, “Commission Implementing Regulation (EU) 2024/2979 — implementing Directive 1999/93/EC as regards the validation of qualified electronic signatures and qualified electronic seals (Holder Binding / cnf claim reference),” 28 พฤศจิกายน 2567, https://eur-lex.europa.eu/eli/reg_impl/2024/2979/oj (เข้าถึงเมื่อ 4 สิงหาคม 2569) + +## แผนภาพความสัมพันธ์ระหว่างนิยามใหม่ (Traceability) + +แผนภาพต่อไปนี้แสดงความสัมพันธ์ระหว่างนิยามใหม่ในเวอร์ชัน 2.0 เพื่อให้ตรวจสอบได้ว่าแต่ละนิยามถูกใช้ในบริบทใดในเอกสารฉบับนี้และฉบับอื่น ๆ ของโครงการ โดยแบ่งเป็น 5 กลุ่มตามหัวข้อ + +**กลุ่มที่ 1 หน่วยกระเป๋าเอกสารและหลักฐานรับรอง (WUA)** + +```mermaid +flowchart LR + WU["2.23 wallet unit"] -.-> WUA["2.24 WUA
Wallet Unit Attestation"] + WUA -.-> WSCD["2.36 WSCD"] + WUA -.-> WSCA["2.37 WSCA"] + WUA -.-> KBJ["2.33 holder binding
(cnf claim)"] +``` + +**กลุ่มที่ 2 Trusted list และ Trust Model 3** + +```mermaid +flowchart LR + LoTE["2.39 LoTE"] -.-> TL["2.25 trusted list"] + LoTL["2.40 LoTL"] -.-> TL + TL -.-> M3["2.26 Trust Model 3"] + M3 -.-> DID["DID 2.15 + key ref 2.34"] + DID -.-> M3 +``` + +**กลุ่มที่ 3 ชุดวิธีการเข้ารหัสลับและวงจรชีวิตกุญแจ** + +```mermaid +flowchart LR + CS["2.27 cryptographic suite"] -.-> SIG["2.13 digital signature"] + CS -.-> KM["2.28 key management"] + KM -.-> KR["2.29 key rotation"] + KR -.-> CS +``` + +**กลุ่มที่ 4 สถานะและการเพิกถอน VC** + +```mermaid +flowchart LR + CR["2.31 VC revocation"] -.-> CSTAT["2.30 credential status"] + CSTAT -.-> SL["2.35 status list"] + SL -.-> VCSR["2.38 VC status registry"] + VCSR -. "อ้างอิงใน trusted list" .-> TL["2.25 trusted list"] +``` + +**กลุ่มที่ 5 การเปิดเผยแบบเลือกได้และการออกเอกสารโดยหน่วยงานรัฐ** + +```mermaid +flowchart LR + SD["2.32 selective disclosure"] -.-> VP["2.7 VP"] + SD -.-> VC["2.6 VC"] + PuB["2.41 PuB-EAA"] -. "ออกโดยหน่วยงานรัฐ" .-> VC +``` + +## ตารางตรวจสอบย้อนกลับ (Traceability Matrix) + +ตารางนี้ระบุการใช้นิยามใหม่ในเอกสารอื่น ๆ ของโครงการ เพื่อให้ผู้อ่านตามรอยคำนิยามไปยังการใช้งานจริงในบทอื่นได้ + +| นิยาม | ใช้ในเอกสาร (TH) | ใช้ในเอกสาร (EN) | อ้างอิงมาตรฐาน | +|-------|------------------|-------------------|----------------| +| 2.23 wallet unit | §5 (Wallet Roles), §7 (Trusted List), §8 (Impl. Guidelines) | §5, §7, §8 | EUDI ARF 2.9.0 [D1] | +| 2.24 WUA | §5, §7, §9, §10 (Wallet Unit Attestation topic) | §5, §7, §9, §10 | EUDI ARF 2.9.0 [D1] | +| 2.25 trusted list | §7, §9 (Trust Model 3) | §7, §9 | ETSI TS 119 602 [D7], research/32, research/33 | +| 2.26 Trust Model 3 | §7, §9 | §7, §9 | research/32, research/33 [D2] | +| 2.27 cryptographic suite | §7, §9, §11 (S8 Security) | §7, §9, §11 | W3C VC Data Integrity 1.0 [D3] | +| 2.28 key management | §9, §12 | §9, §12 | research/31, FIPS 140-2 Level 3 (ยอมรับ FIPS 140-3 เป็นมาตรฐานสืบทอด) | +| 2.29 key rotation | §9, §12 | §9, §12 | CIR (EU) 2024/2979 [D8] | +| 2.30 credential status | §13 | §13 | IETF Token Status List [D4] | +| 2.31 VC revocation | §13 | §13 | IETF Token Status List [D4] | +| 2.32 selective disclosure | §5, §7 | §5, §7 | W3C VC DM 2.0 [D5] | +| 2.33 holder binding | §5, §7, §9 | §5, §7, §9 | CIR (EU) 2024/2979 [D8] | +| 2.34 key reference | §7, §9 | §7, §9 | W3C DID Core 1.0 | +| 2.35 status list | §13 | §13 | IETF Token Status List [D4] | +| 2.36 WSCD | §5, §7, §9, §10 | §5, §7, §9, §10 | EUDI ARF 2.9.0 [D1] | +| 2.37 WSCA | §5, §7, §9, §10 | §5, §7, §9, §10 | EUDI ARF 2.9.0 [D1] | +| 2.38 VC status registry | §13, usecase/09 | §13, usecase/09 (EN mirror) | usecase/09 [D6] | +| 2.39 LoTE | §7, §9 | §7, §9 | ETSI TS 119 602 [D7] | +| 2.40 LoTL | §7, §9 | §7, §9 | ETSI TS 119 602 [D7], CID 2025/2164 | +| 2.41 PuB-EAA | §5, §7 | §5, §7 | EUDI ARF 2.9.0 [D1] | +| 2.42 `cnf` | §5, §7, §9 | §5, §7, §9 | CIR (EU) 2024/2979 [D8] | + +> **หมายเหตุ:** หมายเลข § ในคอลัมน์ “ใช้ในเอกสาร” อ้างอิงบทในรายงานฉบับนี้ ได้แก่ §7 การทำงานร่วมกันข้ามระบบนิเวศและ Trusted List §9 Trust Model 3 §10 Wallet Unit Attestation (WUA) §11 ชุดวิธีการเข้ารหัสลับ (cryptographic suites) §12 การบริหารจัดการกุญแจและการติดตั้ง Trusted List §13 สถานะและการเพิกถอน VC และ §14 แบบจำลองภัยคุกคามและการทดสอบความสอดคล้อง + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 1 — ขอบข่าย](01-scope.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 3 — ภาพรวมการใช้งาน ➡️](03-usage-overview.md) diff --git a/docs/thai-vc-arf/03-usage-overview.md b/docs/thai-vc-arf/03-usage-overview.md new file mode 100644 index 0000000..33fef5d --- /dev/null +++ b/docs/thai-vc-arf/03-usage-overview.md @@ -0,0 +1,117 @@ +--- +description: "3. ภาพรวมการใช้งาน — Thai VC ARF 2.0 DRAFT 0" +--- + +# 3. ภาพรวมการใช้งาน + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารแปลงจาก PDF ต้นฉบับของ สพธอ. (Thai VC ARF v1.1, มกราคม 2568) +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + + +ในปัจจุบันเอกสารในรูปแบบของข้อมูลอิเล็กทรอนิกส์เริ่มมีการใช้งานแพร่หลายมากยิ่งขึ้น แต่การใช้งานยังคงถูกจำกัดอยู่ในแค่บางกรณี เนื่องจากการออกหนังสือหรือเอกสารบางส่วนยังคงอยู่ในรูปแบบกระดาษ ส่วนเอกสารอิเล็กทรอนิกส์ที่มีใช้งานอยู่นั้นก็มีรูปแบบและมาตรฐานที่หลากหลาย เอกสารที่ออกโดยหน่วยงานหนึ่งอาจต้องใช้วิธีการในการจัดเก็บและวิธีการในการตรวจสอบที่แตกต่างกันไปตามเทคโนโลยีที่ใช้งาน + +การใช้งานเอกสารอิเล็กทรอนิกส์ในระยะหลัง เริ่มมีแนวคิดของการนำเอกสารรับรองดิจิทัล (verifiable credential: VC) และเอกสารสำแดงดิจิทัล (verifiable presentation: VP) มาใช้งาน โดยเอกสารรับรองดิจิทัลและเอกสารสำแดงดิจิทัลนั้น มีคุณสมบัติที่ช่วยให้สามารถตรวจสอบความถูกต้องของเอกสารได้ว่าเอกสารดังกล่าวออกโดยหน่วยงานนั้นจริง และบุคคลที่ยื่นเอกสารเป็นเจ้าของเอกสารนั้นจริง นอกจากนั้น ยังช่วยเพิ่มความเป็นส่วนตัวให้กับผู้ใช้งาน เนื่องจากในบางกรณี ผู้ใช้งานสามารถเลือกแสดงข้อมูลในเอกสารบางส่วนได้ + +อย่างไรก็ตาม การนำ VC และ VP มาใช้งานนั้น ระบบที่ให้บริการแต่ละระบบจำเป็นต้องมีความสามารถในการทำงานร่วมกัน (interoperability) เพื่อให้สามารถเชื่อมต่อกันได้และสามารถตรวจสอบข้อมูลข้ามระบบได้อย่างมีประสิทธิภาพ เนื่องจากเอกสารบางประเภทอาจมีความเกี่ยวข้องกับหลายหน่วยงานไม่ว่าจะเป็นองค์กรภาครัฐ บริษัทเอกชน สถาบันการศึกษา หรือบริการออนไลน์ต่าง ๆ + +การใช้งานเอกสารรับรองดิจิทัลในภาพรวม ตั้งแต่การออกเอกสารจนถึงการตรวจสอบ แสดงได้ดังรูปที่ 1 + +![ภาพรวมการใช้งานของเอกสารรับรองดิจิทัล](images/p9-0.png) + +**รูปที่ 1 ภาพรวมการใช้งานของเอกสารรับรองดิจิทัล** + +{/* pdf page 10 */} + +## 3.1. บทบาทและความสัมพันธ์ของผู้เกี่ยวข้อง + +(1) ผู้สมัคร (applicant) และ ผู้ถือเอกสาร (holder) + +(2) ผู้ออกเอกสาร (issuer) + +(3) ผู้ตรวจสอบเอกสาร (verifier) + +(4) ผู้ให้บริการกระเป๋าเอกสารดิจิทัล (digital document wallet provider) + +(5) ระบบทะเบียนเอกสารรับรอง (verifiable data registry) + +การใช้งานกระเป๋าเอกสารดิจิทัลสำหรับเอกสารรับรองดิจิทัล ประกอบด้วยเอนทิตีและระบบที่เกี่ยวข้อง ได้แก่ ผู้สมัคร (applicant) ผู้ถือเอกสาร (holder) ผู้ออกเอกสาร (issuer) ผู้ตรวจสอบเอกสาร (verifier) ผู้ให้บริการกระเป๋าเอกสารดิจิทัล (digital document wallet provider) รวมไปถึงระบบทะเบียนเอกสารรับรอง (verifiable data registry) โดยบทบาทและความสัมพันธ์ของเอนทิตีที่เกี่ยวข้องเป็นไปตามรูปที่ 2 + +![บทบาทของผู้ที่เกี่ยวข้องกับการใช้งานเอกสารรับรองดิจิทัล](images/p10-0.png) + +**รูปที่ 2 บทบาทของผู้ที่เกี่ยวข้องกับการใช้งานเอกสารรับรองดิจิทัล** + +(1) ผู้สมัคร (applicant) ยื่นขอเอกสารรับรองดิจิทัลจากผู้ออกเอกสาร (issuer) พร้อมทั้งกำหนดให้จัดส่ง VC มายังกระเป๋าเอกสารดิจิทัลของผู้ถือเอกสาร (holder) ทั้งนี้ ผู้ออกเอกสารอาจกำหนดให้ผู้สมัครกรอกข้อมูลที่เกี่ยวข้องหรือแนบเอกสารเพิ่มเติม เพื่อให้ผู้ออกเอกสารตรวจสอบความถูกต้องก่อนออกเอกสารรับรองดิจิทัล + +(2) ผู้ถือเอกสาร (holder) ใช้งานและควบคุมการทำงานกระเป๋าเอกสารดิจิทัลที่ให้บริการโดยผู้ให้บริการกระเป๋าเอกสารดิจิทัล (digital document wallet provider) โดยกระเป๋าเอกสารดิจิทัลนั้นอาจอยู่ในรูปแบบแอปพลิเคชันบนอุปกรณ์เคลื่อนที่หรือเว็บไซต์ + +ข้อสังเกต: ผู้สมัคร ผู้ถือเอกสาร และเจ้าของข้อความ (subject) อาจเป็นบุคคลเดียวกันหรือไม่ก็ได้ ตัวอย่างเช่น บิดาเป็นผู้สมัครยื่นขอเอกสารรับรองดิจิทัลซึ่งมีบุตรเป็นเจ้าของข้อความ จากนั้นจัดเก็บ VC เอกสารรับรอง ในกระเป๋าเอกสารดิจิทัลของมารดาซึ่งมีบทบาทเป็นผู้ถือเอกสาร + +{/* pdf page 11 */} + +(3) ผู้ถือเอกสารสามารถสร้าง VP โดยใช้ข้อมูลจาก VC จากนั้นแสดง VP ต่อผู้ตรวจสอบเอกสาร (verifier) ซึ่งผู้ตรวจสอบเอกสารสามารถตรวจสอบความถูกต้องครบถ้วนของ VP ดังกล่าวได้ โดยอาศัยข้อมูลในระบบทะเบียนเอกสารรับรอง (verifiable data registry) + +(4) ระบบทะเบียนเอกสารรับรอง (verifiable data registry) เป็นตัวกลางในการสนับสนุนการสร้างและตรวจสอบเอกสารรับรอง โดยทำหน้าที่ลงทะเบียนตัวระบุ (identifier) รวมไปถึงกุญแจสาธารณะ (public key) ของแต่ละเอนทิตี และเค้าร่าง (schema) ของ VC และ VP + +ทั้งนี้ ผู้ออกเอกสารและผู้ตรวจสอบเอกสารอาจเป็นบุคคลซึ่งใช้งานกระเป๋าเอกสารดิจิทัลเช่นเดียวกันกับผู้ถือเอกสาร หรืออาจเป็นระบบขององค์กรที่ทำหน้าที่ออก VC เอกสารรับรองดิจิทัลหรือตรวจสอบ VP ในกรณีนี้ ระบบขององค์กรจะถือเป็นกระเป๋าเอกสารดิจิทัลขององค์กร (enterprise digital wallet) ซึ่งทำหน้าที่บริหารจัดการเอกสารรับรองดิจิทัลสำหรับองค์กรนั้น + +## 3.2. เอกสารรับรองดิจิทัลและเอกสารสำแดงดิจิทัล + +เอกสารรับรองดิจิทัล (verifiable credential: VC) อาจเป็นเอกสารที่ออกโดยหน่วยงานของรัฐหรือเอกสารอิเล็กทรอนิกส์ใด ๆ ที่ออกให้เพื่อรับรองข้อความที่เกี่ยวข้องกับเจ้าของข้อความ (subject) ซึ่งเอกสารรับรองดิจิทัลนี้จะมีคุณสมบัติที่สามารถตรวจสอบได้ว่าเอกสารนั้นไม่ถูกเปลี่ยนแปลง และสามารถยืนยันตัวบุคคลผู้ออกเอกสารได้ ทั้งนี้ เมื่อพิจารณาตาม Verifiable Credentials Data Model ของ W3C จะสามารถสรุปองค์ประกอบของ VC ได้ ดังนี้ + +(1) คำอธิบายข้อมูล (metadata) ของ VC ซึ่งอาจรวมถึงตัวระบุ (identifier) ของเอนทิตีที่เกี่ยวข้อง ข้อมูลของผู้ออกเอกสาร หรือวันหมดอายุของเอกสารรับรอง + +(2) ข้อความยืนยัน (claim) ซึ่งอาจมีเพียงข้อความยืนยันเดียวหรือหลายข้อความ โดยเป็นข้อมูลที่ยืนยันเกี่ยวกับเจ้าของข้อความ ซึ่งอาจเป็นบุคคลธรรมดา นิติบุคคล สิ่งของ หรือซอฟต์แวร์ + +(3) ข้อพิสูจน์ (proof) ของ VC ซึ่งอาจมีข้อพิสูจน์เดียวหรือหลายข้อพิสูจน์ โดยเป็นข้อมูลที่เป็นลายมือชื่อดิจิทัลของผู้ออกเอกสาร + +เอกสารสำแดงดิจิทัล (verifiable presentation: VP) คือ ชุดข้อมูลที่ผู้ถือเอกสารสร้างขึ้นจาก VC โดย VP นั้นจะมีคุณสมบัติที่สามารถตรวจสอบได้ว่าเอกสารนั้นไม่ถูกเปลี่ยนแปลง (integrity) และสามารถยืนยันตัวผู้ถือเอกสารได้ (authenticity) ทั้งนี้ เมื่อพิจารณาตาม Verifiable Credentials Data Model ของ W3C จะสามารถสรุปองค์ประกอบของ VP ได้ ดังนี้ + +(1) คำอธิบายข้อมูล (metadata) ของ VP + +(2) เอกสารรับรอง (verifiable credential: VC) ซึ่งอาจมี VC เดียวหรือหลาย VC ก็ได้ ทั้งนี้ VP อาจไม่ใช้ข้อมูลจาก VC โดยตรง แต่อาจใช้ข้อมูลซึ่งสร้าง (derived) มาจาก VC อีกที + +(3) ข้อพิสูจน์ (proof) ของ VP ซึ่งอาจมีข้อพิสูจน์เดียวหรือหลายข้อพิสูจน์ก็ได้ โดยเป็นข้อมูลที่เป็นลายมือชื่อดิจิทัลของผู้ถือเอกสาร + +{/* pdf page 12 */} + +องค์ประกอบพื้นฐานของ VC และ VP สรุปได้ตามรูปที่ 3 + +![องค์ประกอบพื้นฐานของเอกสารรับรองและเอกสารสำแดง](images/p12-0.png) + +**รูปที่ 3 องค์ประกอบพื้นฐานของเอกสารรับรองและเอกสารสำแดง** + +## 3.3. ระบบทะเบียนเอกสารรับรอง (verifiable data registry) + +ในการใช้งานเอกสารรับรองดิจิทัล เอนทิตีที่เกี่ยวข้องจะใช้งานกระเป๋าเอกสารดิจิทัลร่วมกับระบบทะเบียนเอกสารรับรอง โดยระบบทะเบียนเอกสารรับรองจะถูกนำมาใช้สนับสนุนการทำงานและจัดเก็บข้อมูลต่าง ๆ เช่น + +- ทะเบียนตัวระบุ (identifier) หรือตัวระบุแบบกระจายศูนย์ (decentralized identifier: DID) รวมไปถึง DID Document ของผู้ที่เกี่ยวข้อง เช่น ผู้ถือเอกสาร ผู้ออกเอกสาร หรือผู้ตรวจสอบเอกสาร + +ข้อสังเกต: การตรวจสอบ VC หรือ VP สามารถทำได้โดยการนำ DID ของ issuer หรือ holder มาใช้ในการดึงข้อมูล (resolve) DID Document ที่ถูกลงทะเบียนไว้ในระบบทะเบียนเอกสารรับรอง เพื่อนำ public key มาใช้ตรวจสอบลายมือชื่อดิจิทัล + +- ทะเบียนเค้าร่างของเอกสารรับรอง (credential schema) + +ข้อสังเกต: เพื่อให้ผู้ออกเอกสาร (issuer) สามารถเลือกใช้งานเค้าร่างของเอกสารรับรองได้ เมื่อต้องมีการออกเอกสารรับรองในลักษณะเดียวกัน โดยเอกสารรับรองที่ออกตาม credential schema เดียวกัน จะมีโครงสร้าง (structure) เนื้อหา (content) และรูปแบบ (format) ที่สอดคล้องกัน + +- ทะเบียนรายการเพิกถอนเอกสารรับรอง (revocation registry) + +นอกจากนี้ ระบบทะเบียนเอกสารรับรองอาจถูกนำมาใช้เป็นทะเบียนรายชื่อของเอนทิตีต่าง ๆ ในระบบได้ เช่น + +- รายชื่อผู้ให้บริการกระเป๋าเอกสารดิจิทัล (digital document wallet providers) +- รายชื่อผู้ออกเอกสาร (issuers) +- รายชื่อผู้ตรวจสอบเอกสาร (verifiers) +- รายชื่อผู้ให้บริการออกใบรับรอง (certification authorities: CA) + +ทั้งนี้ ระบบทะเบียนเอกสารรับรองสามารถพัฒนาได้หลากหลายรูปแบบ เช่น รูปแบบรวมศูนย์ (centralized) ด้วยระบบฐานข้อมูล หรือรูปแบบกระจายศูนย์ (decentralized) ด้วยเทคโนโลยีบล็อกเชน (blockchain) หรือโครงสร้างพื้นฐานใบรับเหตุการณ์กุญแจ (key event receipt infrastructure: KERI) หรือรูปแบบผสม + +> 📌 **หมายเหตุ (ส่วนขยายนอกรายงานต้นฉบับ):** แนวทางการจัดทำรายชื่อของเอนทิตีแบบกระจายศูนย์ในระยะแรก อ้างอิงตาม Trust Model 3 (Trusted List + DID) ดังรายละเอียดใน [§9 Trust Model 3](09-trust-model-3.md) + +{/* pdf page 13 */} + +--- + +**การนำทาง:** [⬅️ บทที่ 2 — บทนิยาม](02-definitions.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 4 — แนวคิดและองค์ประกอบที่ส่งเสริมการทำงานร่วมกัน ➡️](04-interoperability-concepts.md) diff --git a/docs/thai-vc-arf/04-interoperability-concepts.md b/docs/thai-vc-arf/04-interoperability-concepts.md new file mode 100644 index 0000000..9ae77d7 --- /dev/null +++ b/docs/thai-vc-arf/04-interoperability-concepts.md @@ -0,0 +1,34 @@ +--- +description: "4. แนวคิดและองค์ประกอบที่ส่งเสริมให้เกิดการทำงานร่วมกัน (interoperability) — Thai VC ARF 2.0 DRAFT 0" +--- + +# 4. แนวคิดและองค์ประกอบที่ส่งเสริมให้เกิดการทำงานร่วมกัน (interoperability) + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารแปลงจาก PDF ต้นฉบับของ สพธอ. (Thai VC ARF v1.1, มกราคม 2568) +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + + +เนื่องด้วยการใช้งานเอกสารรับรองดิจิทัลและเอกสารสำแดงดิจิทัลยังอยู่ในระยะเริ่มต้น การให้บริการในปัจจุบันจึงมีความหลากหลายทั้งในมิติเชิงธุรกิจและการพัฒนาระบบที่อ้างอิงกระบวนการทางเทคนิคหรือมาตรฐานที่แตกต่างกัน ในกรณีที่ต้องมีการขยายการใช้งานไปในวงกว้างหรือต้องการใช้งานเอกสารดังกล่าวข้ามระบบนิเวศ (Ecosystem) ก็อาจไม่สามารถทำงานร่วมกันได้ ดังนั้น ในการพัฒนาระบบเกี่ยวกับเอกสารรับรองดิจิทัลและเอกสารสำแดงดิจิทัล ผู้พัฒนาควรให้ความสำคัญในรายละเอียดของเรื่องดังต่อไปนี้ + +- มาตรฐานเกี่ยวกับ VC และ VP เนื่องจากเอกสารรับรองดิจิทัลหรือเอกสารสำแดงดิจิทัลเป็นเอกสารที่ต้องมีการส่งต่อหรือแลกเปลี่ยนระหว่างหลายหน่วยงาน หากเอกสารดังกล่าวมีรูปแบบที่แตกต่างกัน เช่น หน่วยงาน A และ หน่วยงาน B ใช้เอกสารที่มีโครงสร้างข้อมูลที่แตกต่างกัน ก็อาจส่งผลต่อการประมวลผลแบบอัตโนมัติของระบบ หรือทำให้ไม่สามารถตรวจสอบเอกสารดังกล่าวได้ + +- เค้าร่าง (schema) เอกสารรับรองที่ออกโดยหน่วยงานต่าง ๆ อาจถูกออกแบบและใช้โครงสร้างข้อมูลที่แตกต่างกัน เมื่อต้องมีการส่งเอกสารข้ามหน่วยงาน หากเอกสารดังกล่าวไม่ได้ใช้เค้าร่างข้อมูลเดียวกัน ก็อาจส่งผลให้มีโครงสร้าง (structure) เนื้อหา (content) และรูปแบบ (format) ที่แตกต่างกัน ซึ่งส่งผลต่อประมวลผลแบบอัตโนมัติของระบบ หรือทำให้ไม่สามารถตรวจสอบเอกสารดังกล่าวได้ + +- มาตรฐานเกี่ยวกับ Protocol สำหรับการออก VC เพื่อให้การเชื่อมต่อในขั้นตอนของการออก VC โดยผู้ออกเอกสาร (issuer) มีความมั่นคงและปลอดภัย และระบบต่าง ๆ สามารถเชื่อมต่อกันได้ด้วยวิธีการที่เป็นมาตรฐานเดียวกัน + +- มาตรฐานเกี่ยวกับ Protocol สำหรับการใช้งาน VP เพื่อให้การเชื่อมต่อในขั้นตอนของการใช้งาน VP ระหว่าง Digital Document Wallet และผู้ตรวจสอบเอกสาร รวมไปถึงผู้ถือเอกสารมีความมั่นคงและปลอดภัย และระบบต่าง ๆ สามารถเชื่อมต่อกันได้ด้วยวิธีการที่เป็นมาตรฐานเดียวกัน + +- มาตรฐานเกี่ยวกับตัวระบุ (identifier) ตัวระบุแบบกระจายศูนย์ (decentralized identifier: DID) และ DID Method เนื่องจากเป็นส่วนสำคัญเกี่ยวกับการระบุตัวบุคคลที่เกี่ยวข้องและการตรวจสอบ VC และ VP ดังนั้น identifier รวมไปถึงในส่วนของ DID Document ควรมีฟังก์ชันและการบริหารจัดการต่าง ๆ ที่มีความมั่นคงปลอดภัย ไม่ถูกแก้ไขโดยไม่ได้รับอนุญาต และสามารถดึงข้อมูล (resolve) มาใช้เมื่อต้องการตรวจสอบ VC หรือ VP ได้ + +- Universal Resolver เนื่องจากในการตรวจสอบ VC และ VP นั้นต้องมีการดึงข้อมูล DID Document ที่ถูกจัดเก็บไว้ใน registry ของแต่ละ DID Method โดยการดึงข้อมูลดังกล่าว ส่วนใหญ่จะเป็นการดำเนินการผ่านช่องทาง resolver ของแต่ละ DID Method ดังนั้น ในกรณีที่มีการใช้งานหลาย DID Method ร่วมกัน ย่อมต้องมีการใช้ resolver หลายอัน หากมี Universal resolver ที่เป็นเสมือนศูนย์รวมของ resolver ก็จะช่วยให้สามารถเข้าใช้งานและดึงข้อมูล DID Document ได้ที่จุดเดียว + +{/* pdf page 14 */} + +--- + +**การนำทาง:** [⬅️ บทที่ 3 — ภาพรวมการใช้งาน](03-usage-overview.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 5 — มาตรฐานและข้อปฏิบัติสำหรับ VC และ VP ➡️](05-standards-and-compliance.md) diff --git a/docs/thai-vc-arf/05-standards-and-compliance.md b/docs/thai-vc-arf/05-standards-and-compliance.md new file mode 100644 index 0000000..ec926df --- /dev/null +++ b/docs/thai-vc-arf/05-standards-and-compliance.md @@ -0,0 +1,706 @@ +--- +description: "5. มาตรฐานและข้อปฏิบัติสำหรับเอกสารรับรองดิจิทัลและเอกสารสำแดงดิจิทัล (Standards and Compliance for Verifiable Credentials and Presentations) — Thai VC ARF 2.0 DRAFT 0" +--- + +# 5. มาตรฐานและข้อปฏิบัติสำหรับเอกสารรับรองดิจิทัลและเอกสารสำแดงดิจิทัล (Standards and Compliance for Verifiable Credentials and Presentations) + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📐 Specification — มาตรฐานที่ประกาศใช้จริงในเวอร์ชัน 2.0 แทนที่เนื้อหาต้นฉบับเวอร์ชัน 1.1 ทั้งบท +> **เวอร์ชันเอกสาร:** 2.0 (10 สิงหาคม 2569) — แทนที่เค้าร่างเอกสารและโพรโทคอลของรายงานต้นฉบับทั้งหมด (ดูเหตุผลในกล่องด้านล่างและ [ภาคผนวก ข ข.3.6](15-appendix.md)) (MASA-170) +> **แหล่งข้อมูลเดิม:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย (มกราคม 2568) — บทนี้ปรับปรุงจากต้นฉบับดังกล่าว รายละเอียดที่ถูกแทนที่ดูได้จาก git history ของไฟล์นี้ +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) · [§8 — Implementation Guidelines](08-implementation-guidelines.md) · [§8.1 — OID4VCI Full Flow](08.1-issuance-full-flow-detail.md) · [§8.2 — OID4VP Full Flow](08.2-oid4vp-full-flow-detail.md) · [§11 — Cryptographic Suites](11-cryptographic-suites.md) · [ภาคผนวก ข — บันทึกการเปลี่ยนแปลง v1.1→v2.0](15-appendix.md) +*/} + +--- + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะการรองรับรูปแบบเอกสาร:** รายงานต้นฉบับเวอร์ชัน 1.1 ใช้ W3C Verifiable Credentials Data Model v1.1 (W3C VCDM) และโพรโทคอลฉบับร่าง สำหรับ ARF ฉบับนี้ ระบบยังคงรองรับ W3C VCDM v1.1 เพื่อใช้งานเอกสารเดิม และเพิ่ม IETF SD-JWT Verifiable Credential (`dc+sd-jwt`) [12] สำหรับการออกและการแสดงเอกสารตาม OID4VCI 1.0 Final [10] และ OID4VP 1.0 Final พร้อม DCQL [11] ตัวอย่างทางเทคนิคในบทนี้ใช้ SD-JWT VC เป็นหลัก ระบบต้องรองรับทั้ง W3C VCDM และ SD-JWT VC ควบคู่กัน +*/} + +เมื่อการทำธุรกรรมมีแนวโน้มมาอยู่ในรูปแบบอิเล็กทรอนิกส์มากขึ้น หน่วยงานทั้งภาครัฐและภาคเอกชนอาจมีการออกเอกสารรับรองข้อมูลในรูปของเอกสารรับรองดิจิทัลที่ตรวจสอบได้ (verifiable credential: VC) โดยเอกสารรับรองดิจิทัลที่ตรวจสอบได้ดังกล่าวเป็นเอกสารรับรองข้อมูลในรูปแบบอิเล็กทรอนิกส์ที่มีคุณสมบัติช่วยให้สามารถตรวจพบการปลอมแปลงและตรวจสอบผู้เขียนข้อมูลได้ด้วยกระบวนการเข้ารหัสลับ (Cryptography) + +ในรายงานฉบับนี้ เอกสารรับรองข้อมูลตามที่กล่าวข้างต้นประกอบด้วย 2 ส่วน ดังนี้ + +- เอกสารรับรองดิจิทัล (verifiable credential: VC) +- เอกสารสำแดงดิจิทัล (verifiable presentation: VP) + +ระบบต้องรองรับ W3C Verifiable Credentials Data Model v1.1 (W3C VCDM) [5] สำหรับเอกสารเดิม และ IETF SD-JWT Verifiable Credential (`dc+sd-jwt`) [12] ซึ่งเพิ่มใน ARF ฉบับนี้ สำหรับเอกสารใหม่ การออกและการสำแดงผ่านโพรโทคอล **OID4VCI 1.0 Final** [10] และ **OID4VP 1.0 Final พร้อม DCQL** [11] ใช้ `dc+sd-jwt` เป็นรูปแบบที่แนะนำ รายละเอียดทางเทคนิคระดับ field-by-field พร้อมตัวอย่าง SD-JWT VC อยู่ที่ [§8.1 — OID4VCI Full Flow](08.1-issuance-full-flow-detail.md) และ [§8.2 — OID4VP Full Flow](08.2-oid4vp-full-flow-detail.md) + +## 5.0 โครงของบทนี้ + +บทนี้มีเนื้อหายาว รายงานฉบับนี้จึงแบ่งออกเป็น 5 ส่วนหลัก เพื่อให้ผู้อ่านเข้าถึงหัวข้อที่ต้องการได้สะดวก ดังนี้ + +| ส่วน | หัวข้อในบท | เนื้อหาโดยย่อ | +|------|-----------|--------------| +| โครงสร้าง VC/VP | §5.1–§5.2 | การรองรับ W3C VCDM v1.1 สำหรับเอกสารเดิม และ **SD-JWT VC (`dc+sd-jwt`)** ที่เพิ่มใน ARF ฉบับนี้ พร้อมตัวอย่าง SD-JWT VC | +| การออกเอกสาร (OID4VCI) | §5.3 | ขั้นตอนการออก VC ด้วยโพรโทคอล **OID4VCI 1.0 Final** [10] | +| การสำแดงเอกสาร (OID4VP) | §5.4 | ขั้นตอนการสำแดงและตรวจสอบ VP ด้วยโพรโทคอล **OID4VP 1.0 Final พร้อม DCQL** [11] | +| Universal Resolver | §5.5 | การใช้ DIF Universal Resolver เพื่อการทำงานร่วมกันระหว่างระบบนิเวศ | +| บทบาทของผู้เกี่ยวข้อง | §5.6 | บทบาทของผู้ออกเอกสาร ผู้ถือเอกสาร และผู้ตรวจสอบเอกสาร | + +## 5.1 เอกสารรับรองดิจิทัล (verifiable credential: VC) + +เอกสารรับรองดิจิทัล ประกอบด้วย identifier และคำอธิบายข้อมูล (metadata) เช่น ผู้ออกเอกสาร วันและเวลาเมื่อ VC เริ่มมีผลผูกพันและสิ้นผลผูกพัน + +โครงสร้างของเอกสารรับรองดิจิทัลในรายงานทางเทคนิคฉบับนี้ แสดงตัวอย่าง SD-JWT VC (`dc+sd-jwt`) [12] ซึ่งประกอบด้วย (1) คำอธิบายข้อมูลของ VC (credential metadata) (2) ข้อความยืนยัน (claim) และ (3) ข้อพิสูจน์ (proof) การใช้ตัวอย่างดังกล่าวไม่กระทบการรองรับเอกสาร W3C VCDM v1.1 ที่ออกอยู่เดิม [5] + +![โครงสร้างเอกสารรับรอง (VC)](images/p15-0.png) + +**รูปที่ 4 โครงสร้างเอกสารรับรอง (VC)** + +เอกสารรับรองใช้เค้าร่าง **IETF SD-JWT Verifiable Credential (`dc+sd-jwt`)** [12] ซึ่งบันทึกทั้ง 3 ส่วนข้างต้นในรูปแบบ SD-JWT แทนการบันทึกแบบ JSON-LD โดย SD-JWT VC ประกอบด้วย 3 ส่วนเรียงต่อกันด้วยสัญลักษณ์ "~" ได้แก่ + +1. **Issuer-signed JWT** — JOSE header ระบุประเภทเอกสาร (`typ: "dc+sd-jwt"`) และวิธีลงลายมือชื่อ (`alg`) ตามด้วย JWT claims ที่มี claim ซึ่งถูกแฮชไว้ในอาร์เรย์ `_sd` (แทนการเปิดเผยค่าจริงในตัวเอกสาร) พร้อมกุญแจสาธารณะของผู้ถือเอกสาร (`cnf.jwk`) สำหรับผูกเอกสารไว้กับผู้ถือ +2. **Disclosures** — รายการค่าจริงของแต่ละ claim ที่เข้ารหัส Base64URL แยกทีละรายการ ผู้ถือเอกสารเลือกเปิดเผยเฉพาะรายการที่ต้องการได้ในภายหลัง โดยไม่กระทบลายมือชื่อของผู้ออกเอกสาร +3. **Key Binding JWT (KB-JWT)** — JWT ที่ผู้ถือเอกสารลงลายมือชื่อด้วยกุญแจของตนเอง (คู่กับ `cnf.jwk` ในข้อ 1) เพื่อพิสูจน์การครอบครองคู่กุญแจ สร้างขึ้นเฉพาะตอนนำเอกสารไปสำแดง (ดู §5.2) + +ในรายงานฉบับนี้ ได้ยกตัวอย่างกรณีศึกษาของ VC ที่แสดงกรณีศึกษาเรื่องการขอใบประมวลผลการศึกษา (Transcript) ตามรูปที่ 6 จากมหาวิทยาลัย โดยเอกสารจะออกให้เมื่อนักศึกษาพิสูจน์ได้ว่าตนเองเคยศึกษา ณ มหาวิทยาลัยดังกล่าว ซึ่งทางมหาวิทยาลัยได้ออก VC เพื่อรับรองการสำเร็จการศึกษา และนักศึกษาจัดเก็บ VC นั้นไว้ในกระเป๋าเอกสารดิจิทัลของตนเอง + +![VC JWT Format](images/p15-1.png) + +**รูปที่ 5 VC JWT Format** + +รูปที่ 5 แสดงแนวคิดทั่วไปของการบันทึกข้อมูล VC เป็นโครงสร้างหลายส่วนที่ลงลายมือชื่อแบบ JOSE ซึ่งเป็นแนวคิดเดียวกับที่ SD-JWT VC นำมาใช้ ต่างกันเพียงตัวคั่นส่วนย่อยเป็น "~" แทน "." และมีส่วน Disclosures กับ KB-JWT เพิ่มเข้ามาเพื่อรองรับ selective disclosure ตามที่อธิบายไว้ข้างต้น + +![กรณีศึกษา VC การขอใบประมวลผลการศึกษา (Transcript)](images/p16-0.png) + +**รูปที่ 6 กรณีศึกษา VC การขอใบประมวลผลการศึกษา (Transcript)** + +ขั้นตอนการออกใบประมวลผลการศึกษามีกระบวนการ (User Journey) ดังนี้ + +1. นักศึกษาหรือผู้ที่ต้องการหลักฐานการศึกษาส่งคำร้อง (request) ให้กับทางมหาวิทยาลัย ผ่านช่องทางที่เป็น web application และมีการยืนยันตัวตน (user authentication) +2. เมื่อนักศึกษาร้องขอเอกสารใบประมวลผลการศึกษา (Transcript) ไปยังมหาวิทยาลัย จากนั้นมหาวิทยาลัยจะดำเนินการสร้างคิวอาร์โคด (QR code) สำหรับรายละเอียดการเชื่อมต่อกับ wallet รับเอกสาร นักศึกษาสแกนคิวอาร์โคด (QR code) เพื่อขอรับเอกสาร และมหาวิทยาลัยจัดเตรียมข้อมูลสำหรับใบประมวลผลการศึกษา (Transcript) ในรูปแบบเอกสารรับรองดิจิทัล (VC) และทำการลงลายมือชื่ออิเล็กทรอนิกส์ +3. เมื่อนักศึกษาได้รับใบประมวลผลการศึกษา (Transcript) ในรูปแบบเอกสารรับรองดิจิทัล (VC) แล้วจึงนำเอกสารที่ได้รับไปจัดเก็บในกระเป๋าเอกสารดิจิทัลของนักศึกษา (digital document wallet) +4. เมื่อนักศึกษาต้องการไปสมัครงานกับบริษัทสมมติเป็นผู้ตรวจสอบเอกสาร ทางบริษัทจะส่งคำร้องขอ VC ที่ออกโดยมหาวิทยาลัยมายังกระเป๋าเอกสารดิจิทัลของนักศึกษา และกระเป๋าเอกสารดิจิทัลจะถามนักศึกษาว่าต้องการใช้ VC ที่มีอยู่หรือไม่ เมื่อนักศึกษาตอบตกลง VC จะถูกนำมาใช้สร้างเป็นเอกสารสำแดงดิจิทัล (VP) และส่งต่อไปให้บริษัทดำเนินการตรวจสอบ + +**ตัวอย่างที่ 1 โครงสร้าง VC ที่ใช้ในโครงการ แสดงรูปแบบ SD-JWT VC (`dc+sd-jwt`)** + +JOSE Header (ส่วนที่ 1 ของ Issuer-signed JWT): + +```json +{ + "typ": "dc+sd-jwt", + "alg": "EdDSA", + "kid": "https://credential-issuer.example.com#key-1" +} +``` + +JWT Claims (ส่วนที่ 2 ของ Issuer-signed JWT): + +```json +{ + "iss": "https://credential-issuer.example.com", + "vct": "https://credentials.example.com/vct/transcript", + "iat": 1755500991, + "exp": 1755501291, + "cnf": { + "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "holder_pub_key_x" } + }, + "_sd_alg": "sha-256", + "_sd": [ + "TGZlNVR4Rk1md0dhUUdicjFXODNZa3VqVWJPYld4Wko3ZTgxU1RCVVo1SQ", + "OFdVaXVUb2R2TzFyaWt6TzZrb0J0dUlvMEZuVjV5cE5xUUNMSDJZeXNXVQ" + ] +} +``` + +Disclosure (ส่วนที่ 2 ของ SD-JWT VC — ตัวอย่างการเปิดเผย claim `student`): + +``` +["3jqcb67z9wksrndv", "student", "Nattapong Methawon"] +``` + +| ส่วน | ตัวคั่น | เนื้อหา | +|------|--------|--------| +| Issuer-signed JWT | `~` หลังส่วนนี้ | JOSE header + JWT claims ที่ลงลายมือชื่อโดยมหาวิทยาลัย (ผู้ออกเอกสาร) | +| Disclosures | `~` คั่นระหว่างแต่ละรายการ | ค่าจริงของแต่ละ claim (เช่น `student`, `gpa`) ที่เข้ารหัส Base64URL แยกทีละรายการ | +| Key Binding JWT | ต่อท้ายสุด | สร้างเฉพาะตอนนำไปสำแดงเป็น VP (ดู §5.2) — ไม่มีในตัว VC ที่จัดเก็บไว้ในกระเป๋าเอกสาร | + +รายละเอียดโครงสร้าง field-by-field พร้อมตัวอย่างที่ใช้งานจริงในระบบอยู่ที่หัวข้อ STEP 6 — Issue VC ใน [§8.1 — OID4VCI Full Flow](08.1-issuance-full-flow-detail.md) และหมวดอัลกอริทึมที่รองรับอยู่ที่ [§11 — Cryptographic Suites](11-cryptographic-suites.md) + +## 5.2 เอกสารสำแดงดิจิทัล (verifiable presentation: VP) + +เอกสารสำแดงข้อมูลที่ประกอบด้วย VC อย่างน้อยหนึ่งชุด ตามรูปที่ 4 ซึ่งอาจเป็นข้อมูลต้นฉบับตามที่ปรากฏใน VC หรือเป็นข้อมูลที่สังเคราะห์จาก VC ก็ได้ โดย VP จะมีคุณสมบัติที่สามารถตรวจพบการเปลี่ยนแปลงใด ๆ ที่เกิดกับความถูกต้องครบถ้วนของข้อมูล และตรวจสอบลายมือชื่ออิเล็กทรอนิกส์ของผู้ถือเอกสารและตรวจสอบ VC ที่เกี่ยวข้องได้ด้วยกระบวนการเข้ารหัสลับ + +การสังเคราะห์ข้อมูลจาก VC เพื่อสร้างเป็น VP ใช้วิธีการที่รองรับการเลือกเปิดเผยข้อมูลบางส่วน (selective disclosure) ตามที่อธิบายไว้ใน §5.1 กล่าวคือ ผู้ถือเอกสารเลือกส่งเฉพาะ Disclosure ของ claim ที่ต้องการเปิดเผย โดยไม่ต้องส่ง claim อื่นที่อยู่ใน VC ทั้งหมด เช่น เลือกแสดงเฉพาะชื่อและนามสกุลจากข้อมูลทั้งหมดตามหน้าบัตรประชาชน โดยไม่เปิดเผยข้อมูลส่วนอื่น + +โครงสร้างของเอกสารสำแดงดิจิทัลในรายงานทางเทคนิคฉบับนี้ แสดงตัวอย่าง SD-JWT VC (`dc+sd-jwt`) [12] โดยประกอบด้วย (1) คำอธิบายข้อมูลของ VP (presentation metadata) (2) เอกสารรับรองดิจิทัล (VC) และ (3) ข้อพิสูจน์ (proof) ส่วนข้อพิสูจน์คือ Key Binding JWT (KB-JWT) ที่ผู้ถือเอกสารสร้างขึ้นและลงลายมือชื่อด้วยกุญแจของตนเอง — มาตรฐาน SD-JWT VC ไม่ต้องมี JWT-VP ห่ออีกชั้น เพราะ KB-JWT ทำหน้าที่พิสูจน์การครอบครองเอกสารในตัวอยู่แล้ว การใช้ตัวอย่างนี้ไม่ตัดการรองรับ VP ที่อ้างอิงเอกสาร W3C VCDM v1.1 เดิม [5] + +สำหรับในรายงานฉบับนี้ การตรวจสอบเอกสารสำแดงดิจิทัลมีขั้นตอนการตรวจสอบดังนี้ + +1. ตรวจสอบความถูกต้องของโครงสร้างเอกสารสำแดงดิจิทัล โดยแยกส่วนด้วยสัญลักษณ์ "~" แล้วตรวจว่าประกอบด้วย Issuer-signed JWT, Disclosures และ Key Binding JWT ครบถ้วนตามรูปแบบ SD-JWT +2. ตรวจสอบ Key Binding JWT (KB-JWT) โดยตรวจลายมือชื่อด้วยกุญแจสาธารณะของผู้ถือเอกสาร (`cnf.jwk`) ที่ปรากฏใน Issuer-signed JWT และตรวจว่าค่า `sd_hash` ใน KB-JWT ตรงกับค่าแฮชที่คำนวณจากส่วน Issuer-signed JWT และ Disclosures ที่ส่งมาจริง เพื่อยืนยันว่าผู้ส่งเป็นผู้ครอบครองคู่กุญแจของเอกสารจริง +3. ตรวจสอบผู้ออกเอกสาร (Issuer) เป็น Legal Entity หรือไม่ โดยการร้องขอ DID Document จาก Registry ผ่าน DIF Universal Resolver +4. ตรวจสอบ Disclosure แต่ละรายการที่เปิดเผยมาว่าเมื่อคำนวณแฮชแล้วตรงกับค่าที่ปรากฏในอาร์เรย์ `_sd` ของ Issuer-signed JWT หรือไม่ เพื่อยืนยันว่าข้อมูลที่เปิดเผยไม่ถูกแก้ไข +5. ตรวจสอบความถูกต้องของ Issuer-signed JWT โดยตรวจลายมือชื่อดิจิทัลด้วยกุญแจสาธารณะจาก Trusted List ที่เกี่ยวข้อง หรือ DID Document ที่ผูกกับรายการใน Trusted List แล้ว และตรวจสถานะกับขอบเขตสิทธิ์ของ Issuer ใน Trusted List ตาม [ข้อกำหนดการเผยแพร่ Trusted List](12.1-trust-list-publication-profile.md) + +เมื่อทำการตรวจสอบทั้ง 5 ขั้นตอนแล้วถูกต้องทั้งหมด จึงจะยอมรับว่า VP ที่ได้รับมาถูกสร้างออกมาอย่างถูกต้อง จึงจะนำไปใช้งานหรือดำเนินการอื่นต่อไปได้ รายละเอียดขั้นตอนการตรวจสอบเต็มรูปแบบ (รวม Trust Checkpoint ของ ETDA Trust Model) อยู่ที่ [§8.2 — OID4VP Full Flow](08.2-oid4vp-full-flow-detail.md) + +![โครงสร้างเอกสารสำแดงดิจิทัล VP](images/p19-0.png) + +**รูปที่ 7 โครงสร้างเอกสารสำแดงดิจิทัล VP** + +จากกรณีศึกษาในตัวอย่างที่ 1 เมื่อนักศึกษาได้รับ VC มาจัดเก็บไว้ในกระเป๋าเอกสารดิจิทัลของตนเองแล้ว ต่อมาจะนำเอกสารไปทำการสมัครงานจากบริษัทสมมติซึ่งเป็นผู้ตรวจสอบเอกสาร บริษัทที่รับสมัครจะส่งคำร้องขอ VC ที่ออกโดยมหาวิทยาลัยผ่านไปยังกระเป๋าเอกสารดิจิทัลของนักศึกษา และกระเป๋าเอกสารดิจิทัลจะถามนักศึกษาว่าต้องการใช้ VC ที่มีอยู่หรือไม่ เมื่อนักศึกษาตอบตกลง VC จะถูกนำมาใช้สร้างเป็น VP แล้วส่งต่อไปให้บริษัททำการตรวจสอบ ตัวอย่างของ VP ข้างต้นในรูปแบบ SD-JWT Presentation เป็นดังนี้ + +**ตัวอย่างที่ 2 โครงสร้าง VP ที่ใช้ในโครงการ แสดงรูปแบบ SD-JWT Presentation** + +Key Binding JWT (KB-JWT) — ส่วนสุดท้ายของ SD-JWT Presentation: + +```json +{ + "typ": "kb+jwt", + "alg": "ES256" +} +. +{ + "iss": "wallet-instance-abc123", + "aud": "https://verifier.example.com", + "nonce": "fa2450d7-8ca5-40d2-8832-b03891c355be", + "iat": 1755501000, + "sd_hash": "9GzX9RVLtj7dCbCyeGnHb-8O0R5Fp2VW8LTkfWzePUM" +} +``` + +**การประกอบ VP ฉบับสมบูรณ์:** `~~~` + +จากตัวอย่างที่ 1 และ 2 องค์ประกอบของ SD-JWT VC และ VP แบ่งออกเป็นส่วนหลัก ดังนี้ + +1. **JOSE header** (`typ`, `alg`, `kid`) ระบุประเภทเอกสาร ประเภทลายมือชื่อ และตัวชี้กุญแจที่ใช้ตรวจสอบ +2. **`iss`** เป็น claim ที่ระบุตัวตนของผู้ออกเอกสาร (URI แบบ https) +3. **`vct`** (Verifiable Credential Type) เป็น claim ที่ระบุประเภทของเอกสารรับรองตามมาตรฐาน IETF SD-JWT VC [12] สำหรับเอกสาร `dc+sd-jwt` โดยทำหน้าที่ใกล้เคียงกับคุณสมบัติ `type` ใน W3C VC Data Model v1.1 [5] เอกสาร W3C VCDM เดิมยังคงใช้ `type` ตามโครงสร้างของตน +4. **`iat`/`exp`** เป็น claim ที่ระบุวันและเวลาที่ออกเอกสาร (issuance) และวันหมดอายุ (expiration) ตามลำดับ +5. **`cnf.jwk`** เป็น claim ที่ระบุกุญแจสาธารณะของผู้ถือเอกสาร ใช้ผูกเอกสารไว้กับผู้ถือ (holder binding) และใช้ตรวจสอบ Key Binding JWT ตอนสำแดงเอกสาร +6. **`_sd` และ Disclosures** คือกลไก selective disclosure — `_sd` เก็บค่าแฮชของแต่ละ claim ส่วน Disclosure เก็บค่าจริงที่เปิดเผยได้ทีละรายการ แทนคุณสมบัติ `credentialSubject` ในโครงสร้าง JSON-LD +7. **Key Binding JWT (KB-JWT)** เป็นข้อพิสูจน์ (proof) ที่ผู้ถือเอกสารลงลายมือชื่อด้วยกุญแจของตนเอง ทำหน้าที่แทน `proof` ของผู้ถือเอกสารในโครงสร้าง JSON-LD และยืนยันว่าเป็นผู้ครอบครองเอกสารรับรองจริง + +รายละเอียดขั้นตอนสร้างและตรวจ KB-JWT พร้อมตัวอย่างที่ใช้งานจริงอยู่ที่ [§8.2 — OID4VP Full Flow](08.2-oid4vp-full-flow-detail.md) + +## 5.3 Protocol สำหรับการออก VC + +โพรโทคอล **OpenID for Verifiable Credential Issuance (OID4VCI) 1.0 Final** [10] เป็นโพรโทคอลที่อยู่ในกลุ่ม OpenID ใช้สำหรับการออกเอกสารรับรองดิจิทัลผ่านทาง API ระบบยังคงรองรับ W3C VCDM v1.1 สำหรับเอกสารเดิม [5] และเพิ่ม **IETF SD-JWT Verifiable Credential (`dc+sd-jwt`)** [12] สำหรับการออกเอกสารใหม่ โดยระบบระบุ `format: "dc+sd-jwt"` เมื่อขอหรือออกเอกสารในรูปแบบ SD-JWT VC + +![การใช้งาน OID4VC](images/p22-0.png) + +**รูปที่ 8 การใช้งาน OID4VC** + +![ขั้นตอนการทำ OID4VCI](images/p22-1.png) + +**รูปที่ 9 ขั้นตอนการทำ OID4VCI** + +### 5.3.1 กระบวนการทำงาน OID4VCI + +การออกเอกสารรับรองดิจิทัล (VC) ของใบประมวลผลการศึกษา ใช้โพรโทคอล OID4VCI 1.0 Final ผ่าน Pre-Authorized Code Flow มีขั้นตอนการทำงานตามรูปที่ 9 ดังนี้ + +1. การให้ความยินยอมและการรวบรวมข้อมูล +2. การสร้าง Credential Offer +3. การดึงข้อมูล Metadata ผู้ออกเอกสาร +4. การร้องขอ Token +5. การร้องขอและการออก Credential Request + +#### (1) การให้ความยินยอมและการรวบรวมข้อมูล + +ผู้ออกเอกสาร (Credential Issuer) ต้องได้รับการยินยอมจากผู้ถือเอกสาร และรวบรวมข้อมูลที่จำเป็นของผู้ถือเอกสารสำหรับการออกเอกสารรับรองดิจิทัลผ่านกระบวนการเฉพาะ เช่น การยืนยันตัวตน (Authentication) และการอนุญาต (Authorization) ของผู้ถือเอกสาร โดยกระบวนการนี้ไม่อยู่ในขอบเขตของ OID4VCI + +#### (2) การสร้าง Credential Offer + +ผู้ออกเอกสารสร้าง Credential Offer สำหรับเอกสารรับรองที่จะทำการออกและส่งไปยังกระเป๋าเอกสารดิจิทัล โดยมีวิธีส่งได้หลายวิธี แต่ในการศึกษาครั้งนี้ผู้ออกเอกสารจะสร้าง QR Code ให้กระเป๋าเอกสารสแกน หรือคัดลอก URI ส่งให้ผ่านช่องทางอื่น Credential Offer ประกอบด้วย URL ของผู้ออกเอกสาร, ตัวระบุ Credential Configuration และ Pre-Authorized Code + +**ตัวอย่าง credential offer** + +```json +{ + "credential_issuer": "https://credential-issuer.example.com", + "credential_configuration_ids": ["TranscriptCredential"], + "grants": { + "urn:ietf:params:oauth:grant-type:pre-authorized_code": { + "pre-authorized_code": "sX2CpoKx", + "tx_code": { + "length": 4, + "input_mode": "numeric", + "description": "Please provide the one-time code that was sent via e-mail" + } + } + } +} +``` + +**คำอธิบายพารามิเตอร์ (Pre-Authorized Code)** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| credential_issuer | Required | URL | URL ของผู้ออกเอกสารที่คำนึงถึงตัวพิมพ์เล็ก-ใหญ่ ใช้โปรโตคอล https และต้องประกอบด้วย scheme และ host รวมถึง port number และ path components ได้ตามต้องการ แต่ต้องไม่มีส่วนประกอบ query หรือ fragment | +| credential_configuration_ids | Required | Array | อาร์เรย์ของข้อความที่ไม่ซ้ำกัน โดยแต่ละข้อความคือตัวระบุ Credential Configuration ที่ปรากฏใน `credential_configurations_supported` ของ Metadata ผู้ออกเอกสาร ซึ่งผู้ออกเอกสารสามารถออกให้ได้สำหรับ Credential Offer ครั้งนี้ | +| grants | Optional | Grant Type | ระบุ Grant Types ที่ Authorization Server ของผู้ออกเอกสารใช้สำหรับ Credential Offer ครั้งนี้ โดยในการศึกษาใช้เป็น urn:ietf:params:oauth:grant-type:pre-authorized_code | +| pre-authorized_code | Required | String | รหัสที่แสดงถึงการอนุญาตของผู้ออกเอกสารให้ผู้ถือเอกสารใช้ขอรับเอกสารรับรองประเภทใดประเภทหนึ่ง รหัสนี้ต้องมีอายุสั้นและใช้ได้เพียงครั้งเดียว หากเลือกใช้ Pre-Authorized Code Flow ต้องมีพารามิเตอร์นี้ | +| tx_code | Optional | Object | ใช้ระบุว่า Authorization Server ต้องการ Transaction Code จากผู้ถือเอกสารพร้อมกับ Token Request ใน Pre-Authorized Code Flow หรือไม่ หาก Authorization Server ไม่ต้องการ Transaction Code พารามิเตอร์นี้จะไม่ถูกใส่เข้ามา Transaction Code มีวัตถุประสงค์เพื่อเชื่อมโยง Pre-Authorized Code กับธุรกรรมที่เฉพาะเจาะจง เพื่อป้องกันการใช้รหัสซ้ำโดยผู้โจมตี เช่น การสแกน QR code โดยไม่ได้รับอนุญาต แนะนำให้ส่ง Transaction Code ผ่านช่องทางอื่น | +| input_mode | Optional | String | กำหนดชุดตัวอักษรที่สามารถป้อนข้อมูลได้ ค่าเลือกที่เป็นไปได้คือเฉพาะตัวเลขหรือเฉพาะตัวอักษร ค่าเริ่มต้นคือตัวเลข | +| length | Optional | Integer | กำหนดความยาวของ Transaction Code ช่วยให้กระเป๋าเอกสารแสดงหน้าจอสำหรับป้อนข้อมูลได้ดีขึ้นและช่วยเพิ่มประสบการณ์การใช้งานของผู้ใช้ | +| description | Optional | String | คำแนะนำแก่ผู้ถือกระเป๋าเอกสาร เกี่ยวกับวิธีการรับรหัสธุรกรรม เช่น การบอกว่ารหัสจะถูกส่งผ่านช่องทางการสื่อสารใด แนะนำให้แสดงคำอธิบายนี้ถัดจากหน้าจอป้อน Transaction Code เพื่อปรับปรุงประสบการณ์การใช้งาน ข้อความนี้ต้องไม่เกิน 300 ตัวอักษร และไม่รองรับการแปลหลายภาษา | + +#### (3) การดึงข้อมูล Metadata ผู้ออกเอกสาร + +เพื่อดึง Metadata กระเป๋าเอกสารส่ง HTTP GET ไปที่ URL ของผู้ออกเอกสารต่อด้วย `/.well-known/openid-credential-issuer` เช่น `https://credential-issuer.example.com/.well-known/openid-credential-issuer` หาก URL ของผู้ออกเอกสารมี path component ( / ) ที่อยู่ท้ายสุด ให้ลบออกก่อนเพิ่ม `/.well-known/openid-credential-issuer` + +Metadata จะเป็น JSON ประกอบด้วย ประเภทและรูปแบบของเอกสารรับรองทั้งหมดที่ผู้ออกเอกสารสามารถออกได้, Token Endpoint, Credential Endpoint และอื่น ๆ + +**ตัวอย่าง Metadata** + +```json +{ + "credential_issuer": "https://credential-issuer.example.com", + "authorization_servers": [ "https://server.example.com" ], + "credential_endpoint": "https://credential-issuer.example.com/credential", + "deferred_credential_endpoint": "https://credential-issuer.example.com/deferred_credential", + "notification_endpoint": "https://credential-issuer.example.com/notification", + "credential_response_encryption": { + "alg_values_supported": [ + "ECDH-ES" + ], + "enc_values_supported": [ + "A128GCM" + ], + "encryption_required": false + }, + "display": [ + { + "name": "Example University", + "locale": "en-US" + }, + { + "name": "Example Université", + "locale": "fr-FR" + } + ], + "credential_configurations_supported": { + "TranscriptCredential": { + "format": "dc+sd-jwt", + "vct": "https://credentials.example.com/vct/transcript", + "scope": "TranscriptCredential", + "cryptographic_binding_methods_supported": [ + "jwk" + ], + "credential_signing_alg_values_supported": [ + "EdDSA" + ], + "credential_metadata": { + "claims": [ + { + "path": ["given_name"], + "display": [{ "name": "Given Name", "locale": "en-US" }] + }, + { + "path": ["family_name"], + "display": [{ "name": "Surname", "locale": "en-US" }] + }, + { "path": ["degree"] }, + { + "path": ["gpa"], + "display": [{ "name": "GPA" }] + } + ] + }, + "proof_types_supported": { + "jwt": { + "proof_signing_alg_values_supported": [ + "ES256" + ] + } + }, + "display": [ + { + "name": "University Credential", + "locale": "en-US", + "logo": { + "url": "https://university.example.edu/public/logo.png", + "alt_text": "a square logo of a university" + }, + "background_color": "#12107c", + "text_color": "#FFFFFF" + } + ] + } + } +} +``` + +**คำอธิบายค่าพารามิเตอร์** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| credential_issuer | Required | URL | URL ของผู้ออกเอกสารที่คำนึงถึงตัวพิมพ์เล็ก-ใหญ่ ใช้โปรโตคอล https และต้องประกอบด้วย scheme และ host รวมถึง port number และ path components ได้ตามต้องการ แต่ต้องไม่มีส่วนประกอบ query หรือ fragment | +| authorization_servers | Optional | Array of strings | อาร์เรย์ของสตริง ซึ่งแต่ละสตริงเป็นตัวระบุของ OAuth 2.0 Authorization Server ที่ผู้ออกเอกสารอ้างอิงเพื่อใช้ในการอนุมัติ หากไม่มีการระบุพารามิเตอร์นี้ ผู้ออกเอกสารจะทำหน้าที่เป็น Authorization Server ด้วย โดยใช้ตัวระบุของผู้ออกเอกสารเพื่อดึงข้อมูล Metadata ของ Authorization Server | +| credential_endpoint | Required | URL | URL ของ Credential Endpoint ของผู้ออกเอกสาร URL นี้ต้องใช้โปรโตคอล HTTPS | +| deferred_credential_endpoint | Optional | URL | URL ของ Deferred Credential Endpoint ของผู้ออกเอกสาร URL นี้ต้องใช้โปรโตคอล HTTPS หากไม่มีการระบุพารามิเตอร์นี้ แสดงว่าผู้ออกเอกสารไม่รองรับ Deferred Credential Endpoint | +| notification_endpoint | Optional | URL | URL ของ Notification Endpoint ผู้ออกเอกสารต้องใช้โปรโตคอล HTTPS หากไม่มีการระบุพารามิเตอร์นี้ แสดงว่าผู้ออกเอกสารไม่รองรับ Notification Endpoint | +| credential_response_encryption | Optional | Object | แสดงข้อมูลว่าผู้ออกเอกสารรองรับการเข้ารหัส Credential Response เพิ่มเติมจาก TLS หรือไม่ | +| alg_values_supported | Required* | Array | เป็นอาร์เรย์ที่ประกอบด้วยรายการของอัลกอริทึมการเข้ารหัส JWE ([RFC7516]) ที่ได้รับการสนับสนุนโดย Credential Endpoint เพื่อเข้ารหัสข้อมูล Credential Response ในรูปแบบ JWT ([RFC7519]) ตามที่กำหนดไว้ใน [RFC7518] | +| enc_values_supported | Required* | Array | เป็นอาร์เรย์ที่ประกอบด้วยรายการของอัลกอริทึมการเข้ารหัส JWE ([RFC7516]) (ค่า enc) ที่ได้รับการสนับสนุนโดย Credential Endpoint เพื่อเข้ารหัสข้อมูล Credential Response ในรูปแบบ JWT ([RFC7519]) ตามที่กำหนดไว้ใน [RFC7518] | +| encryption_required | Required* | Boolean | เป็นค่าบูลีนที่ระบุว่าผู้ออกเอกสารต้องการการเข้ารหัสเพิ่มเติมนอกเหนือจาก TLS สำหรับ Credential Response หรือไม่ หากค่าเป็น true ผู้ออกเอกสารกำหนดให้เข้ารหัส Credential Response ทุกครั้ง และกระเป๋าเอกสารต้องส่งคีย์เข้ารหัสใน Credential Request หากค่าเป็น false กระเป๋าเอกสารเลือกส่งคีย์เข้ารหัสได้ | +| display | Optional | Array of objects | อาร์เรย์ที่มีคุณสมบัติการแสดงผลของผู้ออกเอกสารในแต่ละภาษา | +| credential_configurations_supported | Required | Object | วัตถุที่อธิบายรายละเอียดของเอกสารรับรองที่ผู้ออกเอกสารรองรับ แต่ละรายการระบุ `format: "dc+sd-jwt"` และ `vct`; คำอธิบายการแสดงผลและรายการ claims อยู่ใน `credential_metadata` ตาม OID4VCI Final Appendix A และ B.2 โดย `path` เป็นอาร์เรย์ที่ไม่ว่างตาม Appendix C | + +> *ต้องใส่เมื่อมี credential_response_encryption + +#### (4) การร้องขอ Token + +![Token Exchange](images/p29-0.png) + +**รูปที่ 10 Token Exchange** + +- กระเป๋าเอกสารส่ง Pre-Authorized Code ที่ได้รับในขั้นตอนที่ 3 ไปยัง Token Endpoint หากผู้ออกเอกสารกำหนดให้ต้องใช้ Transaction Code กระเป๋าเอกสารจะส่งไปด้วย +- ผู้ออกเอกสารรับคำร้องขอโทเคนและทำการตรวจสอบสิทธิ์การเข้าถึงข้อมูล หากถูกต้องผู้ออกเอกสารจะดำเนินการสร้าง Access Token และส่ง Token กลับไปให้กระเป๋าเอกสาร + +**ตัวอย่างการส่ง Token Request ของกระเป๋าเอกสาร (Pre-Authorized Code)** + +```http +POST /token HTTP/1.1 +Host: server.example.com +Content-Type: application/x-www-form-urlencoded + +grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code +&pre-authorized_code=SplxlOBeZQQYbYS6WxSbIA +&tx_code=493536 +``` + +**คำอธิบายค่าพารามิเตอร์ (Pre-Authorized Code)** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| grant_type | Required | String | ระบุ Grant Types ที่ Authorization Server ของผู้ออกเอกสารใช้สำหรับ Credential Offer ครั้งนี้ โดยในการศึกษาใช้เป็น urn:ietf:params:oauth:grant-type:pre-authorized_code | +| pre-authorized_code | Required | String | รหัสที่ได้จากผู้ออกเอกสารที่แสดงถึงการอนุญาตเพื่อขอรับเอกสารรับรองประเภทใดประเภทหนึ่ง | +| tx_code | Optional | String | Transaction Code ที่ได้จากผู้ออกเอกสาร | + +Issuer สร้าง Access Token ตาม OID4VCI §6 และส่ง Token Response กลับให้ Holder. ตัวอย่างนี้ใช้ `Bearer` ตามรูปแบบมาตรฐาน: + +**ตัวอย่าง HTTP Token Response (Pre-Authorized Code)** + +```http +HTTP/1.1 200 OK +Content-Type: application/json +Cache-Control: no-store + +{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ", + "token_type": "Bearer", + "expires_in": 86400 +} +``` + +`c_nonce` และ `c_nonce_expires_in` ไม่ใช่สมาชิกที่กำหนดไว้ใน Token Response ของ OID4VCI Final. หาก Issuer ต้องการให้ Wallet ใส่ nonce ใน proof, Issuer ต้องประกาศ Nonce Endpoint ใน Credential Issuer Metadata. Wallet ส่ง HTTP POST โดยไม่ใช้ Access Token; Issuer ตอบ HTTP 2xx พร้อม JSON `c_nonce` และ `Cache-Control: no-store` ตาม §7. + +**ตัวอย่าง Nonce Endpoint Response:** + +```http +HTTP/1.1 200 OK +Content-Type: application/json +Cache-Control: no-store + +{ + "c_nonce": "tZignsnFbp" +} +``` + +เมื่อ Issuer มี Nonce Endpoint, Wallet ต้องใส่ `c_nonce` ใน claim `nonce` ของ proof JWT ตาม Appendix F.1. + +#### (5) การร้องขอและการออกเอกสารรับรอง + +![Credential Request](images/p31-0.png) + +**รูปที่ 11 Credential Request** + +- กระเป๋าเอกสารส่ง Credential Request ไปยัง Credential Endpoint พร้อม Access Token และ `proofs` เมื่อ Metadata ของ Credential ที่ขอระบุ `proof_types_supported` [10, §8.2] +- ผู้ออกเอกสารตรวจสอบ Access Token และ proofs ก่อนออกเอกสารรับรอง +- เมื่อผ่านการตรวจสอบ ผู้ออกเอกสารจะส่งเอกสารรับรองกลับไปยังกระเป๋าเอกสารใน Credential Response + +**ตัวอย่างการส่ง HTTP Request** + +```http +METHOD: HttpMethod(value=POST) +Content-Type: application/json +Authorization: Bearer eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJhN2MwODczZS02Y2IxLTRjYmMtYWM2OS1hYTM3YWZlNzgwNTQiLCJpc3MiOiJodHRwczovL2lzc3Vlci1hcGktdGVzdC5ldGRhLm9yLnRoIiwiYXVkIjoiQUNDRVNTIn0.dH8bufDYHuMagSi3IpnUUcscYcZDtYgygFaDX5xY-LBSVM4eUpUj-a50rd9tFo4mMMHMo1OiPvFHguMV1KiSBw + +BODY +Content-Type: application/json + +{ + "credential_configuration_id": "TranscriptCredential", + "proofs": { + "jwt": [ + "" + ] + } +} +``` + +**คำอธิบายค่าพารามิเตอร์** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| credential_configuration_id | Required* | String | ตัวระบุ Credential Configuration ตามที่ประกาศไว้ใน `credential_configurations_supported` ของ Metadata ผู้ออกเอกสาร ใช้เมื่อ Authorization Details ใน Token Response ไม่มี `credential_identifiers`; ห้ามส่งพร้อม `credential_identifier` [10, §8.2] | +| proofs | Required* | Object | หลักฐานการครอบครองกุญแจที่จะผูกกับ Credential ที่ออก ต้องมีเมื่อ Metadata ของ Credential ที่ขอระบุ `proof_types_supported` วัตถุนี้มีชื่อ proof type เป็น key เช่น `jwt` และมีค่าเป็นอาร์เรย์ที่ไม่ว่างของ proofs ตามชนิดนั้น [10, §8.2] | +| credential_response_encryption | Optional | Object | ข้อมูลสำหรับเข้ารหัส Credential Response หากไม่มีพารามิเตอร์นี้ Credential Response ที่ส่งกลับมาจะไม่ถูกเข้ารหัส | + +ค่า JWT ในตัวอย่างเป็น placeholder; JWT ที่ส่งจริงต้องลงลายมือชื่อด้วยกุญแจที่จะผูกกับ Credential สำหรับ JWT proof ชนิด `jwt` ตาม OID4VCI 1.0 Final Appendix F.1, JOSE header ต้องมี `typ: "openid4vci-proof+jwt"` และ `alg` ที่ใช้ลายมือชื่อแบบ asymmetric ส่วน claims ต้องมี `aud` และ `iat`; ต้องมี `nonce` เมื่อผู้ออกเอกสารมี Nonce Endpoint ค่า `aud` ต้องเป็น Credential Issuer Identifier ส่วน `key_attestation` หากใช้ จะอยู่ใน JOSE header ของ JWT proof ตามข้อกำหนด Appendix D และ F.1 [10] + +Wallet Attestation ตาม Appendix E ใช้ยืนยันตัว Wallet กับ Authorization Server ในคำขอ PAR หรือ Token Request ตามวิธีที่ Authorization Server กำหนด [10] จึงไม่ใช่สมาชิกของ JSON body ใน Credential Request ตัวอย่างนี้ + +Credential Response อาจเป็นแบบทันทีหรือแบบรอการดำเนินการ: + +- กรณีตอบกลับทันที: ผู้ออกเอกสารออกเอกสารรับรองที่ร้องขอได้ทันทีและส่งไปยังกระเป๋าเอกสาร +- กรณีรอตอบกลับ (Deferred Response): ผู้ออกเอกสารอาจไม่สามารถออกเอกสารรับรองที่ร้องขอได้ทันที และจะส่งพารามิเตอร์ transaction_id ให้กับกระเป๋าเอกสารเพื่อใช้ขอรับเอกสารรับรองเมื่อพร้อม รหัสสถานะ HTTP ต้องเป็น 202 + +การเข้ารหัสข้อมูล Credential Response: + +- กรณีไม่มีการเข้ารหัส สื่อประเภทของคำตอบ (media type) ต้องถูกตั้งค่าเป็น application/json +- หากผู้ถือเอกสารร้องขอการเข้ารหัส Credential Response โดยส่ง credential_response_encryption ในคำขอ ผู้ออกเอกสารต้องเข้ารหัสข้อมูลใน Credential Response เป็น JWT โดยใช้พารามิเตอร์จาก credential_response_encryption และสื่อประเภทของคำตอบ (media type) ต้องถูกตั้งค่าเป็น application/jwt +- หากมีการร้องขอให้เข้ารหัสใน Credential Request แต่ Credential Response ไม่ได้ถูกเข้ารหัส กระเป๋าเอกสารควรปฏิเสธ Credential Response + +**ตัวอย่างการส่ง HTTP Response ของผู้ออกเอกสาร (ไม่ได้เข้ารหัส)** + +```http +HTTP/1.1 200 OK +Content-Type: application/json +Cache-Control: no-store + +{ + "credentials": [ + { + "credential": "eyJ0eXAiOiJkYytzZC1qd3QiLCJhbGciOiJFZERTQSJ9...LUpixVCWJk0eOt4CXQe1NXK~WyIzanFjYjY3ejl3a3NybmR2Iiwgc3R1ZGVudCIsICJOYXR0YXBvbmcgTWV0aGF3b24iXQ" + } + ] +} +``` + +**คำอธิบายค่าพารามิเตอร์** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| credentials | Optional | Array | รายการ Credential ที่ออกให้; แต่ละรายการมี object ที่มี `credential` ตาม §8.3 | +| transaction_id | Optional | String | ใช้เมื่อ Issuer ออก Credential แบบ Deferred; response ต้องมี HTTP 202 และ `interval` ตาม §8.3 | +| notification_id | Optional | String | ตัวระบุ Credential ที่ต้องส่งกลับใน Notification Request ตาม §8.3 และ §11.1 | + +## 5.4 Protocol สำหรับการใช้งาน VP + +โพรโทคอล **OpenID for Verifiable Presentations (OID4VP) 1.0 Final** [11] ต่อยอดจากโพรโทคอล OID4VCI โดยมีการนำโพรโทคอล OpenID Connect (OIDC) มาใช้งานร่วมกันกับเอกสารสำแดงดิจิทัล (VP) ดังรูปที่ 12 การร้องขอ claim ใช้ **Digital Credentials Query Language (DCQL)** [11] แทนกลไก Presentation Exchange รุ่นก่อนหน้า โดยระบุ `format: "dc+sd-jwt"` และรายการ claim ที่ต้องการโดยตรงในคำร้องขอ + +1. **OpenID Connect (OIDC):** เป็นโปรโตคอลที่ใช้ในการยืนยันตัวตนบนอินเทอร์เน็ต โดยสามารถใช้ในการลงชื่อเข้าใช้และรับรองตัวตนของผู้ใช้ได้ โดยมีการใช้ Access Token และ ID Token เพื่อยืนยันตัวตนและรับข้อมูลพื้นฐานของผู้ใช้ +2. **Verifiable Presentations (VP):** เป็นรูปแบบการนำเสนอข้อมูลที่เชื่อถือได้ ที่สามารถใช้ในการยืนยันข้อมูลหรือข้อมูลตัวตนของบุคคลหรือองค์กร โดยมีการลงนามด้วยลายมือชื่อดิจิทัลและเข้ารหัสเพื่อให้มั่นใจได้ว่าข้อมูลมีความน่าเชื่อถือและไม่ได้ถูกแก้ไขโดยไม่ได้รับอนุญาต + +![การใช้งาน OID4VP](images/p34-0.png) + +**รูปที่ 12 การใช้งาน OID4VP** + +![ขั้นตอนการทำ OID4VP](images/p35-0.png) + +**รูปที่ 13 ขั้นตอนการทำ OID4VP** + +### 5.4.1 กระบวนการทำงาน OID4VP + +การตรวจสอบข้อมูลเอกสารสำแดงดิจิทัล (VP) ใช้โพรโทคอล OID4VP 1.0 Final พร้อม DCQL มีขั้นตอนการทำงานตามรูปที่ 13 ดังนี้ + +1. ผู้ตรวจสอบเอกสารส่ง Authorization Request ให้ผู้ถือเอกสาร +2. ผู้ถือเอกสารส่ง VP ให้ผู้ตรวจสอบผ่าน Authorization Response + +#### (1) ผู้ตรวจสอบเอกสารส่ง Authorization Request ให้ผู้ถือเอกสาร + +![สร้าง Authorization QR Code](images/p36-0.png) + +**รูปที่ 14 สร้าง Authorization QR Code** + +- Verifier สร้าง QR +- ผู้ถือเอกสารดำเนินการ Scan QR Code หรือ Upload QR Code ดังรูปที่ 14 + +**ตัวอย่างพารามิเตอร์ Authorization Request หลังถอดรหัส** + +> ตัวอย่างนี้แสดงค่าพารามิเตอร์ให้อ่านง่าย ไม่ใช่ข้อความ wire format ที่ส่งตรงใน QR หรือ HTTP request ทุกกรณี: QR อาจมี `request_uri` ให้ Wallet ไปดึง Request Object; หากส่งพารามิเตอร์ object เช่น `dcql_query` ใน Authorization Request ตาม OAuth ให้ serialize เป็น JSON string แล้ว form-encode ตาม OID4VP §5.1 [11]. ตัวอย่างนี้สมมติว่า `client_id` เป็น client ที่ลงทะเบียนไว้ล่วงหน้า และ `response_uri` เป็น URI ที่ client นี้ได้รับอนุญาตให้ใช้เป็น `redirect_uri` ตาม OID4VP §8.2 และ §5.9 โดยใช้ HTTPS. + +```json +{ + "client_id": "verifier-example", + "response_uri": "https://verifier.com/VP/callback", + "response_type": "vp_token", + "response_mode": "direct_post", + "dcql_query": { + "credentials": [ + { + "id": "transcript", + "format": "dc+sd-jwt", + "meta": { "vct_values": ["https://credentials.example.com/vct/transcript"] }, + "claims": [ + { "path": ["student"] } + ] + } + ] + }, + "nonce": "fa2450d7-8ca5-40d2-8832-b03891c355be", + "state": "8c17d20f4a936be105fd7c28a6403b91" +} +``` + +**คำอธิบายค่าพารามิเตอร์** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| `client_id` | Required | String | ตัวระบุผู้ตรวจสอบเอกสาร; หากไม่ใช้ Client Identifier Prefix ค่านี้ต้องอ้างถึง client ที่ Wallet ลงทะเบียนไว้ล่วงหน้า; หากใช้ prefix ต้องระบุเป็น `:` ตาม OID4VP §5.9 | +| `response_uri` | Required เมื่อใช้ `direct_post` | String | HTTPS URI ที่รับ Authorization Response | +| `response_type` | Required | String | OAuth Response Type; ตัวอย่างนี้ใช้ `vp_token` | +| `response_mode` | Required | String | Response Mode; ตัวอย่างนี้ใช้ `direct_post` เพื่อให้ Wallet ส่งคำตอบผ่าน HTTPS ตาม OID4VP §8.2 | +| `dcql_query` หรือ `scope` | อย่างใดอย่างหนึ่ง Required | Object หรือ String | ต้องระบุ DCQL Query ด้วย `dcql_query` หรือด้วย `scope` ที่แทน DCQL Query; ห้ามส่งทั้งสองพร้อมกัน. ตัวอย่างใช้ `dcql_query` และ `vct_values` ต้องตรงกับค่า `vct` ในตัวอย่าง Issuer Metadata และ SD-JWT VC ข้างต้น | +| `client_metadata` | Optional | JSON object | Metadata ของผู้ตรวจสอบเอกสาร; ต้องเข้ารหัสเป็น UTF-8 เมื่อนำส่ง. OID4VP §5.1 กำหนดว่าค่าจากแหล่งข้อมูลที่เชื่อถือได้อื่นมี precedence และ metadata อื่นที่ไม่ได้กำหนดไว้ต้องถูกละเว้น เว้นแต่ profile จะระบุ | +| `nonce` | Required | String | ค่าสุ่มใหม่ที่ผู้ตรวจสอบสร้างและเก็บไว้ เพื่อผูก VP กับธุรกรรม ตาม OID4VP §5.2 | +| `state` | Required ตามเงื่อนไขใน OID4VP §5.3; มิฉะนั้น Optional | String | ใช้เชื่อมโยงคำตอบกับธุรกรรมเดิม; ในตัวอย่างใส่ไว้เพื่อให้ผู้ตรวจสอบจับคู่ response กับ session ได้ | + +#### (2) ผู้ถือเอกสารส่ง VP ให้ผู้ตรวจสอบผ่าน Authorization Response + +![OID4VP Initiation](images/p38-0.png) + +**รูปที่ 15 OID4VP Initiation** + +- Verifier สร้าง QR Code และส่งให้ Holder ทำการ Scan QR หรือ Upload file QR Code ดังรูปที่ 15 +- Holder จับคู่ VC ในเครื่องกับ `dcql_query` แล้วเลือก Disclosure ของ claim ที่ต้องการเปิดเผย ประกอบเป็น SD-JWT Presentation ตามโครงสร้างในตัวอย่างที่ 2 +- Holder ส่งข้อมูล VP ให้ทาง Verifier + +**ตัวอย่างค่าพารามิเตอร์ Authorization Response ก่อน form-encoding** + +> ตัวอย่างนี้แสดงค่าเพื่อให้อ่านง่าย ไม่ใช่ HTTP body ที่ส่งจริง เมื่อใช้ `direct_post` Wallet ส่ง Authorization Response ไปยัง `response_uri` ผ่าน HTTPS; ต้อง serialize ค่า object ของ `vp_token` เป็น JSON string แล้ว form-encode ค่าพารามิเตอร์ก่อนส่ง ตาม OID4VP §8.2 [11]. + +```json +{ + "vp_token": { + "transcript": ["eyJ0eXAiOiJkYytzZC1qd3QiLCJhbGciOiJFZERTQSJ9...LUp~WyIzanFjYjY3ejl3a3NybmR2Iiwgc3R1ZGVudCIsICJOYXR0YXBvbmcgTWV0aGF3b24iXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9...ziRjjF8Z3V5QkHEhZTFplqnqfk2BxEtZDA"] + }, + "state": "8c17d20f4a936be105fd7c28a6403b91" +} +``` + +เมื่อนำส่งจริง HTTP request ใช้ `Content-Type: application/x-www-form-urlencoded`; ค่า `vp_token` จะเป็น JSON string ที่ถูก form-encode ไม่ใช่ JSON object ที่วางตรงใน body. + +**คำอธิบายค่าพารามิเตอร์** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| `vp_token` | Required เมื่อส่ง Authorization Response สำเร็จ | JSON object | จับคู่ `id` ของ DCQL Credential Query กับ array ของ Presentation ที่ตรงกับ query; ค่าในตัวอย่างเป็น SD-JWT Presentation (`Issuer-signed JWT~Disclosures~KB-JWT`) หนึ่งรายการ ตาม OID4VP §8.1 [11] | +| `state` | Required ตามเงื่อนไขใน OID4VP §5.3; มิฉะนั้น Optional | String | ใช้เชื่อมโยง Response กับ Request เดิม | + +### 5.4.2 กระบวนการ Resolve DID เพื่อตรวจสอบ VP + +กระบวนการ Resolve DID เพื่อตรวจสอบ VP มีขั้นตอนดังรูปที่ 16 + +![Resolve DID](images/p39-0.png) + +**รูปที่ 16 Resolve DID** + +- ผู้ตรวจสอบเอกสารรับข้อมูล VP จากผู้ถือเอกสาร ดำเนินการ Decode VP และตรวจสอบความถูกต้องของโครงสร้าง +- ผู้ตรวจสอบเอกสารทำการส่งข้อมูล DID ID ไปตรวจสอบข้อมูลใน Registry + - ข้อมูล DID => “did:tbsi:zv8fVB1i6S7MLMZX8wd3Ayo” +- การ Resolve ข้อมูลใน Registry มี 2 รูปแบบ + - ทำการ Request ผ่าน DIF Universal Resolver + - ทำการ Request ไปยัง Registry โดยตรง ซึ่งในรายงานฉบับนี้จะใช้วิธี “http get” +- ข้อมูลที่ได้จากการทำ Resolve คือข้อมูล DID Doc ของผู้ออกเอกสาร และส่งข้อมูล DID Doc กลับมาให้ผู้ตรวจสอบเอกสาร + +การตรวจสอบข้อมูลใน Registry ในรายงานฉบับนี้จะระบุพารามิเตอร์ดังนี้ + +**ตัวอย่างการส่ง HTTP Request** + +```http +GET 'http://example.verifier_url/GetDIDDoc?' Id=did:tbsi:zv8fVB1i6S7MLMZX8wd3Ayo +``` + +**คำอธิบายค่าพารามิเตอร์** + +| พารามิเตอร์ | Required | รายละเอียด | +|-------------|----------|------------| +| Id | Required | หมายเลข DID | + +ข้อมูลที่ได้จากการ Resolve และส่งกลับไปให้ Verifier + +**ตัวอย่างการส่ง HTTP Response ของ Registry ไปยัง Verifier** + +```json +{ + "status": 200, + "message": "Success", + "data": { + "@context": "https://www.context.org", + "id": "did:tbsi:zv8fVB1i6S7MLMZX8wd3Ayo", + "verificationMethod": [ + { + "id": "did:tbsi:zv8fVB1i6S7MLMZX8wd3Ayo#1XmYo2EnZwAL0giRxBWMADGk2Zei+TD2VKx7aWgWWAo=", + "type": "JsonWebKey2020", + "publicKeyJwk": { + "kty": "OKP", + "crv": "Ed25519", + "alg": "EdDSA" + } + } + ], + "assertionMethod": [] + } +} +``` + +**คำอธิบายค่าพารามิเตอร์** + +| พารามิเตอร์ | Required | Type | รายละเอียด | +|-------------|----------|------|------------| +| Status | Required | Number | สถานะการเรียกใช้งาน | +| Message | Required | String | คำอธิบาย status | +| Data | Required | JSON | ข้อมูล DID DOC | + +## 5.5 Universal Resolver + +เพื่อให้ทุกระบบนิเวศ (ecosystem) ทำงานร่วมกันได้ จำเป็นต้องมีตัวกลางสำหรับรวบรวมข้อมูลที่ใช้ตรวจสอบและยืนยันความถูกต้องของข้อมูลสำคัญในรูปแบบมาตรฐานเดียวกัน จึงเกิดแนวคิดการใช้ DIF Universal Resolver + +DIF Universal Resolver เป็นจุดเชื่อมต่อไปยังระบบนิเวศ (ecosystem) แต่ละแห่ง เพื่อให้การทำงานร่วมกัน (interoperability) เกิดขึ้นได้ แต่ละระบบนิเวศต้องลงทะเบียนใช้งาน DIF Universal Resolver โดยข้อมูลอยู่ในรูปแบบไฟล์อิเล็กทรอนิกส์มาตรฐานเดียวกันทั้งระบบนิเวศ และเมื่อต้องการตรวจสอบความถูกต้องของข้อมูลจากระบบนิเวศใด ก็สามารถสอบถามผ่าน DIF Universal Resolver ได้ทันที ทำให้แต่ละขั้นตอนดำเนินการได้รวดเร็วและเป็นมาตรฐานเดียวกัน + +## 5.6 บทบาทและกระบวนการที่เกี่ยวข้องภายใต้ VC Ecosystem + +รายงานฉบับนี้สรุปบทบาทและกระบวนการที่เกี่ยวข้องภายใต้ VC Ecosystem ดังรูปที่ 17 โดยมีรายละเอียดของแต่ละบทบาทดังนี้ + +![บทบาทของผู้ที่เกี่ยวข้องกับการใช้งานเอกสารรับรองดิจิทัล](images/p41-0.png) + +**รูปที่ 17 บทบาทของผู้ที่เกี่ยวข้องกับการใช้งานเอกสารรับรองดิจิทัล** + +**ก. ผู้ออกเอกสาร (Issuer) และกระบวนการที่เกี่ยวข้อง** + +1. สร้าง DID Document ในรูปแบบ Legal Person +2. ลงทะเบียนใน Registry +3. เป็นผู้สร้าง VC +4. ยึดมาตรฐานการส่ง VC ในรูปแบบ Protocol OID4VCI 1.0 Final +5. สร้างและแสดง QR Code เพื่อให้ Holder เข้ามาร้องขอข้อมูลที่ต้องการ +6. ออก Token เพื่อใช้ในการยืนยันการส่ง VC +7. ตรวจสอบความถูกต้องของข้อมูลร้องขอ VC ที่ได้รับจาก Holder ส่งข้อมูล VC ในรูปแบบ SD-JWT VC (`dc+sd-jwt`) ให้กับทาง Holder + +**ข. เจ้าของกระเป๋าเอกสารดิจิทัล (Wallet Holder) และกระบวนการที่เกี่ยวข้อง** + +1. สร้าง DID Document ในรูปแบบ Natural Person +2. ร้องขอข้อมูลที่ต้องการจาก Issuer +3. Scan QR Code ที่ Issuer แสดงขึ้นมา เพื่อดำเนินการขอข้อมูล VC ในรูปแบบ Protocol OID4VCI 1.0 Final +4. ร้องขอ Token จาก Issuer เพื่อใช้ตรวจสอบการขอ VC +5. จัดเก็บ VC ที่ได้จาก Issuer ใน Wallet สำเร็จ +6. เลือก Verifier ที่ต้องการส่งข้อมูล VC ให้ +7. สร้างข้อมูล VC ให้อยู่ในรูปแบบ VP โดยเลือก Disclosure ของ claim ที่ต้องการเปิดเผยและสร้าง Key Binding JWT +8. Scan QR Code ที่ Verifier แสดงขึ้นมา เพื่อดำเนินการส่งข้อมูล VP ในรูปแบบ Protocol OID4VP 1.0 Final พร้อม DCQL +9. ร้องขอ Token จาก Verifier เพื่อใช้ตรวจสอบการส่ง VP +10. ดำเนินการส่งข้อมูล VP ให้กับ Verifier สำเร็จ + +**ค. ผู้ตรวจสอบเอกสาร (Verifier) และกระบวนการที่เกี่ยวข้อง** + +1. ยึดมาตรฐานการรับ VP ในรูปแบบ Protocol OID4VP 1.0 Final พร้อม DCQL +2. สร้างและแสดง QR Code เพื่อให้ Holder เข้ามาร้องขอการส่งข้อมูล +3. ออก Token เพื่อใช้ในการยืนยันการรับ-ส่ง VP +4. รับข้อมูล VP จาก Holder สำเร็จ +5. ดึงข้อมูล (Resolve) จาก Registry จะดำเนินการผ่านขั้นตอน Decentralized Identity Foundation Universal Resolver (DIF Universal Resolver) เพื่อนำมาตรวจสอบความถูกต้องของผู้ออกข้อมูล VC +6. ดำเนินการตรวจสอบความถูกต้องของข้อมูล VP ที่ได้รับมา โดยตรวจสอบรายละเอียด ดังนี้ + - ถอดโครงสร้างเอกสารสำแดงดิจิทัล (Decode the VP) เป็นการแยกส่วนตามสัญลักษณ์ "~" ต้องประกอบด้วย + - Issuer-signed JWT (คำอธิบายข้อมูลของ VC และข้อความยืนยันที่ถูกแฮชไว้ใน `_sd`) + - Disclosures ของ claim ที่เปิดเผย + - Key Binding JWT (ข้อพิสูจน์การครอบครองของผู้ถือเอกสาร) + - ตรวจสอบลายมือชื่อดิจิทัล (Verify the Signature) จะตรวจสอบความถูกต้องของข้อมูลกุญแจสาธารณะของผู้ออกเอกสารรับรอง และตรวจลายมือชื่อ Key Binding JWT ด้วยกุญแจของผู้ถือเอกสาร + - ตรวจสอบความถูกต้องของผู้ออกเอกสาร (Check Issuer's Credentials) + - ตรวจสอบความถูกต้องของโครงสร้างเอกสารรับรองดิจิทัล (Validate the Credential Schema) + - ตรวจสอบวันหมดอายุของเอกสารรับรองดิจิทัล (Validate the Credential Expiry) + +--- + +**การนำทาง:** [⬅️ บทที่ 4 — แนวคิดและองค์ประกอบที่ส่งเสริมการทำงานร่วมกัน](04-interoperability-concepts.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 6 — Decentralized Identifiers Methodologies ➡️](06-did-methodologies.md) diff --git a/docs/thai-vc-arf/06-did-methodologies.md b/docs/thai-vc-arf/06-did-methodologies.md new file mode 100644 index 0000000..9d56ef0 --- /dev/null +++ b/docs/thai-vc-arf/06-did-methodologies.md @@ -0,0 +1,113 @@ +--- +description: "6. Decentralized Identifiers Methodologies — Thai VC ARF 2.0 DRAFT 0" +--- + +# 6. Decentralized Identifiers Methodologies + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารแปลงจาก PDF ต้นฉบับของ สพธอ. (Thai VC ARF v1.1, มกราคม 2568) +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + +บทนี้อธิบายวิธีการของตัวระบุแบบกระจายศูนย์ (decentralized identifier: DID) ที่รายงานฉบับนี้ใช้ ครอบคลุมรูปแบบ DID การสร้างและการลงทะเบียน DID Document และการดึงข้อมูล (resolve) DID Document + +เป็นตัวระบุ (identifier) บนระบบทะเบียนเอกสารรับรอง (verifiable data registry) แบบกระจายศูนย์ที่ใช้ URL ในการเชื่อมโยงกับเอนทิตี้ โดยทั่วไป DID จะนำมาใช้ใน VC เพื่อแสดงความเชื่อมโยงไปยังเจ้าของข้อความซึ่งทำให้สามารถส่งต่อหรือโอนย้าย VC จากกระเป๋าเอกสารดิจิทัลหนึ่งไปยังกระเป๋าเอกสารดิจิทัลอีกอันหนึ่งได้โดยไม่จำเป็นต้องออก VC ชุดใหม่อย่างไรก็ตาม VC ไม่จำเป็นต้องใช้ identifier เป็น DID ก็ได้ + +## 6.1. รูปแบบ DID + +อ้างอิงตาม W3C โครงสร้างของ DID สามารถแสดงได้ตามรูปที่ 18 ซึ่งประกอบด้วย 3 ส่วน ได้แก่ (1) scheme (2) DID method และ (3) DID method-specific string + +![โครงสร้าง DID](images/p44-0.png) + +**รูปที่ 18 โครงสร้าง DID** + +## 6.2. การสร้างและการลงทะเบียน DID Document + +ยกตัวอย่างการทำ Digital Document โครงสร้างของ DID จะเป็นตามรูปที่ 19 ในส่วนของ DID method จะระบุเป็น “tbsi” และ DID method-specific ขึ้นต้นด้วยตัวอักษร “z” และชุดข้อความสุ่ม 16 ตัวอักษร + +![โครงสร้าง DID Document](images/p44-1.png) + +**รูปที่ 19 ตัวอย่างโครงสร้าง DID** + +จากโครงสร้าง DID ข้างต้น เมื่อทำการสร้าง DID จะมีลักษณะองค์ประกอบ ตามรูปที่ 20 + +{/* pdf page 45 */} + +![DID ID](images/p45-0.png) + +**รูปที่ 20 DID ID** + +ข้อมูลที่ได้จากการสร้าง DID จะนำไปใช้ในการสร้าง DID Document เพื่อใช้ในการลงทะเบียน (Register) ในระบบ Registry ตามรูปที่ 21 การลงทะเบียนใน Registry จะทำโดยใช้โปรโตคอล OID4VP ซึ่งข้อมูลที่นำมาใช้ในการลงทะเบียน คือ DID Document ในรายงานฉบับนี้จะเป็นการลงทะเบียนแบบออฟไลน์ (Offline) + +![การลงทะเบียน DID Document](images/p45-1.png) + +**รูปที่ 21 การลงทะเบียน DID Document** + +(1) DID Document + +เป็นชุดข้อมูลที่อธิบาย DID ประกอบด้วยข้อมูลที่ใช้ในการเข้ารหัสลับ (encrypted data) และกุญแจสาธารณะ (public key) ดังแสดงตามรูปที่ 22 + +{/* pdf page 46 */} + +![โครงสร้าง DID Document](images/p46-0.png) + +**รูปที่ 22 โครงสร้าง DID Document** + +## 6.3. การดึงข้อมูล (Resolve) DID Document + +ระบบของประเทศไทยใช้ DID Method หลัก 3 รูปแบบ แต่ละรูปแบบมีวิธี resolve ต่างกัน ดังนี้ + +| DID Method | ผู้ใช้ | วิธี Resolve | ต้อง Resolver กลาง? | +|------------|--------|-------------|:-------------------:| +| `did:web` | หน่วยงาน (Issuer, Verifier, Wallet Provider) | HTTPS GET ไปที่ `/.well-known/did.json` ของโดเมนต์นั้น | ไม่ | +| `did:jwk` | ผู้ถือเอกสาร (Holder) | กุญแจสาธารณะฝังอยู่ในสายอักขระ DID โดยตรง ไม่ต้องดึงจากที่อื่น | ไม่ | +| `did:ndid` | ระบบตัวตนของไทย (เช่น NDID) | ส่งคำขอไปยัง ETDA Trust Gateway ซึ่งทำหน้าที่เป็น Universal Resolver | ใช่ | + +```mermaid +flowchart LR + USER["ผู้ตรวจสอบ/กระเป๋า"] --> CHECK{"DID Method?"} + CHECK -->|did:web| HTTPS["HTTPS GET
.well-known/did.json"] + CHECK -->|did:jwk| SELF["แยกกุญแจจากสายอักขระ
(self-contained)"] + CHECK -->|did:ndid หรือ custom| UR["ETDA Trust Gateway
(Universal Resolver)"] + HTTPS --> DOC["DID Document"] + SELF --> KEY["กุญแจสาธารณะ"] + UR --> DOC + + style CHECK fill:#FFF3E0,stroke:#E65100 + style UR fill:#E3F2FD,stroke:#1565C0 +``` + +{/* TODO: อัปเดตแผนภาพและรายละเอียดการ resolve ของ did:ndid เมื่อมีการกำหนด spec ของ did:ndid method อย่างเป็นทางการ */} + +DID Document ที่ได้จากการ resolve จะมีกุญแจสาธารณะที่ใช้ตรวจลายมือชื่อของเอนทิตีนั้น ทั้งนี้ การ resolve ผ่าน DIF Universal Resolver ยังคงเป็นกลไกสำหรับ custom DID method ที่ยังไม่ได้จดทะเบียนใน W3C DID method registry + +{/* pdf page 47 */} + +![ขั้นตอน Resolve DID Document](images/p47-0.png) + +**รูปที่ 23 ขั้นตอน Resolve DID Document** + +![การดึงข้อมูลผ่าน http get](images/p47-1.png) + +**รูปที่ 24 การดึงข้อมูลผ่าน http get** + +ในตัวอย่าง จะเป็นการแสดงถึงการเรียกในรูปแบบ http get โดย Key ที่ส่งไปพร้อมกับ URL คือ DID ของ Issuer ที่ได้มาจากข้อมูลของ VC และเมื่อส่งข้อมูลในรูปแบบ URL แล้วข้อมูลถูกต้อง ทาง Registry จะตอบกลับเป็น DID Document มาให้ โดย DID Document จะเป็นของ Issuer ที่ได้ทำการลงทะเบียนไว้กับ Registry + +{/* pdf page 48 */} + +![ข้อมูลที่ผ่านการ resolve](images/p48-0.png) + +**รูปที่ 25 ข้อมูลที่ผ่านการ resolve** + +![การตั้งค่า Registry สำหรับ DIF Universal Resolver](images/p48-1.png) + +**รูปที่ 26 การตั้งค่า Registry สำหรับ DIF Universal Resolver** + +{/* pdf page 49 */} + +--- + +**การนำทาง:** [⬅️ บทที่ 5 — มาตรฐานและข้อปฏิบัติสำหรับ VC และ VP](05-standards-and-compliance.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 6.5 — กลไกการสร้างความน่าเชื่อถือ (Trust-Building Mechanism) ➡️](06.5-trust-building-mechanism.md) diff --git a/docs/thai-vc-arf/06.5-trust-building-mechanism.md b/docs/thai-vc-arf/06.5-trust-building-mechanism.md new file mode 100644 index 0000000..4cbde77 --- /dev/null +++ b/docs/thai-vc-arf/06.5-trust-building-mechanism.md @@ -0,0 +1,223 @@ +--- +description: "6.5 กลไกการสร้างความน่าเชื่อถือ (Trust-Building Mechanism): Trusted List และแบบจำลองความน่าเชื่อถือ 3 รูปแบบ — Thai VC ARF 2.0 DRAFT 0" +--- + +# 6.5 กลไกการสร้างความน่าเชื่อถือ (Trust-Building Mechanism): Trusted List และแบบจำลองความน่าเชื่อถือ 3 รูปแบบ + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📊 ส่วนขยาย (extension) — จัดทำเพิ่มเติมโดยคณะทำงาน VC มิได้เป็นส่วนหนึ่งของรายงานต้นฉบับ Thai VC ARF เวอร์ชัน 1.1 +> **แหล่งข้อมูล:** สรุปและสังเคราะห์จากการวิเคราะห์เปรียบเทียบช่องโหว่ความปลอดภัยของ PKI การเข้ารหัสลับ และระบบ VC [G1] การเปรียบเทียบสถาปัตยกรรมความน่าเชื่อถือ 3 รูปแบบ [G2] [G3] และมาตรฐานที่เกี่ยวข้อง ได้แก่ W3C DID Core [G4] OID4VCI [G5] OID4VP [G6] SD-JWT VC [G7] IETF Token Status List [G8] ETSI TS 119 602 [G9] +> **เอกสารที่เกี่ยวข้อง:** [§3.3 ระบบทะเบียนเอกสารรับรอง](03-usage-overview.md) · [§6 DID Methodologies](06-did-methodologies.md) · [§7 การทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List](07-cross-ecosystem-interoperability.md) · [§9 Trust Model 3](09-trust-model-3.md) · [research/31 ช่องโหว่ PKI/คริปโทฯ vs VC](../research/31-security-gaps-pki-crypto-vs-vc.md) · [ดัชนี ARF](README.md) +*/} + +--- + +บทนี้เป็นส่วนขยายของรายงาน Thai VC ARF เวอร์ชัน 1.1 จัดทำขึ้นเพื่อเป็นบทเกริ่นนำเรื่องกลไกการสร้างความน่าเชื่อถือ (trust-building mechanism) ก่อนเข้าสู่บทที่ 7 (การทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List) บทที่ 8 (Implementation Guidelines) และบทที่ 9 (Trust Model 3) โดยรายงานต้นฉบับเวอร์ชัน 1.1 ระบุไว้ใน [§3.3](03-usage-overview.md) ว่าระบบทะเบียนเอกสารรับรอง (verifiable data registry) อาจจัดเก็บรายชื่อของเอนทิตี (entity) ได้ แต่มิได้อธิบายว่ากลไกที่ทำให้รายชื่อดังกล่าวน่าเชื่อถือทำงานอย่างไร ทั้งบทที่ 7 อ้างถึง "ผลของ Trusted List" และบทที่ 9 กล่าวถึง Trust Model 3 โดยตรง จึงจำเป็นต้องมีบทเกริ่นนำเพื่อให้ผู้อ่านเข้าใจพื้นฐานก่อน + +บทนี้ครอบคลุม 3 ส่วน ดังนี้ (1) ความหมายของ Trusted List และประโยชน์ของ Trusted List ต่อระบบ (2) สรุปพื้นฐานเปรียบเทียบระหว่างโครงสร้างพื้นฐานกุญแจสาธารณะ (public key infrastructure: PKI) กับการเข้ารหัสลับแบบกระจายศูนย์ (decentralized cryptography) ตาม [G1] และ (3) ภาพรวมของแบบจำลองความน่าเชื่อถือ (trust model) ทั้ง 3 รูปแบบ เพื่อให้ผู้อ่านเข้าใจ Trust Model 1 และ Trust Model 2 ก่อนศึกษารายละเอียดของ Trust Model 3 ในบทที่ 9 + +อย่างไรก็ตาม บทนี้เป็นบทเกริ่นนำเชิงภาพรวม มิได้ทดแทนรายละเอียดในบทอื่น รายละเอียดของ Trust Model 3 อยู่ในบทที่ 9 รายละเอียดการจัดการกุญแจ (key management) และการหมุนกุญแจ (key rotation) อยู่ในบทที่ 12 และรายละเอียดสถานะและการเพิกถอน (revocation) อยู่ในบทที่ 13 + +--- + +## 6.5.1 บทบาทของบทนี้และขอบเขต + +รายงานฉบับนี้กำหนดให้บทนี้ทำหน้าที่เชื่อม (bridge) ระหว่างบทที่ว่าด้วยตัวระบุแบบกระจายศูนย์ (decentralized identifier: DID) ใน [§6](06-did-methodologies.md) กับบทที่ว่าด้วยการทำงานร่วมกันและความน่าเชื่อถือใน [§7](07-cross-ecosystem-interoperability.md) ถึง [§9](09-trust-model-3.md) โดยมีขอบเขต ดังนี้ + +1. **อยู่ในขอบเขต** — ความหมายและประโยชน์ของ Trusted List ภาพรวมของช่องโหว่ความปลอดภัย (security gap) ที่ระบบต้องปิด การเปรียบเทียบแนวทาง PKI กับการเข้ารหัสลับแบบกระจายศูนย์ และภาพรวมของแบบจำลองความน่าเชื่อถือทั้ง 3 รูปแบบ +2. **ไม่อยู่ในขอบเขต** — กระบวนการทำงานเชิงลึกของ Trust Model 3 (อยู่ในบทที่ 9) การจัดการกุญแจและการหมุนกุญแจ (อยู่ในบทที่ 12) โครงสร้างข้อมูลสถานะและการเพิกถอน (อยู่ในบทที่ 13) และการเชื่อมโยงข้ามพรมแดน ซึ่งมีรายละเอียดในแหล่งอ้างอิงเฉพาะเรื่อง + +--- + +## 6.5.2 ปัญหาความน่าเชื่อถือที่ระบบต้องแก้ + +ก่อนอธิบายว่า Trusted List ช่วยอะไร รายงานฉบับนี้สรุปช่องโหว่ความปลอดภัยที่ระบบเอกสารรับรองดิจิทัลต้องปิดไว้ก่อน โดยอ้างอิงการวิเคราะห์ตาม [G1] ซึ่งจัดช่องโหว่หลักเป็น 7 หมวด (G1–G7) ดังตารางต่อไปนี้ + +| หมวด | ช่องโหว่ | ประเด็นที่ต้องตอบ | +|:---:|---------|-------------------| +| G1 | การปลอมตัวเป็นผู้ออกเอกสาร (impersonation) | ผู้ตรวจสอบทราบได้อย่างไรว่าผู้ลงลายมือชื่อเป็นตัวจริง | +| G2 | กุญแจรั่วหรือถูกขโมย (key compromise) | เมื่อกุญแจรั่วไหล จะจำกัดความเสียหายอย่างไร | +| G3 | การเพิกถอน (revocation) | ยกเลิกสิ่งที่ออกไปแล้วได้หรือไม่ และเร็วเพียงใด | +| G4 | การนำลายมือชื่อเก่ามาใช้ซ้ำ (replay) | ป้องกันการนำข้อมูลเก่ามาใช้หลอกได้อย่างไร | +| G5 | จุดล้มเหลวจุดเดียว (single point of failure) | หากศูนย์กลางหยุดทำงาน ระบบยังตรวจสอบได้หรือไม่ | +| G6 | ความเป็นส่วนตัว (privacy) | ตรวจสอบได้โดยไม่เปิดเผยข้อมูลเกินจำเป็นหรือไม่ | +| G7 | การกู้คืนกุญแจของผู้ใช้ (user key recovery) | เมื่อผู้ใช้ทำกุญแจหาย จะกู้คืนได้หรือไม่ | + +**ตารางที่ 6.5-1 ช่องโหว่ความปลอดภัย 7 หมวด (G1–G7)** [G1] + +รายงานฉบับนี้ยึดหลักการป้องกันเป็นชั้น (defense-in-depth) กล่าวคือ ระบบที่มั่นคงปลอดภัยไม่ควรพึ่งการป้องกันชั้นเดียว แต่ต้องซ้อนหลายชั้น เพื่อให้เมื่อชั้นหนึ่งบกพร่อง ยังมีชั้นถัดไปรองรับ ดังรูปที่ 6.5-1 + +```mermaid +flowchart TB + L1["ชั้น 1 — กุญแจ
จัดเก็บกุญแจในอุปกรณ์ที่มั่นคงปลอดภัย เช่น HSM"] + L2["ชั้น 2 — ลายมือชื่อ
ลงลายมือชื่อข้อมูลเพื่อป้องกันการปลอมแปลง"] + L3["ชั้น 3 — Trusted List
ตรวจสอบว่าเอนทิตีเป็นตัวจริงและได้รับการรับรอง"] + L4["ชั้น 4 — การเพิกถอน
ยกเลิกกุญแจหรือเอกสารที่มีปัญหา"] + L5["ชั้น 5 — นโยบาย fail-closed
เมื่อไม่มั่นใจ ให้ปฏิเสธไว้ก่อน"] + L1 --> L2 --> L3 --> L4 --> L5 --> OK["ผลลัพธ์ — เชื่อถือได้"] +``` + +**รูปที่ 6.5-1 การป้องกันเป็นชั้น (defense-in-depth)** [G1] + +--- + +## 6.5.3 ความหมายของ Trusted List และการแก้ปัญหาความน่าเชื่อถือ + +ระบบรายชื่อที่น่าเชื่อถือ (Trusted List) หมายถึง รายการของเอนทิตีที่ได้รับการตรวจรับรอง (audit) และลงลายมือชื่อดิจิทัล (digital signature) กำกับโดยสำนักงานพัฒนาธุรกรรมทางอิเล็กทรอนิกส์ (สพธอ.) เพื่อใช้ตัดสินว่าเอนทิตีใดได้รับอนุญาตให้ทำหน้าที่ผู้ออกเอกสาร (issuer) ผู้ตรวจสอบเอกสาร (verifier) หรือผู้ให้บริการกระเป๋าเอกสารดิจิทัล (digital document wallet provider) รายการทั้งสามประเภทเผยแพร่เป็น JWT ที่มี payload แบบ LoTE; ตัวอย่างที่มีอยู่แสดงข้อมูลกุญแจหรือ DID ใน `ServiceDigitalIdentity` แต่ฟิลด์สถานะและขอบเขตสิทธิ์บางส่วนยังต้องกำหนดใน Thai profile ก่อนใช้ตัดสินใจจริง [G2] [G9] [รูปแบบ JWT ที่อ้างอิง LoTE](../research/42-trusted-list-lote-jwt-format.md) + +Trusted List ช่วยแก้ปัญหาความน่าเชื่อถือ ดังนี้ + +1. **ปิด G1 (การปลอมตัว)** — เอนทิตีทุกรายต้องผ่านการตรวจรับรองจาก สพธอ. ก่อนถูกบันทึกลง Trusted List ผู้ตรวจสอบจึงเทียบกุญแจในลายมือชื่อกับกุญแจใน Trusted List เพื่อยืนยันว่าเป็นตัวจริง +2. **ช่วยปิด G3 (การเพิกถอน)** — เมื่อเอนทิตีมีปัญหา สพธอ. เปลี่ยนสถานะใน Trusted List เป็นระงับ (suspended) หรือเพิกถอน (revoked) ได้ ทำให้ผู้ตรวจสอบปฏิเสธเอนทิตีนั้นได้ +3. **รองรับ G5 (จุดล้มเหลวจุดเดียว)** — เอนทิตีดึงข้อมูล Trusted List มาจัดเก็บไว้ในเครื่อง (local cache) แล้วตรวจสอบระหว่างกันได้โดยไม่ต้องเรียกไปยัง สพธอ. ทุกครั้ง จึงกระจายสำเนา Trusted List ไปหลายจุดได้ + +ผู้รับ Trusted List ต้องตรวจสอบลายมือชื่อดิจิทัลของ สพธอ. ทุกครั้ง รวมถึงกรณีที่รับไฟล์ผ่านโครงข่ายกระจายเนื้อหา (content delivery network: CDN) กลไกการทำงานพื้นฐานแสดงได้ดังรูปที่ 6.5-2 โดยมีรายละเอียดเชิงลึกในบทที่ 9 + +```mermaid +flowchart LR + ETDA["สพธอ. ตรวจรับรอง (audit)
แล้วลงลายมือชื่อ Trusted List"] + PUB["เผยแพร่ Trusted List ที่ลงลายมือชื่อแล้ว"] + CACHE["เอนทิตีดึงมาจัดเก็บ (cache)
ตรวจลายมือชื่อของ สพธอ. ก่อนใช้"] + VERIFY["เทียบกุญแจ + บทบาท + สถานะ
→ ตรวจลายมือชื่อของ VC/VP"] + ETDA --> PUB --> CACHE --> VERIFY +``` + +**รูปที่ 6.5-2 กลไกพื้นฐานของ Trusted List — ตรวจรับรอง ลงลายมือชื่อ เผยแพร่ และตรวจสอบ** [G2] [G9] + +--- + +## 6.5.4 ความน่าเชื่อถือ 2 สายหลัก: PKI และการเข้ารหัสลับแบบกระจายศูนย์ + +รายงานฉบับนี้สรุปพื้นฐานตาม [G1] ว่า วิธีทำให้ลายมือชื่อดิจิทัลน่าเชื่อถือมี 2 สายหลัก ได้แก่ PKI ซึ่งมีตัวกลางที่เชื่อถือได้ และการเข้ารหัสลับแบบกระจายศูนย์ ซึ่งไม่มีตัวกลาง เนื้อหาส่วนนี้เป็นบทสรุปเพื่อให้เข้าใจว่าเหตุใดระบบ VC ของไทยจึงเลือกแนวทางแบบผสม รายละเอียดฉบับเต็มอยู่ใน [research/31](../research/31-security-gaps-pki-crypto-vs-vc.md) + +### 6.5.4.1 PKI — มีตัวกลางที่เชื่อถือได้ + +PKI ใช้ผู้ให้บริการออกใบรับรอง (certification authority: CA) เป็นตัวกลางตรวจสอบตัวตนก่อนออกใบรับรอง (certificate) แล้วสร้างห่วงโซ่ความเชื่อถือ (chain of trust) ไล่ขึ้นไปถึง Root CA [G10] PKI ปิดช่องโหว่ได้ ดังนี้ + +1. **G1 การปลอมตัว** — CA ตรวจตัวตนก่อนออกใบรับรอง และมีห่วงโซ่ความเชื่อถือให้ตรวจสอบย้อนกลับได้ [G10] +2. **G2 กุญแจรั่ว** — จัดเก็บกุญแจในฮาร์ดแวร์ความมั่นคงปลอดภัย (hardware security module: HSM) [G14] และกำหนดอายุการใช้กุญแจ (cryptoperiod) ตามคำแนะนำการจัดการกุญแจ [G15] +3. **G3 การเพิกถอน** — ประกาศยกเลิกใบรับรองผ่านบัญชีเพิกถอน (certificate revocation list: CRL) หรือตรวจสถานะแบบเรียลไทม์ผ่าน OCSP (online certificate status protocol) [G10] [G11] + +จุดแข็งของ PKI คือมีผู้รับผิดชอบชัดเจนและเพิกถอนสิ่งที่รั่วไหลได้จริง จึงเหมาะกับงานระดับชาติที่ต้องมีความรับผิดชอบตามกฎหมาย ทั้งนี้ จุดอ่อนคือ หาก CA ถูกเจาะจะกระทบทั้งระบบ (G5) จึงต้องป้องกัน CA อย่างเข้มงวด [G1] + +### 6.5.4.2 การเข้ารหัสลับแบบกระจายศูนย์ — ไม่มีตัวกลาง + +การเข้ารหัสลับแบบกระจายศูนย์ เช่น เทคโนโลยีบล็อกเชน (blockchain) ใช้แนวคิดตรงข้าม คือไม่มีตัวกลาง ผู้ใช้แต่ละรายถือกุญแจของตนเอง และมีบัญชีสาธารณะ (ledger) ที่แก้ย้อนหลังไม่ได้ [G12] แนวทางนี้มีจุดเด่นและข้อจำกัด ดังนี้ + +1. **G5 จุดล้มเหลวจุดเดียว — ปิดได้ดีที่สุด** เพราะกระจายศูนย์ (decentralized) ไม่มีศูนย์กลางจุดเดียวให้หยุดทำงาน [G12] +2. **G7 การกู้คืนกุญแจ — ปิดได้ดี** ด้วยแนวคิดวลีกู้คืน (seed phrase) และการกู้คืนบัญชีโดยบุคคลหรืออุปกรณ์ที่ไว้วางใจ (social recovery) [G1] +3. **G3 การเพิกถอน — ทำไม่ได้ในระดับกุญแจดิบ** เนื่องจากไม่มีตัวกลางคอยยกเลิก หากกุญแจรั่วจึงทำได้เพียงย้ายทรัพย์สินไปยังที่อยู่ใหม่ [G12] + +รายงานฉบับนี้จึงประเมินว่า แนวคิด "ไม่มีตัวกลาง" ไม่เหมาะเป็นแกนหลักของเอกสารรับรองระดับชาติ เพราะระบบต้องเพิกถอนสิ่งที่รั่วไหลได้ อย่างไรก็ตาม แนวคิดการกู้คืนกุญแจของผู้ใช้ (G7) มีประโยชน์ และอาจนำมาใช้เสริมเฉพาะกุญแจในเครื่องของผู้ถือเอกสาร [G1] + +### 6.5.4.3 ตารางเปรียบเทียบและบทเรียนสำหรับระบบ VC + +การเปรียบเทียบความสามารถในการปิดช่องโหว่ของ 2 สายหลัก และบทเรียนสำหรับระบบ VC สรุปได้ดังตารางที่ 6.5-2 โดยใช้สัญลักษณ์ ✅ = ปิดได้ดี · 🟡 = ปิดได้บางส่วน · ❌ = ปิดไม่ได้หรือไม่เหมาะ + +| ช่องโหว่ | PKI | การเข้ารหัสลับแบบกระจายศูนย์ | บทเรียนสำหรับระบบ VC | +|---------|:---:|:---:|---------------------| +| G1 การปลอมตัว | ✅ | 🟡 | ใช้แนว PKI — มีตัวกลางตรวจตัวตน (Trusted List) | +| G2 กุญแจรั่ว | ✅ | 🟡 | ใช้แนว PKI (HSM + cryptoperiod) | +| G3 การเพิกถอน | ✅ | ❌ | จำเป็นต้องมี — ใช้ Trusted List status ร่วมกับ Token Status List [G8] | +| G4 การใช้ซ้ำ | ✅ | ✅ | ใช้ได้ทั้งคู่ (timestamp / nonce) | +| G5 จุดล้มเหลวจุดเดียว | 🟡 | ✅ | ใช้แนวการเข้ารหัสลับแบบกระจายศูนย์ — กระจายสำเนา Trusted List หลายจุด | +| G6 ความเป็นส่วนตัว | 🟡 | 🟡 | ใช้การเปิดเผยข้อมูลแบบเลือกได้ (selective disclosure) ด้วย SD-JWT VC [G7] | +| G7 การกู้คืนกุญแจผู้ใช้ | ❌ | ✅ | ใช้แนวการเข้ารหัสลับแบบกระจายศูนย์ เสริมเฉพาะกุญแจในเครื่องผู้ใช้ | + +**ตารางที่ 6.5-2 การปิดช่องโหว่ G1–G7: PKI เทียบกับการเข้ารหัสลับแบบกระจายศูนย์** [G1] + +รายงานฉบับนี้จึงสรุปว่า ระบบ VC ของไทยเลือก **ยืมแกนของ PKI เป็นหลัก** (มีตัวกลาง คือ ETDA Trusted List) เพื่อปิด G1 และ G3 ซึ่งจำเป็นต่อเอกสารระดับชาติ แล้วหยิบแนวคิดของการเข้ารหัสลับแบบกระจายศูนย์มาเสริมเฉพาะที่จำเป็น ได้แก่ การกระจายสำเนา Trusted List (ปิด G5) และการกู้คืนกุญแจในเครื่องผู้ใช้ (ปิด G7) การตอบสนองต่อกรณีกุญแจรั่วของแต่ละแนวทางเปรียบเทียบได้ดังรูปที่ 6.5-3 [G1] + +```mermaid +flowchart TB + Leak["พบว่ากุญแจรั่วหรือถูกขโมย"] + Leak --> PKI["PKI
CA ประกาศเพิกถอนผ่าน CRL/OCSP
ผู้ตรวจสอบเห็นทันทีที่สอบถาม"] + Leak --> VC["ระบบ VC
ผู้ออกเอกสารตั้งสถานะใน Token Status List เป็น revoked
และออกเอกสารใหม่ (re-issue)"] + Leak --> Crypto["การเข้ารหัสลับแบบกระจายศูนย์
เพิกถอนไม่ได้
ทำได้เพียงย้ายทรัพย์สินไปที่อยู่ใหม่"] + PKI --> Done["จำกัดความเสียหายได้"] + VC --> Done + Crypto --> Risk["เสี่ยง — ของเก่ายังถูกใช้ต่อได้"] +``` + +**รูปที่ 6.5-3 การตอบสนองต่อกรณีกุญแจรั่วของแต่ละแนวทาง** [G1] + +--- + +## 6.5.5 แบบจำลองความน่าเชื่อถือ 3 รูปแบบ + +จากการเปรียบเทียบสถาปัตยกรรมความน่าเชื่อถือ [G2] [G3] รายงานฉบับนี้จำแนกแบบจำลองความน่าเชื่อถือของระบบ VC ไว้ 3 รูปแบบ โดยทั้ง 3 รูปแบบยืมแกนของ PKI ร่วมกัน (มีตัวกลาง คือ Trusted List ที่ สพธอ. กำกับดูแล) แต่ต่างกันที่ "ตัวกลางหนักหรือเบา" ความหมายของแต่ละแบบจำลอง มีดังนี้ + +**ข้อกำหนดร่วม:** ไม่ว่าเลือกใช้ Trust Model 1, Trust Model 2 หรือ Trust Model 3 ผู้เข้าร่วมทุกฝ่ายต้องได้รับการตรวจรับรองและปรากฏใน Trusted List ที่เกี่ยวข้องก่อนนำข้อมูลความน่าเชื่อถือไปใช้ตัดสินใจ ระบบต้องดึง ตรวจลายมือชื่อ และตรวจสถานะ บทบาท และช่วงเวลาที่มีผลใช้ได้ของ Trusted List ตามขั้นตอนเดียวกัน ความแตกต่างของแต่ละแบบจำลองมีเฉพาะวิธีผูกและตรวจสอบกุญแจ ได้แก่ สายใบรับรองใน Trust Model 1 และ 2 หรือ DID/key reference ใน Trust Model 3 เท่านั้น [G2] [G3] + +1. **Trust Model 1 — CA Delegation** หมายถึง แบบจำลองที่มีผู้ให้บริการออกใบรับรองระดับรอง (subordinate CA: SubCA) ภายใต้ผู้ให้บริการออกใบรับรองแห่งชาติ (Thailand National Root Certification Authority: NRCA) เป็นผู้ออกใบรับรองให้ผู้ออกเอกสารและผู้ตรวจสอบเอกสาร แล้วลงทะเบียนข้อมูลความน่าเชื่อถือไว้ใน Trusted List โดย สพธอ. ทำหน้าที่ตรวจรับรองเอนทิตีและกำกับดูแล Trusted List [G2] [G13] +2. **Trust Model 2 — ETDA SubCA** หมายถึง แบบจำลองที่คล้าย Trust Model 1 แต่ สพธอ. เป็น SubCA ภายใต้ NRCA เองเพื่อออกใบรับรอง แบบจำลองนี้ต้องดำเนินการและตรวจประเมิน (audit) เทียบเท่า SubCA สาธารณะเต็มรูปแบบ [G2] +3. **Trust Model 3 — Trusted List + DID** หมายถึง แบบจำลองที่ไม่ใช้ CA เป็นสายความน่าเชื่อถือหลัก เอนทิตีสร้างคู่กุญแจ (key pair) และ DID ของตนเอง แล้วนำมาลงทะเบียนต่อ สพธอ. เมื่อผ่านการตรวจรับรองจึงถูกบันทึกลง Trusted List โดย สพธอ. ควบคุมการตรวจรับรองและลงลายมือชื่อกำกับ Trusted List [G2] [G4] + +การเปรียบเทียบทั้ง 3 แบบจำลอง สรุปได้ดังตารางที่ 6.5-3 + +| หัวข้อ | Trust Model 1 (CA Delegation) | Trust Model 2 (ETDA SubCA) | Trust Model 3 (Trusted List + DID) | +|--------|-------------------------------|----------------------------|---------------------------------| +| ผู้ออกใบรับรอง | SubCA ภายใต้ NRCA | สพธอ. เป็น SubCA ภายใต้ NRCA | ไม่มี CA สำหรับความน่าเชื่อถือของเอนทิตี | +| ที่มาของกุญแจ | เอนทิตีสร้างเอง ผูกกับใบรับรอง | เอนทิตีสร้างเอง ผูกกับใบรับรอง | เอนทิตีสร้างเอง (key/DID) | +| การตรวจรับรอง | สพธอ. ตรวจรับรองเอนทิตี | สพธอ. ตรวจรับรองเอนทิตี | สพธอ. ตรวจรับรองเอนทิตี | +| ที่จัดเก็บความน่าเชื่อถือ | Trusted List | Trusted List | Trusted List | +| ต้นทุนและความซับซ้อน | สูง (ค่าตรวจประเมิน CA) | สูงที่สุด (ตั้ง SubCA + ค่าตรวจประเมินต่อเนื่อง) | ต่ำที่สุด (ไม่มีค่าตรวจประเมิน CA) | + +**ตารางที่ 6.5-3 การเปรียบเทียบแบบจำลองความน่าเชื่อถือ 3 รูปแบบ** [G2] [G3] + +รายงานฉบับนี้กำหนดให้ **Trust Model 3 เป็นแนวทางหลักในระยะแรก (พ.ศ. 2570–2571)** เนื่องจากมีต้นทุนและความซับซ้อนต่ำที่สุด ไม่มีภาระค่าตรวจประเมินและค่าลิขสิทธิ์ซอฟต์แวร์ของ CA และสอดคล้องกับผลการวิเคราะห์เชิงข้อเท็จจริงและการพิจารณาของคณะทำงาน [G2] [G3] ความสัมพันธ์ของทั้ง 3 แบบจำลองแสดงได้ดังรูปที่ 6.5-4 ทั้งนี้ รายละเอียดของ Trust Model 3 มีอยู่ในบทที่ 9 + +```mermaid +flowchart TB + NRCA["NRCA (Thailand Root CA)"] + TL["ETDA Trusted List
(สพธอ. ตรวจรับรอง + ลงลายมือชื่อ)"] + M1["Trust Model 1
SubCA ภายใต้ NRCA ออกใบรับรอง"] + M2["Trust Model 2
สพธอ. เป็น SubCA เอง"] + M3["Trust Model 3
ไม่มี CA — ใช้ key/DID + Trusted List"] + NRCA --> M1 + NRCA --> M2 + M1 --> TL + M2 --> TL + M3 --> TL +``` + +**รูปที่ 6.5-4 ความสัมพันธ์ของแบบจำลองความน่าเชื่อถือทั้ง 3 รูปแบบกับ Trusted List** [G2] [G3] + +--- + +## 6.5.6 สรุปและการเชื่อมโยงกับบทถัดไป + +รายงานฉบับนี้สรุปสาระสำคัญของบทนี้ ดังนี้ + +1. Trusted List คือรายการเอนทิตีที่ สพธอ. ตรวจรับรองและลงลายมือชื่อกำกับ ผู้รับต้องตรวจลายมือชื่อก่อนใช้รายการดังกล่าว +2. ระบบ VC เลือกยืมแกนของ PKI เป็นหลัก เพราะต้องเพิกถอนสิ่งที่รั่วไหลได้ (G3) แล้วเสริมด้วยแนวคิดกระจายศูนย์เฉพาะที่จำเป็น (G5, G7) +3. ไม่ว่าใช้ Trust Model 1, 2 หรือ 3 ผู้เข้าร่วมต้องใช้ Trusted List ที่เกี่ยวข้องตามข้อกำหนดเดียวกัน ส่วนความแตกต่างอยู่ที่การผูกและตรวจสอบกุญแจ +4. Trust Model 3 เป็นแนวทางหลักในระยะแรก + +บทถัดไปต่อยอดจากบทนี้ ดังนี้ [§7](07-cross-ecosystem-interoperability.md) อธิบายการทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List [§8](08-implementation-guidelines.md) อธิบายการนำไปปฏิบัติด้วย OID4VCI [G5] และ OID4VP [G6] และ [§9](09-trust-model-3.md) อธิบายรายละเอียดของ Trust Model 3 + +--- + +{/* METADATA (agent-only — not rendered to readers) +## บรรณานุกรม (References) + +- [G1] คณะทำงาน VC, "ช่องโหว่ความปลอดภัย: PKI และคริปโทฯ ปิดอย่างไร เทียบกับระบบ VC (Model 1–3)" — [research/31-security-gaps-pki-crypto-vs-vc.md](../research/31-security-gaps-pki-crypto-vs-vc.md) +- [G2] คณะทำงาน VC, "Trust Architecture: 3 โมเดลตรวจสอบความน่าเชื่อถือ" — [research/32-trustlist-3models.md](../research/32-trustlist-3models.md) +- [G3] คณะทำงาน VC, "3 Trust Architecture Models — Fact-Based Analysis" — [research/33-trust-architecture-models-factbase-analysis.md](../research/33-trust-architecture-models-factbase-analysis.md) +- [G4] World Wide Web Consortium (W3C), "Decentralized Identifiers (DIDs) v1.0", 19 July 2022 — https://www.w3.org/TR/did-core/ +- [G5] OpenID Foundation, "OpenID for Verifiable Credential Issuance 1.0", Final — https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html +- [G6] OpenID Foundation, "OpenID for Verifiable Presentations 1.0", Final — https://openid.net/specs/openid-4-verifiable-presentations-1_0.html +- [G7] IETF, "SD-JWT-based Verifiable Credentials (SD-JWT VC)" — https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/ +- [G8] IETF, "Token Status List (`statuslist+jwt`)" — https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ +- [G9] European Telecommunications Standards Institute (ETSI), "ETSI TS 119 602 — Lists of Trusted Entities (LoTEs)" — https://www.etsi.org/deliver/etsi_ts/119600_119699/119602/ +- [G10] IETF, "RFC 5280 — Internet X.509 Public Key Infrastructure Certificate and CRL Profile" — https://www.rfc-editor.org/rfc/rfc5280 +- [G11] IETF, "RFC 6960 — X.509 Internet Public Key Infrastructure Online Certificate Status Protocol (OCSP)" — https://www.rfc-editor.org/rfc/rfc6960 +- [G12] NIST, "NIST IR 8202 — Blockchain Technology Overview", 2018 — https://csrc.nist.gov/pubs/ir/8202/final +- [G13] Thailand National Root Certification Authority (Thailand NRCA) — https://www.nrca.go.th/ +- [G14] NIST, "FIPS PUB 140-2 / 140-3 — Security Requirements for Cryptographic Modules" — https://csrc.nist.gov/pubs/fips/140-3/final +- [G15] NIST, "SP 800-57 Part 1 Rev.5 — Recommendation for Key Management (cryptoperiod §5.3.6)" — https://csrc.nist.gov/pubs/sp/800/57/pt1/r5/final + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 6 — Decentralized Identifiers Methodologies](06-did-methodologies.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 7 — การทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List ➡️](07-cross-ecosystem-interoperability.md) diff --git a/docs/thai-vc-arf/07-cross-ecosystem-interoperability.md b/docs/thai-vc-arf/07-cross-ecosystem-interoperability.md new file mode 100644 index 0000000..6e44ad6 --- /dev/null +++ b/docs/thai-vc-arf/07-cross-ecosystem-interoperability.md @@ -0,0 +1,196 @@ +--- +description: "7. การทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List — Thai VC ARF 2.0 DRAFT 0" +--- + +# 7. การทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 ส่วนปรับปรุงสำหรับ Thai VC ARF v2.0 (MASA-125) +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) · [ดัชนีเอกสาร ARF](README.md) +> **เวอร์ชัน:** 2.0-draft.2 +> **วันที่ปรับปรุง:** 4 สิงหาคม 2569 +> **ขอบเขต:** การทำงานร่วมกันระหว่าง Issuer, Wallet และ Verifier ภายในประเทศ รวมถึงเงื่อนไขการเชื่อมโยงกับ EUDI Wallet +> **คำสำคัญเชิงข้อกำหนด:** คำว่า **ต้อง (MUST)**, **ควร (SHOULD)** และ **อาจ (MAY)** ให้ตีความตาม RFC 2119/RFC 8174 [R1] +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) · [08-implementation-guidelines.md](08-implementation-guidelines.md) · [09-trust-model-3.md](09-trust-model-3.md) +*/} + +--- + +## 7.0 ภาพรวมการทำงานร่วมกันข้ามระบบนิเวศ (ฐานจากเวอร์ชัน 1.1) + +การทำงานร่วมกัน (interoperability) ข้ามระบบนิเวศ (ecosystem) ช่วยให้ผู้ถือเอกสาร (holder) ขอและใช้เอกสารรับรองดิจิทัลจากระบบนิเวศใดก็ได้ตามข้อมูลที่ต้องการ ตัวอย่างตามรูปที่ 27 ประกอบด้วยระบบนิเวศ 3 ระบบ + +![การทำงานร่วมกันระหว่างระบบนิเวศ](images/p49-0.png) + +**รูปที่ 27 การทำงานร่วมกัน (interoperability) ระหว่างระบบนิเวศ** + +จากรูปที่ 27 กระบวนการทำงานร่วมกันข้ามระบบนิเวศมีขั้นตอน ดังนี้ + +1. ผู้ออกเอกสาร (issuer) ในแต่ละระบบนิเวศต้องสร้าง DID Document ในรูปแบบ Legal Person แล้วนำไปลงทะเบียน (register) ในระบบทะเบียนเอกสารรับรอง (registry) +2. ผู้ถือเอกสารร้องขอข้อมูลจากระบบนิเวศใดก็ได้ตามข้อมูลที่ต้องการ โดย VC ที่ได้รับจากผู้ออกเอกสารของแต่ละระบบนิเวศจะถูกจัดเก็บไว้ในกระเป๋าเอกสารดิจิทัลของผู้ถือเอกสาร +3. ผู้ถือเอกสารส่งข้อมูลให้ผู้ตรวจสอบเอกสาร (verifier) โดยต้องจัดทำข้อมูลให้อยู่ในรูปแบบ VP ก่อนเสมอ +4. เมื่อผู้ตรวจสอบเอกสารต้องการตรวจสอบความถูกต้องของข้อมูลที่ได้รับ ต้องมี DID Document ของผู้ออก VC ก่อน จึงเรียกดู DID Document ของผู้ออกเอกสารที่ลงทะเบียนไว้ผ่าน DIF Universal Resolver +5. DIF Universal Resolver ส่งกลับ (return) DID Document ของผู้ออกเอกสารที่ลงทะเบียนไว้ในระบบทะเบียนให้แก่ผู้ตรวจสอบเอกสาร +6. ผู้ตรวจสอบเอกสารนำ DID Document ที่ได้ไปตรวจสอบความถูกต้องของ VC ต่อไป + +> **ส่วนขยายเวอร์ชัน 2.0:** หัวข้อ 7.1–7.10 ต่อไปนี้เพิ่มชั้น Trusted List เข้ากับภาพรวมข้างต้น เพื่อกำหนดว่าเอนทิตีใดได้รับการรับรองให้ทำหน้าที่ใด มีสถานะใด และมีขอบเขตเพียงใด โดยใช้ร่วมกับการ resolve DID ตามข้างต้น + +--- + +## 7.1 วัตถุประสงค์ + +หัวข้อนี้กำหนดผลของ Trusted List ต่อการทำงานร่วมกัน (interoperability) ระหว่างระบบนิเวศ โดยต่อยอดจากแนวทางเดิมที่ Verifier ต้อง resolve DID Document ของ Issuer เพื่อใช้ตรวจลายมือชื่อ VC การมี Trusted List เพิ่มชั้นข้อมูลเชิงธรรมาภิบาลว่าเอนทิตีใดได้รับการรับรองให้ปฏิบัติหน้าที่ใด มีสถานะใด และมีขอบเขตการออกหรือขอข้อมูลเพียงใด + +**ข้อเท็จจริง:** OID4VCI 1.0 Final กำหนด protocol สำหรับการออก VC และ OID4VP 1.0 Final กำหนด protocol สำหรับการร้องขอและนำเสนอ Credential แต่มาตรฐานทั้งสองมิได้ทำให้เอนทิตีต่างระบบนิเวศเชื่อถือกันโดยอัตโนมัติ การตัดสินใจด้าน trust ยังคงขึ้นกับ trust framework, policy และ profile ของแต่ละระบบนิเวศ [R2] [R3] + +**ข้อวิเคราะห์:** Trusted List เป็นชั้นนโยบายด้าน trust ที่ใช้ร่วมกับ OID4VCI/OID4VP, DID resolution, protocol, schema, การตรวจลายมือชื่อ, สถานะ credential และกฎหมายที่ใช้บังคับ + +## 7.2 ผลต่อ ecosystem เมื่อมี Trusted List + +เมื่อ สพธอ. เผยแพร่ Trusted List ที่ลงลายมือชื่อดิจิทัลแล้ว ecosystem จะได้รับผลดังต่อไปนี้ + +1. **การค้นพบ trust anchor ร่วมกัน:** Wallet, Issuer และ Verifier สามารถใช้รายการเดียวกันเพื่อตรวจ role, status, validity, key reference และ policy metadata ของคู่กรณี +2. **Cross-issuer verification:** Verifier สามารถรับ VC จาก Issuer หลายรายได้ หาก Issuer นั้นอยู่ใน Trusted List มี role และสถานะที่ถูกต้อง และได้รับอนุญาตให้ออก credential type ที่นำเสนอ +3. **Cross-verifier presentation:** Wallet สามารถตรวจ Verifier ก่อนส่งข้อมูล และบังคับใช้ขอบเขต purpose/allowed claims ตามข้อมูลที่ Trusted List หรือเอกสารกำกับระบุ เพื่อลดความเสี่ยงจากผู้ตรวจสอบที่ไม่รู้จักหรือขอข้อมูลเกินจำเป็น +4. **ลดการทำ bilateral onboarding ซ้ำในประเทศ:** ผู้เข้าร่วมไม่จำเป็นต้องแลก public key หรือสร้าง allowlist แยกกับทุกคู่กรณี หากทุกฝ่ายยอมรับ governance ของ Trusted List เดียวกัน +5. **เพิกถอนและระงับสิทธิ์จากจุดกำกับร่วม:** การเปลี่ยน status ใน Trusted List ทำให้ทุกระบบที่ refresh ตาม policy สามารถบังคับผลได้อย่างสม่ำเสมอ อย่างไรก็ดี ผลไม่เกิดขึ้นทันทีในระบบที่ยังใช้ cache เก่า +6. **การตรวจสอบย้อนหลัง:** version, issued time และการลงลายมือชื่อของ Trusted List สนับสนุน audit trail ว่าผู้ตรวจใช้ trust state ใด ณ เวลาตัดสินใจ + +Trusted List **มิได้รับรองความถูกต้องของเนื้อหา VC ทุกฉบับ** และ **มิได้แทนที่** การตรวจลายมือชื่อ VC/VP, holder binding, validity, credential status, schema และ policy ของ use case + +## 7.3 ข้อกำหนดสำหรับการทำงานร่วมกันภายในประเทศ + +ผู้เข้าร่วม ecosystem **ต้อง (MUST)** ปฏิบัติดังต่อไปนี้ + +1. ตรวจลายมือชื่อและความถูกต้องของ Trusted List ก่อนใช้ข้อมูลภายในรายการ +2. ตรวจชนิดบริการจาก `ServiceInformation.ServiceTypeIdentifier` ตรวจสถานะระดับองค์กรและบริการ ช่วงเวลาที่มีผล กุญแจหรือ DID จาก `ServiceDigitalIdentity` และขอบเขตสิทธิ์ตาม Thai profile ที่ประกาศใช้ [research/42](../research/42-trusted-list-lote-jwt-format.md) +3. resolve DID Document และตรวจลายมือชื่อของ VC, VP หรือ request object แยกจากการตรวจ Trusted List +4. ตรวจ credential status และ holder binding ตาม profile ของ credential +5. ปฏิเสธ transaction เมื่อ Trusted List หรือ role หมดอายุ ถูกระงับ หรือถูกเพิกถอน เว้นแต่ governance policy กำหนด exception ที่มีหลักฐานตรวจสอบย้อนหลัง +6. ใช้ OID4VCI 1.0 Final สำหรับ issuance และ OID4VP 1.0 Final สำหรับ presentation ตาม national profile [R2] [R3] + +ผู้เข้าร่วม **ควร (SHOULD)** รองรับ Issuer และ Verifier มากกว่าหนึ่งรายโดยไม่ผูกกับ vendor เดียว และ **ควร (SHOULD)** แยกการตัดสินใจ 4 ชั้นให้ชัดเจน ได้แก่ protocol conformance, cryptographic validity, trust authorization และ business acceptance + +ผู้เข้าร่วม **อาจ (MAY)** ใช้ Universal Resolver เพื่อรองรับ DID Method หลายชนิด แต่ผลจาก Resolver เพียงอย่างเดียวไม่ถือเป็นหลักฐานว่าเอนทิตีได้รับอนุญาตภายใต้ Trusted List + +## 7.4 ลำดับการตรวจสอบข้าม Issuer และ Verifier + +```mermaid +sequenceDiagram + actor H as Holder + participant W as Wallet + participant V as Verifier + participant TL as ETDA Trusted List + participant R as DID Resolver + participant S as Credential Status + + V-->>W: OID4VP Authorization Request + W->>TL: GET Verifier Trusted List JWT ฉบับเต็มจาก CDN + TL-->>W: JWT แบบ LoTE พร้อม ETag + W->>W: ตรวจลายมือชื่อ บทบาท สถานะ และขอบเขตตาม Thai profile + W->>W: ตรวจ request signature + policy + W->>H: แสดง purpose/claims และขอความยินยอม + H-->>W: อนุมัติ + W-->>V: Verifiable Presentation + V->>TL: GET Issuer และ Wallet Provider Trusted List JWT ฉบับเต็ม + TL-->>V: JWT แบบ LoTE ทั้งสองรายการ + V->>V: ตรวจลายมือชื่อ บทบาท สถานะ และกุญแจตาม Thai profile + V->>R: Resolve Issuer DID Document + R-->>V: verification methods + V->>S: ตรวจ credential status + S-->>V: valid / suspended / revoked + V->>V: ตรวจ VC/VP signature, holder binding, schema และ business rules +``` + +ผล `trusted` จาก Trusted List หมายถึงเอนทิตีผ่านเงื่อนไขตาม governance ของรายการ ณ version และเวลาที่ตรวจเท่านั้น มิได้หมายถึง transaction นั้นผ่านทุกเงื่อนไขโดยอัตโนมัติ + +## 7.5 การทำงานร่วมกันกับ EUDI Wallet + +**ข้อเท็จจริง:** EUDI Architecture and Reference Framework (EUDI ARF) เป็นกรอบสถาปัตยกรรมและ reference framework ของระบบ EUDI Wallet รายงานฉบับนี้ใช้ **EUDI ARF 2.9.0** เป็น baseline ให้สอดคล้องกับบทอื่นของรายงาน ส่วน eIDAS Regulation (EU) 2024/1183 กำหนดกรอบกฎหมายของ European Digital Identity Framework [R5] + +**ฉบับใหม่กว่า — ผลกระทบอยู่ระหว่างการทบทวน:** มีการเผยแพร่ EUDI ARF **v3.0.0** เมื่อ 23 กรกฎาคม 2569 (2026-07-23) [R4] ซึ่งใหม่กว่า baseline 2.9.0 ผลกระทบของฉบับนี้อยู่ระหว่างการทบทวนใน QA Gate จึงยังไม่นำมาใช้เป็น baseline ของรายงาน ทั้งนี้ การพิจารณาว่า Trusted List ทำงานร่วมกับ EUDI ได้หรือไม่ ต้องอิงกับฉบับที่ปลายทางประกาศใช้จริง ณ เวลานั้น + +**องค์ประกอบใหม่ใน EUDI ARF v3.0.0 ที่อยู่ระหว่างการทบทวนผลกระทบต่อ Trusted List interoperability** [R4]: + +- **Trust-anchor retrieval** — กำหนดให้ Wallets, Relying Parties และ Issuers รองรับทั้ง **ETSI TS 119 612 Trusted Lists** และ **ETSI TS 119 602 Lists of Trusted Entities (LoTEs)** สำหรับดึงข้อมูล trust anchor ที่ปลายทาง +- **Relying Party Services** — แนวคิดใหม่ที่เพิ่มบทบาทผู้ให้บริการที่เกี่ยวข้องกับ Relying Party registration ตาม Commission Implementing Regulation (EU) 2025/848 +- **Functional Conformance Assessment Framework (FCAF)** — ชุดทดสอบกลางที่เผยแพร่และดูแลที่ `https://conformance.eudi.dev` ผู้ที่อ้าง conformance กับ EUDI ARF **ต้อง (MUST)** อ้างผลทดสอบจากแหล่งนี้เป็นหลักฐาน +- **Alignment with the amending Commission Implementing Regulations** — CIR 2024/2977 (PID/EAA), CIR 2024/2979 (integrity/core functionalities), CIR 2024/2980 (ecosystem notifications), CIR 2024/2982 (protocols/interfaces) และ CIR 2025/848 (Wallet-Relying Party registration) + +**ข้อเท็จจริง:** การใช้ OID4VCI/OID4VP 1.0 Final [R2] [R3], SD-JWT VC หรือ DID ที่เหมือนกันช่วยลดความต่างของ protocol และ credential format แต่ **ไม่ทำให้ Thai Trusted List ได้รับการยอมรับใน EUDI ecosystem โดยอัตโนมัติ** เนื่องจาก EUDI trust infrastructure อิงกับ ETSI TS 119 612/602 และ rulebook (PID, mDL, EAA) ที่กำหนดเงื่อนไขของตนเอง + +สำหรับการเชื่อมโยงข้ามพรมแดน ผู้ดำเนินการ **ต้อง (MUST)** จัดให้มีองค์ประกอบเพิ่มเติมอย่างน้อยดังนี้ + +1. **Trust agreement หรือ recognition arrangement:** ระบุคู่ภาคี ขอบเขต credential/use case ระดับ assurance หน้าที่รับผิด ความรับผิดทางกฎหมาย การตรวจประเมิน การเพิกถอน และการระงับข้อพิพาท กรณี EU อ้างอิงคือกระบวนการ third-country recognition ภายใต้ Regulation (EU) 2024/1183 [R5] +2. **Trust mapping:** แปลง role, status, assurance และ policy ของ Thai Trusted List ให้ตรงกับ trust model และ rulebook ที่ปลายทางยอมรับ (เช่น PID Rulebook, mDL Rulebook) [R4] +3. **Technical profile agreement:** ตกลง credential format, protocol profile, cryptographic suites, identifier/client authentication, status mechanism และ schema ให้สอดคล้องกับ Credential Format Profile ที่ EUDI ARF กำหนด +4. **Trusted-list distribution/bridge:** หากปลายทางกำหนดรูปแบบหรือ trust anchor ต่างกัน (เช่น ETSI TS 119 612 XML หรือ LoTEs) **ต้อง (MUST)** มี bridge หรือ dual publication ที่ปลายทางตรวจสอบได้ โดย **ต้อง (MUST NOT)** อ้างว่า bridge เท่ากับ legal recognition +5. **Conformance evidence:** ผลทดสอบ **ต้อง (MUST)** มาจาก Functional Conformance Assessment Framework ที่ EUDI เผยแพร่ที่ `https://conformance.eudi.dev` หรือ pilot ที่ตรวจสอบย้อนกลับได้ ก่อนประกาศรองรับ cross-border +6. **Operational readiness:** ผู้ดูแล **ต้อง (MUST)** จัดให้มี audit, monitoring, SLA และ 24/7 operations ตามระดับความเสี่ยงที่คู่ภาคีกำหนด + +**ข้อวิเคราะห์:** Thai Trusted List ตาม [§9](09-trust-model-3.md) อาจทำหน้าที่เป็นฐานข้อมูลต้นทางสำหรับ bridge ไปยัง EUDI trust infrastructure ได้ เพราะมีข้อมูลเอนทิตี role status และ key reference ที่มี governance กำกับ การวิเคราะห์รูปแบบ cross-border ที่เป็นไปได้ (Hybrid · Split TL · Bridge Service) อยู่ใน [`th/research/34-trustlist-domestic-and-crossborder.md`](../research/34-trustlist-domestic-and-crossborder.md) — เอกสารฉบับนี้ไม่คัดลอกผลวิเคราะห์ แต่ใช้เป็นข้อมูลอ้างอิงเชิงสถาปัตยกรรมเท่านั้น การนำไปใช้จริงขึ้นกับข้อตกลง technical profile และ conformance evidence ที่คู่ภาคียอมรับ + +**ข้อจำกัด:** หัวข้อนี้กำหนดสถาปัตยกรรม โดยยังไม่มีผลทดสอบ interoperability จริง จึง **ห้าม (MUST NOT)** ใช้ข้อความว่า "รองรับ EUDI" หรือ "ได้รับการรับรองจาก EU" ในเอกสาร UI หรือข้อเสนอใด ๆ จนกว่าจะมีหลักฐานตามข้อ 1–6 ครบถ้วน + +## 7.6 สิ่งที่ Trusted List ทำได้และทำไม่ได้ + +| ประเด็น | Trusted List ทำได้ | Trusted List เพียงอย่างเดียวทำไม่ได้ | +|---|---|---| +| ความเชื่อถือข้าม Issuer | ให้รายการ Issuer ที่ได้รับอนุญาตและสถานะล่าสุดตามรอบ refresh | รับรองว่า VC ทุกฉบับมีข้อมูลถูกต้อง | +| ความเชื่อถือ Verifier | ให้ Wallet ตรวจ identity/role/scope ก่อนเปิดเผยข้อมูล | รับประกันว่า Verifier จะปฏิบัติตาม purpose หลังรับข้อมูล | +| Protocol interoperability | ระบุ protocol/profile ที่รองรับ | ทำให้ implementation ที่ไม่ conform ทำงานร่วมกันได้ | +| DID interoperability | ให้ key reference และ policy สำหรับ DID | ทำให้ทุก DID Method resolve ได้โดยไม่มี driver/resolver | +| Revocation | ระงับหรือเพิกถอนสิทธิ์ระดับ entity/role | แทน credential-level status หรือทำให้ cache เก่ารู้ผลทันที | +| EUDI interoperability | เป็นข้อมูลต้นทางสำหรับ trust mapping/bridge | ก่อให้เกิด EU recognition หรือ cross-border trust โดยอัตโนมัติ | + +## 7.7 ข้อจำกัดและการจัดการความเสี่ยง + +1. **Cache freshness:** Client **ต้อง (MUST)** ตรวจ `LoTE.ListAndSchemeInformation.NextUpdate` เมื่อเกินกำหนดหรือหมดอายุ **ต้อง (MUST)** ไม่ถือว่าสถานะเดิมยัง active โดยปริยาย [research/42](../research/42-trusted-list-lote-jwt-format.md) +2. **Governance concentration:** การรวม trust decision ไว้ที่ Trusted List ทำให้ signing key และกระบวนการอนุมัติเป็นจุดสำคัญ ผู้ดูแล **ต้อง (MUST)** ใช้ key protection, dual control, audit log และ incident response +3. **Semantic mismatch:** ชื่อ credential เหมือนกันอาจมี schema หรือ assurance ต่างกัน ผู้เข้าร่วม **ต้อง (MUST)** ตรวจ rulebook/schema version ไม่ใช้ชื่อประเภทเพียงอย่างเดียว +4. **Privacy:** รายการสาธารณะ **ต้อง (MUST)** ไม่บรรจุข้อมูลส่วนบุคคลของ Holder และ **ควร (SHOULD)** บรรจุเฉพาะข้อมูลของนิติบุคคล/บริการที่จำเป็นต่อการตัดสินใจ trust +5. **Cross-border overstatement:** เอกสารหรือ UI **ต้อง (MUST NOT)** ระบุว่า “รองรับ EUDI” หรือ “ได้รับการรับรองจาก EU” จนกว่าจะมี trust agreement, technical profile และหลักฐาน conformance ที่เกี่ยวข้อง + +## 7.8 Evidence matrix + +| ID | ข้ออ้าง/ข้อกำหนด | หลักฐานปฐมภูมิ | สถานะตรวจสอบ | ข้อจำกัด | +|---|---|---|---|---| +| E-01 | OID4VCI กำหนด API สำหรับ issuance | OID4VCI 1.0 Final, Abstract และ metadata/security sections [R2] | ตรวจจาก HTML ต้นฉบับเมื่อ 4 ส.ค. 2569 | ไม่กำหนด Thai governance หรือ cross-border recognition | +| E-02 | OID4VP กำหนด protocol สำหรับ requesting/presenting Credentials และให้ trust framework/policy มีผลต่อการใช้ข้อมูล Verifier | OID4VP 1.0 Final, Abstract และ transaction data/Verifier information text [R3] | ตรวจจาก HTML ต้นฉบับเมื่อ 4 ส.ค. 2569 | ไม่สร้าง trust ระหว่าง ecosystem อัตโนมัติ | +| E-03 | EUDI ARF เป็น reference framework ที่มี release version ชัดเจน และ v3.0.0 เพิ่ม Trust-anchor retrieval สำหรับ Trusted Lists / LoTEs, Relying Party Services, และ Functional Conformance Assessment Framework (FCAF) | EUDI ARF GitHub Releases, release v3.0.0 (`published_at` = 2026-07-23T19:10:17Z) และ release body ระบุองค์ประกอบทั้งสาม [R4] | ตรวจ GitHub Releases API 4 ส.ค. 2569 เวลา 12:xx น. ICT — release JSON เก็บไว้ในแหล่งตรวจของ run นี้ | release ใหม่กว่า baseline 2.9.0 ของรายงาน ผลกระทบอยู่ระหว่างการทบทวน ต้องทำ version impact review ใน QA Gate ก่อนปรับ baseline | +| E-04 | eIDAS 2.0 เป็นกรอบกฎหมาย European Digital Identity Framework | Regulation (EU) 2024/1183, OJ L, 30.4.2024 [R5] | อ้างอิง primary legal act; endpoint EUR-Lex ตอบ 202/no body ในรอบตรวจนี้ | ไม่ตีความว่า third country ได้ recognition อัตโนมัติ | +| E-05 | DID resolution ให้ verification material แต่ไม่ให้ governance authorization | W3C DID Core 1.0, DID Resolution/DID Document [R6] | ตรวจตาม W3C Recommendation | ต้องใช้ร่วมกับ Trusted List และ policy | +| E-06 | คำ MUST/SHOULD/MAY ใช้ในความหมาย normative | RFC 2119 และ RFC 8174 [R1] | ตรวจตาม RFC published | คำไทยในเอกสารใส่คำอังกฤษกำกับเพื่อลดความกำกวม | + +## 7.9 สมมติฐานและช่องว่างที่ต้องติดตาม + +**สมมติฐาน:** Thai VC ARF v2.0 ใช้ ETDA-signed Trusted List เป็น national trust anchor สำหรับ domestic ecosystem และผู้เข้าร่วมยอมรับ governance policy ฉบับเดียวกัน + +**ช่องว่างที่ต้องติดตาม:** + +- รายงานฉบับนี้ใช้ EUDI ARF **2.9.0** เป็น baseline ของทั้งเล่ม ส่วน release **v3.0.0** (23 กรกฎาคม 2569 / 2026-07-23) [R4] เป็นฉบับใหม่กว่า ซึ่งรวมองค์ประกอบใหม่ (Trust-anchor retrieval, Relying Party Services, FCAF) ที่อาจกระทบ requirements ด้าน trust list และ conformance ผลกระทบของ v3.0.0 อยู่ระหว่างการทบทวน โดย QA Gate **ควร (SHOULD)** ตรวจ breaking changes ก่อนปรับ baseline ในฉบับถัดไป +- ยังไม่มี trust agreement/recognition arrangement ระหว่าง Thai ecosystem กับ EU Member State ที่ระบุในขอบเขตงานนี้ จึง **ต้อง (MUST NOT)** อ้างว่าเปิดใช้งาน cross-border ได้แล้ว กรณี EU ให้อิงกระบวนการ third-country recognition ภายใต้ Regulation (EU) 2024/1183 [R5] +- ยังไม่มีผล conformance suite (FCAF) หรือ cross-border pilot ที่แนบกับหัวข้อนี้ จึงใช้หัวข้อนี้เป็นข้อกำหนดเชิงสถาปัตยกรรม เมื่อมี conformance suite พร้อมใช้งาน **ควร (SHOULD)** เก็บผลทดสอบจากแหล่งกลาง (เช่น `https://conformance.eudi.dev`) ไว้ในภาคผนวกของฉบับถัดไป +- การวิเคราะห์รูปแบบ cross-border ที่เป็นไปได้ (Hybrid · Split TL · Bridge Service) อยู่ใน [`th/research/34-trustlist-domestic-and-crossborder.md`](../research/34-trustlist-domestic-and-crossborder.md) เอกสารฉบับนี้ใช้อ้างอิงผลวิเคราะห์ต้นทุนและความเสี่ยงจากงานวิจัยดังกล่าว + +{/* METADATA (agent-only — not rendered to readers) +## 7.10 แหล่งอ้างอิง + +- **[R1]** IETF, *Key words for use in RFCs to Indicate Requirement Levels*, RFC 2119, March 1997; and *Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words*, RFC 8174, May 2017. https://www.rfc-editor.org/rfc/rfc2119 และ https://www.rfc-editor.org/rfc/rfc8174 — เข้าถึง 4 สิงหาคม 2569 (เปลี่ยนเส้นทางเป็น `https://www.rfc-editor.org/info/rfc2119/` และ `info/rfc8174/`) +- **[R2]** OpenID Foundation, *OpenID for Verifiable Credential Issuance 1.0*, Final, 16 September 2025. https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html — เข้าถึงและตรวจ HTML ต้นฉบับ 4 สิงหาคม 2569; Abstract = "This specification defines an API for the issuance of Verifiable Credentials." +- **[R3]** OpenID Foundation, *OpenID for Verifiable Presentations 1.0*, Final, 9 July 2025. https://openid.net/specs/openid-4-verifiable-presentations-1_0.html — เข้าถึงและตรวจ HTML ต้นฉบับ 4 สิงหาคม 2569; Abstract = "This specification defines a protocol for requesting and presenting Credentials." +- **[R4]** European Commission / EUDI Wallet Consortium, *EUDI Wallet Architecture and Reference Framework*, release v3.0.0, `published_at` = 2026-07-23T19:10:17Z. https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/releases/tag/v3.0.0 — เข้าถึงผ่าน GitHub Releases API 4 สิงหาคม 2569; online: https://eudi.dev +- **[R5]** European Parliament and Council, *Regulation (EU) 2024/1183 amending Regulation (EU) No 910/2014 as regards establishing the European Digital Identity Framework*, OJ L, 30 April 2024. https://eur-lex.europa.eu/eli/reg/2024/1183/oj — เข้าถึง 4 สิงหาคม 2569 (endpoint EUR-Lex ตอบ HTTP 202 พร้อม body ว่างในรอบตรวจสำหรับ content-fetch) +- **[R6]** W3C, *Decentralized Identifiers (DIDs) v1.0*, W3C Recommendation, 19 July 2022. https://www.w3.org/TR/did-core/ — เข้าถึง 4 สิงหาคม 2569 + +**แหล่งอ้างอิงที่เกี่ยวข้องในระบบนิเวศ (ไม่ได้ใช้เป็นหลักฐานหลักของข้อกำหนดในหัวข้อนี้):** + +- Thai Trusted List cross-border scenarios analysis: [`th/research/34-trustlist-domestic-and-crossborder.md`](../research/34-trustlist-domestic-and-crossborder.md) +- EUDI ARF v2.8+ alignment background: [`th/research/30-eu-aligned-trust-model-adaptation.md`](../research/30-eu-aligned-trust-model-adaptation.md) +- PKI/VC security gap context: [`th/research/31-security-gaps-pki-crypto-vs-vc.md`](../research/31-security-gaps-pki-crypto-vs-vc.md) +- EUDI Conformance Assessment: https://conformance.eudi.dev — ยังไม่มีผลทดสอบระบบจริงที่แนบกับหัวข้อนี้ + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 6.5 — กลไกการสร้างความน่าเชื่อถือ (Trust-Building Mechanism)](06.5-trust-building-mechanism.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 8 — Implementation Guidelines (OID4VCI / OID4VP) ➡️](08-implementation-guidelines.md) diff --git a/docs/thai-vc-arf/08-implementation-guidelines.md b/docs/thai-vc-arf/08-implementation-guidelines.md new file mode 100644 index 0000000..ac6b71a --- /dev/null +++ b/docs/thai-vc-arf/08-implementation-guidelines.md @@ -0,0 +1,404 @@ +--- +description: "8. Implementation Guidelines — OID4VCI / OID4VP และแนวทางการนำไปใช้งาน — Thai VC ARF 2.0 DRAFT 0" +--- + +# 8. Implementation Guidelines — OID4VCI / OID4VP และแนวทางการนำไปใช้งาน + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** Reference — เอกสารแปลงจาก PDF ต้นฉบับของ สพธอ. (Thai VC ARF v1.1, มกราคม 2568) และปรับปรุงเพิ่มเติมจากเอกสารใน trust-guide (th/public-site/vcdoc/docs/trust-guide) ภายใต้ข้อกำหนด MASA-158 +> **เวอร์ชันเอกสาร:** v2.1.3 (2026-10-01) — กำหนดให้รองรับการลงลายมือชื่อด้วย EdDSA (Ed25519) และ ES256 (ECDSA P-256/SHA-256) ในข้อกำหนดหลักและตารางสรุป +> **เวอร์ชัน:** v2.1 (Implementation Guidelines) +> **วันที่:** 1 ตุลาคม 2569 +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) · trust-guide/03-issuance-flow/ · trust-guide/04-presentation-flow/ · [REF-MASA-158][REF-MASA-165] +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) · [02-definitions.md](02-definitions.md) · [06-did-methodologies.md](06-did-methodologies.md) · [07-cross-ecosystem-interoperability.md](07-cross-ecosystem-interoperability.md) · [08.1 — OID4VCI Full Flow](08.1-issuance-full-flow-detail.md) · [08.2 — OID4VP Full Flow](08.2-oid4vp-full-flow-detail.md) · [09-trust-model-3.md](09-trust-model-3.md) · [15-appendix.md](15-appendix.md) · [16-bibliography.md](16-bibliography.md) +*/} + +--- + +## 8.0. วัตถุประสงค์และขอบเขต + +ข้อแนะนำเบื้องต้นการพัฒนาระบบจะช่วยให้การให้บริการและการแลกเปลี่ยนเอกสารรับรองดิจิทัลมีความมั่นคงปลอดภัย และช่วยคุ้มครองข้อมูลส่วนบุคคลของเอนทิตีต่าง ๆ ที่เกี่ยวข้อง รวมถึงช่วยจำกัดความซับซ้อนของแนวทางการพัฒนาเชิงเทคนิค (technical solution) เพื่อให้กระเป๋าเอกสารดิจิทัล (Document Wallet) และเอกสารรับรอง (Verifiable Credential — VC) สามารถทำงานร่วมกันได้ + +บทนี้ระบุข้อกำหนดเชิงเทคนิค (MUST / SHOULD / MAY) สำหรับการออก VC และการแสดง VP โดยใช้โปรโตคอลมาตรฐาน OID4VCI 1.0 และ OID4VP 1.0 ของ OpenID Foundation พร้อมแผนภาพผังขั้นตอนภาพรวม (Overview Flow) และอธิบายการทำงานร่วมกับรายชื่อผู้ที่ได้รับความเชื่อถือ (ETDA Trusted List) และการ resolve DID (DID Resolution) ในกรณีต่าง ๆ + +{/* METADATA (agent-only — not rendered to readers) +> **ฐานที่มาของบท:** เนื้อหาบทนี้ปรับปรุงจากฐานเวอร์ชัน 1.1 ของรายงาน Thai VC ARF [REF-ARF-V11-SRC] ร่วมกับแผนภาพฉบับสมบูรณ์จากเอกสาร trust-guide (th/public-site/vcdoc/docs/trust-guide) ภายใต้ข้อกำหนดงาน MASA-158 [REF-MASA-158] แผนภาพ Sequence ฉบับสมบูรณ์และรายละเอียดทางเทคนิคระดับ field-by-field ของทั้ง OID4VCI และ OID4VP อยู่ใน [08.1-issuance-full-flow-detail.md](08.1-issuance-full-flow-detail.md) และ [08.2-oid4vp-full-flow-detail.md](08.2-oid4vp-full-flow-detail.md) ตามลำดับ กลไกการตรวจสอบความน่าเชื่อถือ 4 จุดตรวจ (TC-1 ถึง TC-4) อธิบายด้วยข้อความเชิงบรรยายใน §8.4 +*/} + +{/* METADATA (agent-only — not rendered to readers) +> **ศัพท์เทคนิค:** คงคำภาษาอังกฤษตามมาตรฐาน (เช่น Verifiable Credential — VC, Verifiable Presentation — VP, Issuer, Verifier, Holder, Trusted List, Status List) เพื่อความชัดเจนและสอดคล้องกับเอกสารอ้างอิงสากล [REF-OID4VCI][REF-OID4VP][REF-SDJWT][REF-TSL][REF-DID-CORE] +*/} + +--- + +## 8.1. องค์ประกอบพื้นฐาน + +### 8.1.1. กรณีศึกษา + +รายงานฉบับนี้ใช้กรณีศึกษา การออกใบประมวลผลการศึกษา (Transcript) ของสถาบันการศึกษาในประเทศไทย เป็นตัวอย่างประกอบแผนภาพและคำอธิบาย เพื่อให้ผู้อ่านเห็นภาพการนำ OID4VCI/OID4VP ไปใช้งานจริง + +### 8.1.2. ข้อกำหนดทางเทคนิค + +ระบบที่พัฒนาตามแนวทางนี้ MUST ปฏิบัติตามข้อกำหนดต่อไปนี้: + +| หัวข้อ | ข้อกำหนด (RFC 2119) | แหล่งอ้างอิง | +|--------|---------------------|---------------| +| ภาษาในการพัฒนา | MUST เป็น High-Level Language | — | +| อัลกอริทึมลายมือชื่ออิเล็กทรอนิกส์ | MUST รองรับการสร้างและตรวจลายมือชื่อด้วย EdDSA (Ed25519) และ ES256 (ECDSA P-256/SHA-256) | [REF-EdDSA] [REF-ES256] | +| เค้าร่างเอกสารรับรอง | MUST รองรับ W3C Verifiable Credentials Data Model v1.1 สำหรับเอกสารเดิม และ MUST รองรับ IETF SD-JWT Verifiable Credential (`dc+sd-jwt`) ที่เพิ่มใน ARF ฉบับนี้; การออกเอกสารใหม่ SHOULD ใช้ `dc+sd-jwt` | [REF-W3C-VCDM] [REF-SDJWT] | +| โปรโตคอลออกเอกสารรับรอง | MUST ใช้ OpenID for Verifiable Credential Issuance (OID4VCI) 1.0 Final | [REF-OID4VCI] | +| โปรโตคอลแสดงเอกสาร | MUST ใช้ OpenID for Verifiable Presentations (OID4VP) 1.0 Final พร้อม Digital Credentials Query Language (DCQL) | [REF-OID4VP] | +| การยกเลิก/ระงับเอกสาร | MUST ใช้ IETF Token Status List (`statuslist+jwt`) เป็นกลไก revocation | [REF-TSL] | +| กระบวนการ Authorization Code | MUST ใช้ pre-authorized code flow ตาม OID4VCI §4.1 ยกเว้นกรณีผู้ถือ (Holder) ร้องขอ authorization แบบ explicit code | [REF-OID4VCI] | +| การ resolve DID (DID Resolution) | MUST รองรับ DIF Universal Resolver เป็นตัวกลางมาตรฐาน และ SHOULD รองรับ `did:web` และ `did:key` โดยตรงเพื่อลดการพึ่งพาส่วนกลาง | [REF-UR][REF-DID-CORE] | +| ลายมือชื่อในคำขอ (Request Object) | MUST ใช้ client identifier scheme ที่ OID4VP §5.9 ระบุ เช่น `x509_san_dns:`, `x509_hash:`, `decentralized_identifier:`, `verifier_attestation`, `openid_federation` | [REF-OID4VP] | + +{/* METADATA (agent-only — not rendered to readers) +> **ฐานจากเวอร์ชัน 1.1 และการปรับปรุงในเวอร์ชัน 2.0:** เวอร์ชัน 1.1 ใช้ W3C VCDM v1.1 และ JSON-LD สำหรับเอกสารเดิม รวมทั้ง OID4VC draft 13 กับ OID4VP draft 20 เวอร์ชัน 2.0 ยังคงรองรับ W3C VCDM v1.1 และเพิ่ม SD-JWT VC (`dc+sd-jwt`) สำหรับเอกสารใหม่ พร้อมใช้ OID4VCI 1.0 Final และ OID4VP 1.0 Final (พร้อม DCQL) โดยกำหนด EdDSA (Ed25519) เป็นอัลกอริทึมที่แนะนำ และกำหนดให้รองรับ ES256 เพิ่มเติมตามโปรไฟล์กุญแจในบทที่ 11; pre-authorized code flow ยังคงเป็นแนวทางเดิม รายละเอียดการเปลี่ยนแปลงดูได้ที่ [ภาคผนวก ข — บันทึกการเปลี่ยนแปลง v1.1→v2.0](15-appendix.md) +*/} + +{/* METADATA (agent-only — not rendered to readers) +> **หมายเหตุการเปลี่ยนชื่อ WTE → WUA:** เอกสารนี้ใช้คำว่า **WUA (Wallet Unit Attestation)** ตาม EUDI ARF 2.9.0 [REF-ARF] แทนคำว่า WTE เดิม ข้อกำหนดใน OID4VCI คงตามกระบวนการออกเอกสารที่ระบุใน §8.2 และภาคผนวก 8.1 ส่วน **ใน OID4VP** Thai profile กำหนดให้ Wallet รัฐบาลส่ง WIA และ WIA-PoP ครบคู่ ส่วน Wallet เอกชนเลือกส่งครบคู่หรือไม่ส่งทั้งคู่ได้ หากส่งไม่ครบคู่หรือหลักฐานตรวจไม่ผ่าน ให้ปฏิเสธ VP; Verifier ต้องจำแนกประเภท Wallet จากข้อมูลที่เชื่อถือได้ และปฏิเสธ VP หากจำแนกไม่ได้ [กลไก §8.4.2 และภาคผนวก 8.2](08.2-oid4vp-full-flow-detail.md) บทนี้กล่าวถึง WUA เฉพาะที่ปรากฏในกระแสงาน OID4VCI/OID4VP ส่วนแนวคิดและนิยามดู [บทที่ 10 — Wallet Unit Attestation (WUA)](10-wallet-unit-attestation.md) +*/} + +### 8.1.3. บทบาทและผู้มีส่วนได้ส่วนเสีย + +การแลกเปลี่ยน VC และ VP ประกอบด้วย 4 บทบาทหลัก MUST เข้าใจร่วมกันดังนี้: + +| บทบาท | ความหมาย | ตัวอย่างในบริบทไทย | +|------|---------|----------------| +| **Issuer** | ผู้ออก VC เป็นผู้รับผิดชอบในการลงนามและเผยแพร่ Status List | สถาบันการศึกษา, กรมการปกครอง (DOPA), กรมการขนส่งทางบก (DLT) | +| **Holder (ผู้ถือ)** | ผู้ครอบครอง VC ในกระเป๋าเอกสารดิจิทัล (Document Wallet) และเป็นผู้สร้าง VP เมื่อต้องแสดง | ผู้ใช้งาน Document Wallet | +| **Verifier** | ผู้ขอและตรวจสอบ VP จากผู้ถือ ตามวัตถุประสงค์ที่ลงทะเบียนไว้กับ สพธอ. | สถานพยาบาล, สถาบันการเงิน, หน่วยงานภาครัฐ | +| **Wallet Provider (WP)** | ผู้พัฒนาและดูแลกระเป๋าเอกสารดิจิทัล ออก Wallet Unit Attestation (WUA) เพื่อรับรองแอป | สำนักงานพัฒนารัฐบาลดิจิทัล (DGA), ผู้ให้บริการกระเป๋าเอกสารเอกชน | + +นอกจาก 4 บทบาทหลักแล้ว ยังมี **ETDA Trusted List (TL)** ทำหน้าที่: + +1. **การเข้าถึงไฟล์ (file access)** — Wallet, Issuer และ Verifier ดึง Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จาก CDN โดยตรงเมื่อเกิดการใช้งาน ไม่ต้องใช้ API key +2. **ควบคุมความเชื่อถือ (trust anchor)** — Trusted List ที่ดึงจาก CDN เป็น JWT ที่ สพธอ. ลงลายมือชื่อ ผู้ตรวจต้องตรวจลายมือชื่อทุกครั้งที่ได้รับไฟล์ใหม่จาก CDN [REF-TRUSTLIST-CDN] + +> รายละเอียดเกี่ยวกับ Trusted List schema และการดึงไฟล์จาก CDN ดูได้ที่ [§9 Trust Model 3](09-trust-model-3.md) และ [ข้อกำหนดการเผยแพร่ Trust List](12.1-trust-list-publication-profile.md) [REF-TRUSTLIST-CDN] + +### 8.1.4. รหัสสีในแผนภาพ + +แผนภาพ Mermaid ในบทนี้ใช้สีเพื่อแยกประเภทของขั้นตอนอย่างชัดเจน: + +| สี | ความหมาย | ผู้รับผิดชอบ | +|----|---------|------------| +| **เขียว** | การดาวน์โหลด Trusted List (เกิดก่อนผู้ใช้เริ่ม) | สพธอ. (ETDA) | +| **น้ำเงิน** | ขั้นตอนตามมาตรฐาน OID4VCI/OID4VP สากล | OpenID Foundation | +| **ส้ม** | จุดตรวจสอบความน่าเชื่อถือ (TC-1 ถึง TC-4) | Trust Framework (สพธอ.) | + +--- + +## 8.2. การออกเอกสารรับรองดิจิทัล (OID4VCI) — Flow การรับเอกสารรับรอง + +{/* METADATA (agent-only — not rendered to readers) +> **แหล่งข้อมูล:** trust-guide/03-issuance-flow/ · [08.1-issuance-full-flow-detail.md](08.1-issuance-full-flow-detail.md) · [REF-OID4VCI] +*/} + +### 8.2.1. สถานการณ์ + +ผู้ใช้ต้องการรับเอกสารรับรองดิจิทัล (เช่น ใบประมวลผลการศึกษา Transcript) ไว้ในกระเป๋าเอกสารดิจิทัล โดยไม่ต้องเดินทางไปสถาบันการศึกษา + +### 8.2.2. ผังขั้นตอนภาพรวม — Flow การรับเอกสารรับรอง (OID4VCI) + +แผนภาพนี้แสดงภาพรวมทั้งกระบวนการ แบ่งเป็น 5 ช่วงหลัก (1) เตรียมความพร้อม (2) การรับคำเชิญและการค้นหาข้อมูลผู้ออกบัตร (3) การตรวจสอบความน่าเชื่อถือก่อนยืนยันตัวตน (4) การขอ Token และ Credential และ (5) การตรวจสอบก่อนบันทึก จุดตรวจความน่าเชื่อถือของระบบ Trust Framework (TC-1 ถึง TC-4) ที่ปรากฏใน Flow นี้มี 2 จุด คือ **ช่วงที่ 3 ตรวจสอบ Issuer ก่อนยืนยันตัวตน = TC-3 (รอบแรก)** และ **ช่วงที่ 5 ตรวจสอบ VC และสถานะก่อนบันทึก = TC-3 (รอบที่สอง) + TC-4** (กลไกแต่ละจุดอธิบายใน §8.4) + +```mermaid +flowchart LR + classDef setup fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20 + classDef std fill:#E3F2FD,stroke:#1565C0,color:#0D47A1 + classDef tc fill:#FFF3E0,stroke:#E65100,color:#BF360C + + P1(["① เตรียมความพร้อม
ดาวน์โหลด Trusted List + WUA"]):::setup + P2(["② รับคำเชิญ + ค้นหาข้อมูล
Credential Offer + Discovery"]):::std + P3(["③ ตรวจ Issuer ก่อนยืนยันตัวตน
TC-3 (รอบแรก)"]):::tc + P4(["④ Token + Credential
ออก VC"]):::std + P5(["⑤ ตรวจ VC + สถานะก่อนบันทึก
TC-3 (รอบที่สอง) + TC-4"]):::tc + + P1 --> P2 --> P3 --> P4 --> P5 +``` + +> **ตารางจับคู่ช่วง ↔ TC (OID4VCI):** + +| ช่วง | ชื่อช่วง | จุดตรวจ TC ที่ทำงาน | ผู้ตรวจ | ผู้ถูกตรวจ | อธิบายกลไก | +|:----:|---------|:-----------------:|--------|----------|------------| +| ① | เตรียมความพร้อม | — | — | — | ดาวน์โหลด Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จาก CDN แบบ lazy และตรวจลายมือชื่อ JWT — ยังไม่มีปฏิสัมพันธ์กับคู่กรณี จึงไม่มี TC | +| ② | รับคำเชิญ + ค้นหาข้อมูล | — | — | — | Credential Offer, Issuer Discovery, /.well-known/openid-credential-issuer เป็นมาตรฐาน OID4VCI ล้วน ยังไม่มีการตรวจสอบความน่าเชื่อถือของ Issuer ในระดับ Trust Framework | +| ③ | ตรวจ Issuer ก่อนยืนยันตัวตน | **TC-3 (รอบแรก)** | Wallet (ฝั่งผู้ถือ) | Issuer | Wallet ตรวจ Credential Issuer Metadata เทียบกับบริการ Issuer ใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` ก่อนขอ OTP/ยืนยันตัวตน (รายละเอียด §8.4.3 และ [§12.1](12.1-trust-list-publication-profile.md)) | +| ④ | Token + Credential | **TC-2 เมื่อ Thai profile กำหนด** | Wallet → Authorization Server (ก่อนออก Access Token) | Wallet | Token Endpoint และ Credential Endpoint เป็น OID4VCI มาตรฐาน; หาก profile ใช้ Client Attestation ให้ตรวจที่ PAR หรือ Token Request ตาม Appendix E. Credential Request ใช้ `proofs.jwt`; ตรวจ Issuer/VC ในช่วง ⑤ | +| ⑤ | ตรวจ VC + สถานะก่อนบันทึก | **TC-3 (รอบที่สอง) + TC-4** | Wallet (ฝั่งผู้ถือ) | Issuer + Status List | (1) TC-3 รอบที่สอง: ตรวจ SD-JWT VC ฉบับสมบูรณ์ — claims, iss, jti, ลายมือชื่อด้วย PK_iss, validFrom/Until, disclosures (รายละเอียด §8.4.3) (2) TC-4: ดึงและตรวจ Status List JWT เพื่อยืนยันว่า VC ยังไม่ถูกยกเลิก/ระงับ (รายละเอียด §8.4.4) | + +> **หมายเหตุ (TC-2):** ใน OID4VCI Flow นี้ **TC-2 (ตรวจ Wallet) ทำงานฝั่ง Issuer** ไม่ได้แสดงเป็น node แยกในภาพรวมนี้ เนื่องจากผู้ตรวจคือ Issuer ซึ่งเป็นฝั่งตรงข้ามกับผู้ถือที่ผู้อ่านมองตามภาพ flow กลไก TC-2 ใน OID4VCI อธิบายใน §8.4.2 และรายละเอียดเชิงลำดับขั้นอยู่ใน [ภาคผนวก 8.1](08.1-issuance-full-flow-detail.md) + +> **หมายเหตุ:** แผนภาพนี้เป็นผังขั้นตอนภาพรวมฉบับย่อ (oversimplified overview) เพื่อให้ผู้อ่านเห็นลำดับกระบวนการโดยรวม หากต้องการทำความเข้าใจขั้นตอน คำขอ-คำตอบ และการตรวจสอบความน่าเชื่อถือในรายละเอียด ให้ศึกษาจาก **[ภาคผนวก 8.1 — OID4VCI Full Flow ทางเทคนิคโดยละเอียด](08.1-issuance-full-flow-detail.md)** ซึ่งแสดง TC-2 เมื่อ profile กำหนด, Client Attestation ในขั้น Token, `proofs.jwt` ใน Credential Request และ DPoP แบบมีเงื่อนไข + +### 8.2.3. แผนภาพ Sequence ฉบับสมบูรณ์และนโยบาย DPoP + +กระบวนการออกเอกสารรับรองดิจิทัลตามมาตรฐาน OID4VCI ฉบับสมบูรณ์ประกอบด้วยทุกขั้นตอน ตั้งแต่การเตรียมระบบ (SETUP) การออกคำเชิญ (STEP 1) การค้นหาข้อมูลผู้ออกเอกสาร (STEP 2) การตรวจสอบความน่าเชื่อถือ (STEP 2.5) การยืนยันตัวตน (STEP 3) การขอ Token และ Credential (STEP 4–5) การออกเอกสาร (STEP 6) และการแจ้งเตือน (STEP 7) รวมถึงการตรวจสอบความสดใหม่ของ Trusted List (Freshness Check) การใช้ DPoP เมื่อ Thai profile กำหนด และการออกเอกสารแบบทันที (Immediate Issuance) หรือแบบรอ (Deferred Issuance) + +รายละเอียดของแผนภาพ Sequence ฉบับสมบูรณ์ พร้อมคำอธิบายระดับ field-by-field และนโยบาย DPoP แยกตามระดับ IAL/AAL อยู่ใน **[ภาคผนวกของบทที่ 8 — OID4VCI Full Flow ทางเทคนิคโดยละเอียด](08.1-issuance-full-flow-detail.md)** + +--- + +## 8.3. การแสดงและตรวจสอบเอกสารรับรองดิจิทัล (OID4VP) — Flow การแสดงเอกสารรับรอง + +{/* METADATA (agent-only — not rendered to readers) +> **แหล่งข้อมูล:** trust-guide/04-presentation-flow/ · [08.2-oid4vp-full-flow-detail.md](08.2-oid4vp-full-flow-detail.md) · [REF-OID4VP] +*/} + +### 8.3.1. สถานการณ์ + +ผู้ใช้ต้องการแสดงเอกสารรับรองดิจิทัล เช่น ใบประมวลผลการศึกษา Transcript ให้หน่วยงานที่ขอตรวจสอบ (เช่น สถานศึกษาปลายทาง หรือนายจ้าง) โดยเปิดเผยเฉพาะข้อมูลที่จำเป็น (Selective Disclosure) + +### 8.3.2. ผังขั้นตอนภาพรวม — Flow การแสดงเอกสารรับรอง (OID4VP) + +แผนภาพนี้แสดงภาพรวมทั้งกระบวนการ แบ่งเป็น 6 ช่วงหลัก (1) เตรียมความพร้อม (2) Verifier ขอข้อมูล (3) Wallet ตรวจ Verifier (4) ยืนยันและสร้าง VP (5) ส่ง VP และตรวจ Wallet ตามเงื่อนไข TC-2 และ (6) Verifier ตรวจ VC และสถานะ สำหรับ OID4VP จุดตรวจ TC-1, TC-3 และ TC-4 ทำงานตามปกติ ส่วน TC-2 กำหนดให้ Wallet รัฐบาลส่ง WIA/WIA-PoP ครบคู่; Wallet เอกชนเลือกส่งครบคู่หรือไม่ส่งทั้งคู่ได้ โดย Verifier ต้องจำแนกประเภท Wallet จากข้อมูลที่เชื่อถือได้ หากตรวจประเภทไม่ได้ให้ปฏิเสธ VP (กลไกแต่ละจุดอธิบายใน §8.4) + +```mermaid +flowchart LR + classDef setup fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20 + classDef std fill:#E3F2FD,stroke:#1565C0,color:#0D47A1 + classDef tc fill:#FFF3E0,stroke:#E65100,color:#BF360C + + P1(["① เตรียมความพร้อม
ดาวน์โหลด Trusted List"]):::setup + P2(["② Verifier ขอข้อมูล
Auth Request + QR"]):::std + P3(["③ Wallet ตรวจ Verifier
TC-1"]):::tc + P4(["④ ยืนยัน + สร้าง VP
Selective Disclosure"]):::std + P5(["⑤ ส่ง VP + ตรวจ Wallet ตามประเภท
TC-2 ตามข้อกำหนดของประเภท Wallet"]):::tc + P6(["⑥ Verifier ตรวจ VC + สถานะ
TC-3 + TC-4"]):::tc + + P1 --> P2 --> P3 --> P4 --> P5 --> P6 +``` + +> **ตารางจับคู่ช่วง ↔ TC (OID4VP):** + +| ช่วง | ชื่อช่วง | จุดตรวจ TC ที่ทำงาน | ผู้ตรวจ | ผู้ถูกตรวจ | อธิบายกลไก | +|:----:|---------|:-----------------:|--------|----------|------------| +| ① | เตรียมความพร้อม | — | — | — | Wallet/Verifier ดาวน์โหลด Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จาก CDN แบบ lazy และตรวจลายมือชื่อ JWT — ยังไม่มีปฏิสัมพันธ์ระหว่างคู่กรณี จึงไม่มี TC | +| ② | Verifier ขอข้อมูล | — | — | — | Verifier สร้าง Authorization Request, Request Object (JWS), แสดง QR/ deep link ตาม OID4VP มาตรฐาน — การยืนยันตัวตนของ Verifier จะเกิดในช่วง ③ | +| ③ | Wallet ตรวจ Verifier | **TC-1** | Wallet (ฝั่งผู้ถือ) | Verifier | ก่อนยินยอมให้ข้อมูล Wallet ตรวจบริการ Verifier ใน Trusted List ลายมือชื่อ Request Object และขอบเขต claims กับวัตถุประสงค์ตาม Thai profile (รายละเอียด §8.4.1 และ [§12.1](12.1-trust-list-publication-profile.md)) | +| ④ | ยืนยัน + สร้าง VP | — | — | — | แสดง Consent UI ให้ผู้ใช้กดยินยอม, จับคู่ Disclosure, สร้าง VP ตาม DCQL เป็น OID4VP มาตรฐาน — ยังไม่มีการตรวจสอบข้ามฝั่งในระดับ Trust Framework | +| ⑤ | ส่ง VP + ตรวจ Wallet | **TC-2 ตามประเภท Wallet** | Verifier | Wallet (WIA/WIA-PoP) | Wallet รัฐบาล MUST ส่งหลักฐานครบคู่; Wallet เอกชน MAY ส่งครบคู่หรือไม่ส่งทั้งคู่ Verifier ต้องจำแนกประเภทจากข้อมูลที่เชื่อถือได้ หากตรวจประเภทไม่ได้ ให้ปฏิเสธ VP; หลักฐานไม่ครบหรือไม่ผ่านการตรวจให้ปฏิเสธ (รายละเอียด §8.4.2) | +| ⑥ | Verifier ตรวจ VC + สถานะ | **TC-3 + TC-4** | Verifier | Issuer + Status List | (1) TC-3: ตรวจ SD-JWT VC ใน `vp_token` — iss/jti, ดึง PK_iss ผ่าน DID Resolution, ตรวจลายมือชื่อ, validFrom/Until, disclosures ตรงกับ DCQL (รายละเอียด §8.4.3) (2) TC-4: ดึงและตรวจ Status List JWT เพื่อยืนยันว่า VC ยังไม่ถูกยกเลิก/ระงับ (รายละเอียด §8.4.4) | + +> **หมายเหตุ:** แผนภาพนี้เป็นผังขั้นตอนภาพรวมฉบับย่อ (oversimplified overview) เพื่อให้ผู้อ่านเห็นลำดับกระบวนการโดยรวม หากต้องการทำความเข้าใจขั้นตอน คำขอ-คำตอบ และการตรวจสอบความน่าเชื่อถือในรายละเอียด ให้ศึกษาจาก **[ภาคผนวก 8.2 — OID4VP Full Flow ทางเทคนิคโดยละเอียด](08.2-oid4vp-full-flow-detail.md)** ซึ่งประกอบด้วยแผนภาพ Sequence ฉบับสมบูรณ์ คำอธิบายระดับ field-by-field และสรุปจุดตรวจ L1–L7 + +### 8.3.3. แผนภาพ Sequence ฉบับสมบูรณ์ — OID4VP + +กระบวนการแสดงและตรวจสอบเอกสารสำแดงดิจิทัลตามมาตรฐาน OID4VP ฉบับสมบูรณ์ประกอบด้วยทุกขั้นตอน ตั้งแต่การเตรียมระบบ (SETUP) การสร้างคำขอ (Step 1) การโอนข้ามอุปกรณ์ (Step 2) การดึง Request Object (Step 2.5) การตรวจสอบผู้ตรวจสอบเอกสาร การจับคู่ข้อมูลในเครื่องและการขอความยินยอม (Step 3) การสร้างเอกสารสำแดงและ Proof of Possession (Step 4) การส่งคำตอบ (Step 5) และการตรวจสอบฝั่งผู้ตรวจสอบเอกสาร (Step 6) ซึ่งรวมถึงการตรวจสอบผู้ให้บริการกระเป๋า การผูกผู้ถือเอกสาร (KB-JWT) การตรวจสอบผู้ออกเอกสาร และการตรวจสอบสถานะเอกสาร + +รายละเอียดของแผนภาพ Sequence ฉบับสมบูรณ์ พร้อมคำอธิบายระดับ field-by-field และสรุปจุดตรวจ L1–L7 อยู่ใน **[ภาคผนวกของบทที่ 8 — OID4VP Full Flow ทางเทคนิคโดยละเอียด](08.2-oid4vp-full-flow-detail.md)** + +--- + +## 8.4. กลไกการตรวจสอบความน่าเชื่อถือ 4 จุดตรวจ (TC-1 ถึง TC-4) + +ทั้ง 2 กระบวนการ (OID4VCI และ OID4VP) ใช้ชุดจุดตรวจเดียวกัน ต่างกันที่ผู้ดำเนินการตรวจและผู้ถูกตรวจ บทนี้อธิบายกลไกด้วยข้อความเชิงบรรยายเพื่อให้ผู้อ่านเข้าใจการทำงานของแต่ละจุดตรวจ โดยไม่อาศัยแผนภาพลำดับการตัดสินใจ (decision flow) ฉบับย่อยซึ่งเคยก่อให้เกิดความสับสนในฉบับก่อนหน้า + +### 8.4.1. TC-1 — กระเป๋าเอกสารดิจิทัลตรวจสอบผู้ตรวจสอบเอกสาร (Wallet ตรวจ Verifier) + +จุดตรวจ TC-1 ปรากฏเฉพาะในกระบวนการ OID4VP และถือเป็นจุดควบคุมความปลอดภัยที่สำคัญที่สุดของฝั่งกระเป๋าเอกสารดิจิทัล เนื่องจากเป็นด่านสุดท้ายก่อนที่กระเป๋าจะส่งข้อมูลส่วนบุคคลของผู้ถือออกไปยังผู้ตรวจสอบเอกสาร + +กลไกของ TC-1 ดำเนินการในช่วงระหว่างที่กระเป๋าเอกสารดิจิทัลดึง Request Object จากผู้ตรวจสอบเอกสาร และก่อนที่จะแสดง Consent UI ให้ผู้ใช้อนุมัติ มีลำดับขั้นตอนหลัก ดังนี้ + +1. **แยกกุญแจสาธารณะของผู้ตรวจสอบเอกสาร (Public Key — PK_v)** จาก Request Object ที่ลงนามแล้ว +2. **ตรวจสอบลายมือชื่อ Trusted List** ด้วย ETDA public key เพื่อยืนยันว่าข้อมูล Trusted List ที่จัดเก็บไว้ในเครื่องยังน่าเชื่อถือ +3. **ค้นหาบริการ Verifier ใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]`** โดยตรวจ `ServiceInformation.ServiceTypeIdentifier` และกุญแจใน `ServiceDigitalIdentity` ต้องยืนยันสถานะระดับองค์กรและบริการเป็น `active` ตาม Thai profile ได้ มิฉะนั้นให้หยุดดำเนินการทันที [research/42](../research/42-trusted-list-lote-jwt-format.md) +4. **ตรวจสอบลายมือชื่อ Request Object ด้วย PK_v** พร้อมตรวจสอบ `aud`, `exp`/`iat` และค่า `nonce` +5. **ตรวจสอบขอบเขตข้อมูลที่ขอ** — `dcql_query.claims[].path` ต้องอยู่ภายในขอบเขต claims ที่ Thai profile ของบริการ Verifier ประกาศไว้ +6. **ตรวจสอบวัตถุประสงค์** — `purpose` ต้องอยู่ในขอบเขตวัตถุประสงค์ที่ Thai profile ของบริการ Verifier ประกาศไว้; หากไม่มีข้อมูลให้ปฏิเสธ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +เมื่อผ่านทุกขั้นตอน กระเป๋าเอกสารดิจิทัลจึงจะแสดง Consent UI ให้ผู้ใช้อนุมัติการส่งข้อมูล หากขั้นตอนใดไม่ผ่าน กระเป๋าเอกสารดิจิทัล MUST หยุดดำเนินการทันทีและไม่ส่งข้อมูลใด ๆ ออกไป + +### 8.4.2. TC-2 — การตรวจสอบความน่าเชื่อถือของกระเป๋าเอกสารดิจิทัล (Issuer/Verifier ตรวจ Wallet) + +จุดตรวจ TC-2 ปรากฏทั้งใน OID4VCI และ OID4VP เพื่อให้ผู้ออกเอกสารหรือผู้ตรวจสอบเอกสารยืนยันความน่าเชื่อถือของ Wallet ตาม Thai profile. ใน OID4VCI เมื่อใช้ Wallet Attestation ตาม Appendix E ให้ส่ง Client Attestation ใน Pushed Authorization Request หรือ Token Request เพื่อยืนยัน Client ต่อ Authorization Server ก่อนออก Access Token; รายละเอียด OID4VCI อยู่ใน [§8.2](08-implementation-guidelines.md) และ [ภาคผนวก 8.1](08.1-issuance-full-flow-detail.md). `proofs.jwt` ใน Credential Request แยกต่างหากและใช้พิสูจน์การครอบครองกุญแจที่จะผูกกับ Credential. + +หาก Thai profile กำหนด TC-2 ใน OID4VCI ผู้ออก/Authorization Server ต้องตรวจ Attestation และ PoP ตามกลไกที่ profile ประกาศก่อนออก Access Token, ตรวจ trust source/ลายมือชื่อและสถานะ Wallet ตามข้อมูลที่ profile กำหนด, และปฏิเสธเมื่อหลักฐานไม่ครบหรือไม่ผ่าน. OID4VCI Appendix E ไม่ได้กำหนด Trusted List schema หรือ Wallet Provider Registry ของไทย; แหล่งข้อมูล ฟิลด์ค้นหา freshness และการจัดการข้อผิดพลาดจึงเป็นเรื่องที่ Thai profile ต้องกำหนด. + +กติกาแยกประเภท Wallet ด้านล่างใช้กับ TC-2 ของ OID4VP เท่านั้น. + +ใน OID4VP ผู้ตรวจสอบเอกสารดำเนินการตรวจหลังได้รับ Authorization Response ดังนี้: + +1. ตรวจประเภท Wallet จากข้อมูล Wallet Provider ที่เชื่อถือได้และ Thai profile ซึ่งต้องกำหนดแหล่งข้อมูลและวิธีตรวจประเภทก่อนใช้งานจริง หากไม่มีข้อมูล ข้อมูลไม่สดใหม่ หรือตรวจประเภทไม่ได้ ให้ปฏิเสธ VP +2. **Wallet รัฐบาล:** ต้องมี WIA และ WIA-PoP ครบทั้งคู่ หากขาดรายการใด ให้ปฏิเสธ VP +3. **Wallet เอกชน:** MAY เลือกส่ง WIA และ WIA-PoP ครบทั้งคู่เพื่อทำ TC-2 หรือไม่ส่งทั้งคู่เพื่อข้าม TC-2 และตรวจ TC-3/TC-4 ต่อ; หากส่งมาเพียงรายการเดียว ให้ปฏิเสธ VP +4. เมื่อมี WIA และ WIA-PoP ครบ ให้ทำ TC-2: + - แยก WIA เพื่ออ่าน `iss` (Wallet Provider ID) และ `PK_wp` + - ค้นหา PK_wp ในบริการ Wallet Provider ใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]`; หากไม่พบ ให้ตอบกลับด้วย `400 untrusted_wallet_provider` [research/42](../research/42-trusted-list-lote-jwt-format.md) + - ตรวจลายมือชื่อ WIA ด้วย PK_wp และตรวจช่วงเวลา `iat`/`exp` + - ตรวจลายมือชื่อ WIA-PoP ด้วย `WIA.cnf.jwk` พร้อมตรวจ `iss`, `aud` และ `nonce` + - ทำเครื่องหมาย `jti` ว่าถูกใช้แล้วเพื่อป้องกัน replay + +หาก WIA หรือ WIA-PoP ที่ส่งมาตรวจไม่ผ่าน Verifier MUST ปฏิเสธ VP + +### 8.4.3. TC-3 — การตรวจสอบความน่าเชื่อถือของผู้ออกเอกสาร (Wallet/Verifier ตรวจ Issuer) + +จุดตรวจ TC-3 ปรากฏทั้งใน OID4VCI และ OID4VP โดยมีจุดประสงค์เพื่อยืนยันว่าเอกสารรับรองที่ได้รับมีผู้ออกเอกสารที่น่าเชื่อถือและลายมือชื่อถูกต้อง + +ใน OID4VCI มีการตรวจสอบ 2 รอบ ดังนี้ + +- **รอบแรก (ตรวจก่อนยืนยันตัวตน)** — กระเป๋าเอกสารดิจิทัลตรวจสอบข้อมูล Credential Issuer Metadata ที่ได้รับจากผู้ออกเอกสารควบคู่กับ Trusted List ก่อนที่จะขอ OTP จากผู้ใช้ เพื่อป้องกันการส่งข้อมูลยืนยันตัวตนไปยังผู้ออกเอกสารที่ไม่ผ่านการรับรอง +- **รอบที่สอง (ตรวจก่อนบันทึก)** — กระเป๋าเอกสารดิจิทัลตรวจสอบ SD-JWT VC ฉบับสมบูรณ์ที่ได้รับจากผู้ออกเอกสาร + +ใน OID4VP ผู้ตรวจสอบเอกสารตรวจสอบ SD-JWT VC ที่อยู่ใน `vp_token` สำหรับแต่ละ credential ที่ผู้ถือส่งมา + +กลไกของ TC-3 ในการตรวจแต่ละครั้งมีลำดับขั้นตอนหลัก ดังนี้ + +1. **แยก claims** จาก JWT payload ได้แก่ `iss`, `jti`, `sub`, `@context`, `type` +2. **ค้นหาบริการ Issuer ใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]`** ต้องยืนยันสถานะระดับองค์กรและบริการเป็น `active` ตาม Thai profile ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) +3. **ตรวจสอบความสอดคล้องของข้อมูล** — ตรวจ `iss` และ `jti` ตามรูปแบบ VC ที่ได้รับ และตรวจประเภท VC กับขอบเขตสิทธิ์ของ Issuer ใน Thai profile; `@context` และ `type` ใช้ตรวจเฉพาะ VC รูปแบบ W3C VCDM ตามข้อกำหนดของรูปแบบนั้น +4. **ดึงกุญแจสาธารณะของผู้ออกเอกสาร (PK_iss)** โดย dereference `kid` ผ่าน DID Resolver +5. **ตรวจสอบลายมือชื่อ SD-JWT VC** ด้วย PK_iss +6. **ตรวจสอบช่วงเวลาใช้งาน** — `validFrom ≤ now ≤ validUntil` +7. **ตรวจสอบ disclosures** ในกรณี OID4VP — ต้อง hash ตรงกับ `_sd` ใน issuer_jwt และ DCQL query ต้อง match กับ `reconstructed_credential` + +หากขั้นตอนใดไม่ผ่าน ฝั่งที่ดำเนินการตรวจ MUST ปฏิเสธคำขอหรือทิ้ง VC ทันที + +### 8.4.4. TC-4 — การตรวจสอบสถานะเอกสาร (Wallet/Verifier ตรวจ Status List) + +จุดตรวจ TC-4 ปรากฏทั้งใน OID4VCI และ OID4VP โดยมีจุดประสงค์เพื่อยืนยันว่าเอกสารรับรองที่ลงนามโดยผู้ออกเอกสารที่น่าเชื่อถือยังคงสถานะใช้งานได้ ไม่ถูกยกเลิกหรือระงับ + +หลักการสำคัญ: ลายมือชื่อที่ถูกต้องตาม TC-3 ไม่ได้หมายความว่าเอกสารรับรองยังใช้ได้ ผู้ออกเอกสารอาจยกเลิกเอกสารรับรองได้ทุกเมื่อ ฝั่งที่ดำเนินการตรวจ MUST ตรวจสอบ Status List ทุกครั้ง แม้ว่า TC-3 จะผ่านแล้ว [REF-TSL] + +กลไกของ TC-4 มีลำดับขั้นตอนหลัก ดังนี้ + +1. **อ่าน `uri` และ `idx`** จาก `status.status_list` ใน JWT payload +2. **ดึง Status List JWT** จาก `uri` โดยระบุ `Accept: application/statuslist+jwt` +3. **ตรวจสอบ Status List JWT** — ต้องมี `typ = "statuslist+jwt"`, `sub` ตรงกับ URI, ช่วงเวลา `iat`/`exp` ยังไม่หมดอายุ และลงนามโดย PK_iss ที่ตรวจได้ใน TC-3 +4. **ถอดรหัส `lst`** — base64url decode แล้วคลาย DEFLATE ตามรูปแบบ ZLIB +5. **อ่านค่า status ที่ตำแหน่ง `idx`** โดยใช้ความกว้าง `bits` ที่ระบุใน Status List JWT — `0x00` หมายถึงใช้งานได้ (VALID), `0x01` หมายถึงถูกยกเลิก (INVALID), `0x02` หมายถึงถูกระงับ (SUSPENDED); ปฏิเสธค่าที่ไม่รู้จักหรือไม่รองรับ (fail-closed) + +หากดึงหรือตรวจสอบ Status List JWT ไม่สำเร็จ ฝั่งที่ดำเนินการตรวจ MUST ปฏิเสธคำขอ (fail-closed) เนื่องจากไม่สามารถยืนยันสถานะของเอกสารรับรองได้ + +### 8.4.5. สรุปจุดตรวจ 4 ด่าน และการเทียบเคียงกับ PKI + +ตารางต่อไปนี้สรุปหน้าที่ของจุดตรวจทั้ง 4 ด่าน และเทียบเคียงกับกลไกในโครงสร้างพื้นฐานกุญแจสาธารณะ (Public Key Infrastructure — PKI) เพื่อให้ผู้อ่านที่คุ้นเคยกับ PKI เข้าใจความสอดคล้อง + +| รหัส | ตรวจอะไร | ใครตรวจ | ปรากฏใน | บังคับ? | เทียบเคียง PKI | +|------|---------|--------|---------|--------|---------------| +| **TC-1** | ตรวจว่าผู้ขอข้อมูลน่าเชื่อถือ | Wallet | OID4VP เท่านั้น | ใช่ — Wallet MUST ตรวจ Verifier ก่อนส่งข้อมูลใด ๆ | ตรวจ Relying Party identity | +| **TC-2** | ตรวจว่า Wallet น่าเชื่อถือ | Issuer (OID4VCI) / Verifier (OID4VP) | ทั้ง 2 Flow | OID4VCI ตาม §8.2; สำหรับ OID4VP ต้องจำแนกประเภท Wallet จากข้อมูลที่เชื่อถือได้ (หากทำไม่ได้ให้ปฏิเสธ VP); Wallet รัฐบาลต้องส่งและผ่านการตรวจ WIA/WIA-PoP ครบคู่ ส่วน Wallet เอกชนเลือกส่งครบคู่หรือไม่ส่งทั้งคู่ หากส่งไม่ครบหรือหลักฐานตรวจไม่ผ่านต้องปฏิเสธ | ตรวจ Application identity | +| **TC-3** | ตรวจว่า Issuer น่าเชื่อถือ + ลายมือชื่อถูกต้อง | Wallet (OID4VCI) / Verifier (OID4VP) | ทั้ง 2 Flow | ใช่ — ใน OID4VCI ตรวจ 2 รอบ (early + full) | ตรวจ Issuer identity + signature | +| **TC-4** | ตรวจว่าสถานะ VC ใช้งานได้ | Wallet (OID4VCI) / Verifier (OID4VP) | ทั้ง 2 Flow | ใช่ — MUST ตรวจ Status List ทุกครั้ง | ตรวจสถานะ JWT แบบ Status List | + +> **ข้อสังเกต:** TC-4 ใช้ IETF Token Status List [REF-TSL] ซึ่งเก็บค่า status แบบบรรจุหลายรายการใน byte array ตามความกว้าง `bits` แล้วบีบอัดเป็น JWT; ผู้ออก Status List ลงลายมือชื่อ Status List JWT และผู้ตรวจสอบต้องตรวจลายมือชื่อนั้นด้วยกุญแจที่เชื่อถือได้ของผู้ออก Status List ตาม profile + +--- + +## 8.5. การ resolve DID (DID Resolution) — 3 กรณี + +เมื่อกระเป๋าเอกสารดิจิทัล (ใน OID4VCI) หรือผู้ตรวจสอบเอกสาร (ใน OID4VP) ต้องตรวจสอบลายมือชื่อของ VC ต้องดึงกุญแจสาธารณะของผู้ออกเอกสารจาก DID ก่อน มี 3 กรณีตาม DID method [REF-DID-CORE][REF-UR] ดังนี้ + +**กรณีที่ 1 — `did:web:issuer.example.th`:** ฝ่ายที่ตรวจสอบดึง DID Document โดยตรงจาก HTTPS GET ไปยัง `https://issuer.example.th/.well-known/did.json` กรณีนี้ต้องอาศัยเครือข่าย และผู้ดูแลคือ Issuer เอง + +**กรณีที่ 2 — `did:key:z6Mki...`:** กุญแจสาธารณะถูกฝังอยู่ในสายอักขระของ DID โดยตรง (self-contained) ฝ่ายที่ตรวจสอบสามารถแยกกุญแจสาธารณะออกมาได้ทันทีโดยไม่ต้องอาศัยเครือข่าย ไม่มีผู้ดูแลส่วนกลาง + +**กรณีที่ 3 — `did:ndid:...` หรือ custom DID method:** ฝ่ายที่ตรวจสอบส่งคำขอไปยัง ETDA Trust Gateway ซึ่งทำหน้าที่เป็น Universal Resolver กรณีนี้ต้องอาศัยเครือข่ายและใช้ API key ผู้ดูแลคือ สพธอ. (ETDA) + +| DID method | วิธี resolve DID | ต้องเครือข่าย? | ผู้ดูแล | +|-----------|-------------|:------------:|---------| +| `did:web:issuer.example.th` | HTTPS GET `.well-known/did.json` | ใช่ | Issuer เอง | +| `did:key:z6Mki...` | แยกจากสายอักขระ DID (self-contained) | ไม่ | ไม่มี | +| `did:ndid:...` / custom | ETDA Trust Gateway Universal Resolver (API key) | ใช่ | สพธอ. (ETDA) | + +> **สำคัญ:** ทั้ง 3 กรณีต้องดำเนินการภายหลังจากผ่าน TC-3 แล้ว กล่าวคือ การ resolve DID เกิดขึ้นหลังยืนยันความน่าเชื่อถือของผู้ออกเอกสารจาก Trusted List แล้ว เพื่อให้ทราบว่าต้องดึงกุญแจสาธารณะจากแหล่งใด +> +> **หมายเหตุ `did:ndid`:** ปัจจุบันยังไม่จดทะเบียนใน W3C DID method registry สพธอ. ต้องพัฒนา DID method spec + Universal Resolver driver เอง + +--- + +## 8.6. กฎเสริมสำหรับการพัฒนา + +นอกเหนือจากข้อกำหนดหลักใน 8.1.2 แล้ว การพัฒนาระบบ SHOULD ปฏิบัติตามกฎเสริมเหล่านี้เพื่อให้ระบบมีความปลอดภัยและทำงานร่วมกันได้: + +| หัวข้อ | กฎเสริม (RFC 2119) | ที่มา | +|--------|--------------------|------| +| Access Token TTL | SHOULD ตั้งอายุสั้น (เช่น 5 นาที) และผูกกับอุปกรณ์ (DPoP หรือ cnf) — แนวปฏิบัติที่เอกสารนี้เสนอเพิ่มเติมจากข้อกำหนด OID4VCI | [REF-OID4VCI] | +| Trusted List TTL | SHOULD ตั้งอายุไม่เกิน 24 ชั่วโมง และ SHOULD มีระบบ push notification เมื่อมีการเปลี่ยนแปลง | แนวปฏิบัติ [REF-ARF] | +| WUA | MUST ใช้ WUA ตาม EUDI ARF 2.9.0 ใน OID4VCI ตาม §8.2; สำหรับ OID4VP Verifier MUST จำแนกประเภท Wallet จากข้อมูลที่เชื่อถือได้และปฏิเสธ VP หากทำไม่ได้; Wallet รัฐบาล MUST ส่ง WIA/WIA-PoP ครบคู่ ส่วน Wallet เอกชน MAY ส่งครบคู่หรือไม่ส่งทั้งคู่ (Thai-profile extension) | [REF-ARF] | +| DPoP | SHOULD ใช้ DPoP (RFC 9449) สำหรับ Access Token binding | [REF-DPOP] | +| PKCE | MUST ใช้ PKCE (RFC 7636) ใน Authorization Code flow | [REF-PKCE] | +| Consent UI | MUST แสดงข้อมูล Verifier, ข้อมูลที่ขอ, วัตถุประสงค์ และให้ผู้ใช้เลือกเปิดเผยเฉพาะที่จำเป็น | [REF-ARF] [REF-PDPA] | +| Audit log | MUST บันทึกเหตุการณ์การออก VC การแสดง VP และการตรวจสอบ โดยเก็บไว้ไม่น้อยกว่า 7 ปีตาม พ.ร.บ. ธุรกรรมทางอิเล็กทรอนิกส์ | [REF-ETA] | +| การป้องกันการโจมตีแบบ replay | MUST ใช้ nonce, `jti` หรือกลไกป้องกัน replay อื่นตามที่ protocol และ Thai profile กำหนด; ตรวจ freshness และใช้ซ้ำในแต่ละ proof/token ตามข้อกำหนดที่เกี่ยวข้อง | [REF-OID4VCI] [REF-OID4VP] [REF-DPOP] | +| การบันทึกข้อมูลส่วนบุคคล | MUST ปฏิบัติตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล (PDPA) 2562 และ SHOULD ใช้ Selective Disclosure เพื่อลดการเปิดเผยข้อมูล | [REF-PDPA] | + +--- + +## 8.7. ข้อกำหนดของกรณีศึกษา (จากฉบับเวอร์ชัน 1.1) + +รายงานฉบับนี้ศึกษาการออกและแสดงเอกสารรับรองดิจิทัล โดยใช้กรณีศึกษาการออกใบประมวลผลการศึกษา (Transcript) ของสถาบันการศึกษาในประเทศไทย ตามแผนภาพที่แสดงใน [§7](07-cross-ecosystem-interoperability.md) และ [§9](09-trust-model-3.md) โดยมีข้อกำหนดซึ่งคงไว้จากฉบับเวอร์ชัน 1.1 ดังนี้ + +1. การศึกษามุ่งเน้นไปที่ขั้นตอนการออกเอกสารรับรองดิจิทัล (VC) และการแสดงเอกสารสำแดงดิจิทัล (VP) +2. กระบวนการดึงข้อมูล (Resolve) DID Document ตามมาตรฐานของ W3C จะใช้ DIF Universal Resolver ซึ่งในรายงานฉบับนี้ใช้ DIF Universal Resolver จำลองของ สพธอ. (ETDA) ที่พัฒนาขึ้นเพื่อรองรับความพร้อมของการใช้งานของผู้ให้บริการในคณะทำงานส่งเสริมการพัฒนา Document Wallet และ Verifiable Credentials เพื่อไปดึงข้อมูล DID Document จาก Registry +3. ขั้นตอนการลงทะเบียน (Register) จะดำเนินการโดยใช้โปรโตคอล OID4VP ในรายงานฉบับนี้คณะทำงานส่งเสริมการพัฒนา Document Wallet และ Verifiable Credentials ได้สร้าง DID Document แล้วบันทึกลง Registry เอง + +--- + +## 8.8. สรุป — ข้อกำหนด MUST / SHOULD / MAY + +| หัวข้อ | MUST | SHOULD | MAY | +|--------|:----:|:------:|:---:| +| ใช้มาตรฐาน OID4VCI 1.0 + OID4VP 1.0 | ✅ | | | +| รองรับ W3C VCDM v1.1 สำหรับเอกสารเดิม | ✅ | | | +| รองรับ credential format `dc+sd-jwt` สำหรับเอกสารใหม่ | ✅ | | | +| รองรับ EdDSA (Ed25519) และ ES256 (ECDSA P-256/SHA-256) | ✅ | | | +| ใช้ IETF Token Status List | ✅ | | | +| ใช้ PKCE ใน Authorization Code flow | ✅ | | | +| ตรวจสอบ Issuer/Verifier ใน Trusted List | ✅ | | | +| จำแนกประเภท Wallet รัฐบาล/เอกชนจากข้อมูล Wallet Provider ที่เชื่อถือได้และตรวจสอบความสดใหม่ได้ใน OID4VP; หากจำแนกไม่ได้ให้ปฏิเสธ VP | ✅ | | | +| ตรวจ Status List ก่อนใช้ VC (TC-4) | ✅ | | | +| ใช้ WUA ใน OID4VCI ตาม §8.2 | ✅ | | | +| Wallet รัฐบาลส่ง WIA และ WIA-PoP ครบคู่ใน OID4VP | ✅ | | | +| Wallet เอกชนเลือกส่ง WIA/WIA-PoP ครบคู่หรือไม่ส่งทั้งคู่ใน OID4VP | | | ✅ | +| ปฏิเสธ WIA/WIA-PoP ที่ส่งไม่ครบหรือไม่ผ่านการตรวจ | ✅ | | | +| ใช้ DPoP สำหรับ Access Token binding | | ✅ | | +| ใช้ W3C DID Universal Resolver | ✅ | | | +| รองรับ `did:web` โดยตรง | | ✅ | | +| รองรับ `did:key` โดยตรง | | ✅ | | +| แสดง Consent UI ครบถ้วน | ✅ | | | +| เก็บ audit log ≥ 7 ปี | ✅ | | | +| ปฏิบัติตาม PDPA | ✅ | | | +| ใช้ Selective Disclosure | | ✅ | | + +--- + +{/* METADATA (agent-only — not rendered to readers) +## อ้างอิง + +- [REF-OID4VCI] OpenID Foundation, "OpenID for Verifiable Credential Issuance 1.0" — https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html +- [REF-OID4VP] OpenID Foundation, "OpenID for Verifiable Presentations 1.0" — https://openid.net/specs/openid-4-verifiable-presentations-1_0.html +- [REF-SDJWT] IETF, “SD-JWT-based Verifiable Digital Credentials (SD-JWT VC),” draft-ietf-oauth-sd-jwt-vc-19, 31 August 2026 (Internet-Draft; status as of 1 October 2026: Waiting for AD Go-Ahead) — https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/ +- [REF-W3C-VCDM] W3C, "Verifiable Credentials Data Model v1.1" — https://www.w3.org/TR/vc-data-model/ +- [REF-TSL] IETF, “Token Status List (TSL),” draft-ietf-oauth-status-list-21, 21 June 2026 (Internet-Draft; status as of 1 October 2026: RFC Editor — Awaiting First editor) — https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ +- [REF-DID-CORE] W3C, "Decentralized Identifiers (DIDs) v1.0", 19 July 2022 +- [REF-ARF] European Commission, "EUDI Architecture and Reference Framework v2.9.0" — https://eudi.dev/2.9.0/main/ +- [REF-EdDSA] IETF RFC 8032, "Edwards-Curve Digital Signature Algorithm (EdDSA)" +- [REF-ES256] IETF RFC 7518, "JSON Web Algorithms (JWA)", §3.4 — https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4 +- [REF-UR] DIF, "Universal Resolver" — https://github.com/decentralized-identity/universal-resolver +- [REF-CIR-WUA] (EU) CIR 2024/2979, "Wallet Unit Attestation" +- [REF-DPOP] IETF RFC 9449, "OAuth 2.0 Demonstrating Proof-of-Possession (DPoP)" +- [REF-PKCE] IETF RFC 7636, "Proof Key for Code Exchange (PKCE)" +- [REF-PDPA] พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562 +- [REF-ETA] พ.ร.บ. ว่าด้วยธุรกรรมทางอิเล็กทรอนิกส์ พ.ศ. 2544 และฉบับแก้ไข +- [REF-MASA-126] MASA-126 — S5 Implementation Guidelines (issue tracker) +- [REF-MASA-158] MASA-158 — Merge Full Flow from trust-guide into ARF, STEP 1.5 → 2.5 (2026-08-07) +- [REF-MASA-165] MASA-165 — Polish th/thai-vc-arf/08-implementation-guidelines.md: ตัด Trust Check flowchart ฉบับย่อย อธิบายกลไกด้วยข้อความเชิงบรรยาย และเพิ่มป้ายกำกับ TC-1 ถึง TC-4 บนผังขั้นตอนภาพรวม OID4VCI/OID4VP (2026-08-07) +- [REF-TRUSTLIST-CDN] คณะทำงาน VC, "แนวทางกระจาย Trustlist (LoTE) ผ่าน File/Edge CDN — ไม่ใช้ RabbitMQ", เวอร์ชัน 1.10, 4 กันยายน 2569 — [research/41](../research/41-trustlist-serving-filecdn-etag-binary.md) +- [REF-ARF-V11-SRC] Thai VC ARF v1.1 (มกราคม 2568), สพธอ. — [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) +- [REF-DRAFT-TFM] ฉบับร่าง Trust Flow ฉบับอธิบาย — [draft/trust-flow-minimal-explained.md](../draft/trust-flow-minimal-explained.md) +- [REF-DRAFT-TFT] ฉบับร่าง Trust Framework Template — [draft/trust-framework-template.md](../draft/trust-framework-template.md) + + + + + +--- +*/} + +**การนำทาง:** [บทที่ 7 — การทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List](07-cross-ecosystem-interoperability.md) · [สารบัญ ARF](README.md) · [บทที่ 8.1 — OID4VCI Full Flow ทางเทคนิคโดยละเอียด](08.1-issuance-full-flow-detail.md) diff --git a/docs/thai-vc-arf/08.1-issuance-full-flow-detail.md b/docs/thai-vc-arf/08.1-issuance-full-flow-detail.md new file mode 100644 index 0000000..7622efa --- /dev/null +++ b/docs/thai-vc-arf/08.1-issuance-full-flow-detail.md @@ -0,0 +1,653 @@ +--- +description: "ภาคผนวกบทที่ 8 — Full Flow OID4VCI ทางเทคนิคโดยละเอียด — Thai VC ARF 2.0 DRAFT 0" +--- + +# ภาคผนวกบทที่ 8 — Full Flow OID4VCI ทางเทคนิคโดยละเอียด + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารขยายเพิ่มเติมสำหรับบทที่ 8 Implementation Guidelines +> **เวอร์ชัน:** v1.1 +> **วันที่:** 7 สิงหาคม 2569 +> **แหล่งข้อมูล:** trust-guide/03-issuance-flow/02-full-flow/ (MASA-158) +> **เอกสารที่เกี่ยวข้อง:** [บทที่ 8 — Implementation Guidelines](08-implementation-guidelines.md) · [บทที่ 9 — Trust Model 3](09-trust-model-3.md) · [ภาคผนวก 8.2 — OID4VP Full Flow](08.2-oid4vp-full-flow-detail.md) +*/} + +--- + +## วัตถุประสงค์ + +ภาคผนวกนี้จัดทำขึ้นเพื่ออธิบายรายละเอียดทางเทคนิคของกระบวนการออกเอกสารรับรองดิจิทัลตามมาตรฐาน OID4VCI (OpenID for Verifiable Credential Issuance) [REF-OID4VCI] แบบเต็มรูปแบบ (Full Flow) ตั้งแต่การเตรียมระบบ (SETUP) จนถึงการแจ้งเตือนภายหลังการรับเอกสาร (STEP 7 — Notification) โดยครอบคลุมทุกขั้นตอนอย่างละเอียด พร้อมคำอธิบายระดับ field-by-field สำหรับสถาปนิกระบบ ผู้พัฒนาซอฟต์แวร์ และผู้ตรวจสอบความสอดคล้อง + +{/* METADATA (agent-only — not rendered to readers) +> **หมายเหตุสำคัญ:** จุดตรวจ TC-3-EARLY ได้ย้ายจากตำแหน่ง STEP 1.5 (ก่อน Discovery) ไปยัง **STEP 2.5** (ภายหลัง Discovery ก่อน Authentication) ตามข้อกำหนด MASA-158 [REF-MASA-158] การอ้างอิง STEP ในภาคผนวกนี้สะท้อนตำแหน่งใหม่แล้ว +*/} + +--- + +## D.0. แผนภาพ Sequence ฉบับสมบูรณ์ — Full Flow OID4VCI (STEP 1–7) + +แผนภาพต่อไปนี้แสดงภาพรวมของกระบวนการทั้งหมดตั้งแต่ SETUP จนถึง STEP 7 (Notification): + +```mermaid +sequenceDiagram + actor EU as 👤 ประชาชน + participant W as 📱 Wallet + participant WP as 🏢 Wallet Provider + participant ETDA as 🛡️ ETDA Trusted List + participant Issuer as 🏛️ Issuer + participant SL as 📋 Issuer Status List + + rect rgb(210, 240, 210) + note over W,Issuer: 🟢 [ETDA คุม] SETUP เมื่อมีธุรกรรม — ดึง ETDA Public Key + Trusted List + W->>ETDA: GET https://etda.or.th/.well-known/did.json + ETDA-->>W: ETDA DID Document + Public Key + W->>W: เก็บ ETDA Public Key ไว้ใน Local Storage + Issuer->>ETDA: GET https://etda.or.th/.well-known/did.json + ETDA-->>Issuer: ETDA DID Document + Public Key + Issuer->>Issuer: เก็บ ETDA Public Key ไว้ใน Local Storage + W->>ETDA: GET https://trustme.etda.or.th/.well-known/iss.txt (CDN, ไม่ต้อง API key) + ETDA-->>W: Trusted List ที่เกี่ยวข้องฉบับเต็ม (JWT) + ETag + W->>W: Verify JWT ด้วย ETDA Public Key + W->>W: เก็บไฟล์เต็ม + ETag + NextUpdate + Issuer->>ETDA: GET https://trustme.etda.or.th/.well-known/wp.txt (CDN, ไม่ต้อง API key) + ETDA-->>Issuer: Trusted List ที่เกี่ยวข้องฉบับเต็ม (JWT) + ETag + Issuer->>Issuer: Verify JWT ด้วย ETDA Public Key + Issuer->>Issuer: เก็บไฟล์เต็ม + ETag + NextUpdate + end + + rect rgb(240, 240, 240) + note over W,WP: ⚙️ [Wallet Provider ดูแล] SETUP — Wallet Attestation + WP->>WP: เตรียม Wallet Attestation ตาม OID4VCI Appendix E และ trust information ตาม Thai profile + WP-->>W: Wallet Attestation สำหรับ Client authentication + W->>W: เตรียม Client Attestation PoP ตาม Appendix E + end + + rect rgb(220, 240, 255) + note over EU,Issuer: 🔵 [OID4VCI] STEP 1 — Credential Offer (รับคำเชิญ) + EU->>Issuer: [1] เข้าสู่ระบบบริการ + กด "ขอเอกสารรับรองดิจิทัล" + Issuer->>Issuer: [2] สร้าง pre_authorized_code + tx_code (OTP) + Issuer-->>EU: [3] QR Code บนหน้าจอ + Issuer-->>EU: [4] SMS OTP + EU->>W: [5] สแกน QR Code + end + + rect rgb(220, 240, 255) + note over W,Issuer: 🔵 [OID4VCI] STEP 2 — Discovery (ดูข้อมูลผู้ออกเอกสาร) + W->>Issuer: [6] GET /.well-known/openid-credential-issuer + Issuer-->>W: [7] Credential Issuer Metadata (format, claims, token_endpoint) + W->>W: [7.1] Scope Validation — ตรวจ credential_configurations_supported ว่ามีประเภทเอกสารที่ต้องการ ถ้าไม่พบ → หยุด + end + + rect rgb(255, 237, 200) + note over W,ETDA: 🟠 [ETDA Trust Model] STEP 2.5 — TC-3-EARLY: Trust Check: Issuer + W->>W: [7.2] ตรวจเจ้าของลายเซ็นบน Credential Issuer Metadata ใน Local Trusted List + W->>W: [7.3] ตรวจสถานะองค์กรและบริการตาม Thai profile + W->>W: [7.4] ตรวจประเภท VC กับขอบเขตสิทธิ์ของ Issuer + end + + rect rgb(220, 240, 255) + note over EU,W: 🔵 [OID4VCI] STEP 3 — Authentication (ยืนยันตัวตนด้วย OTP) + W->>EU: [8] กรุณากรอกรหัส OTP ที่ได้รับทาง SMS + EU->>W: [9] กรอก OTP + end + + rect rgb(235, 230, 255) + opt เมื่อ Thai profile กำหนด DPoP + note over W,Issuer: 🔐 DPoP PRE-STEP 4 — สร้าง DPoP Key Pair + W->>W: [9.1] สร้าง DPoP key pair และเก็บ private key ตามข้อกำหนดของ profile + W->>W: สร้าง DPoP Proof JWT สำหรับ /token ตาม RFC 9449 + end + end + + rect rgb(245, 245, 220) + note over Issuer,WP: ⚙️ เตรียม Wallet Provider Trusted List สำหรับ TC-2 + Issuer->>ETDA: GET Trusted List ของ Wallet Provider + If-None-Match + ETDA-->>Issuer: Trusted List ที่เกี่ยวข้องฉบับเต็ม (JWT) หรือ 304 Not Modified + Issuer->>Issuer: ตรวจลายมือชื่อ ความสดใหม่ ลำดับรายการ และช่วงเวลาที่มีผลใช้ได้ของ Trusted List + end + + rect rgb(220, 240, 255) + note over W,Issuer: 🔵 [OID4VCI] STEP 4 — Token Request + TC-2 (แลกบัตรผ่าน) + W->>Issuer: [10] POST /token + pre_authorized_code + tx_code, Wallet Attestation/Client Attestation PoP เมื่อใช้ client authentication ตาม Appendix E, DPoP เมื่อ Thai profile กำหนด + Issuer->>Issuer: [11] แยกตัวระบุ Wallet Provider และกุญแจตรวจสอบจาก Wallet Attestation + Issuer->>Issuer: [11.1] ค้นหา Wallet Provider ใน Trusted List ที่ตรวจแล้ว → ต้องพบบริการตามบทบาท กุญแจตรวจสอบต้องตรง และยืนยันสถานะองค์กร/บริการเป็น active ได้ + Issuer->>Issuer: [11.2] ตรวจ Attestation/PoP และ OTP ตาม OID4VCI/Thai profile, หาก [11.1] หรือการตรวจใดไม่ผ่าน → ไม่ออก Access Token + opt เมื่อใช้ DPoP + Issuer->>Issuer: ตรวจ DPoP Proof + end + opt เมื่อใช้ DPoP + Issuer->>Issuer: ผูก access_token กับ DPoP public key + end + Issuer-->>W: [12] Token Response: access_token (token_type ตามการใช้ DPoP) + end + + opt Issuer Metadata ระบุ Nonce Endpoint + W->>Issuer: [12.1] POST Nonce Endpoint (ไม่ต้องใช้ Access Token) + Issuer-->>W: [12.2] 2xx + c_nonce, Cache-Control: no-store + end + + rect rgb(220, 240, 255) + note over W,Issuer: 🔵 [OID4VCI] STEP 5 — Credential Request (ขอรับเอกสารรับรองดิจิทัล) + W->>W: [13] สร้าง Key Pair + ลงนาม JWT Proof โดยใส่ nonce หาก Issuer มี Nonce Endpoint + opt เมื่อ access token ผูกกับ DPoP key + W->>W: [13.1] สร้าง DPoP Proof ใหม่สำหรับ /credential ตาม RFC 9449 + end + W->>Issuer: [14] POST /credential + Authorization: Bearer หรือ DPoP + DPoP-Proof เมื่อ access token ผูกกับ DPoP key + credential_configuration_id + proofs.jwt + end + + rect rgb(220, 240, 255) + note over W,Issuer: 🔵 [OID4VCI] STEP 6 — Issue VC (ออกเอกสารรับรองดิจิทัล) + Issuer->>Issuer: [22] Validate Access Token + Key Proof + สร้าง SD-JWT VC + note over Issuer: JOSE — typ:dc+sd-jwt, alg:ES256, kid:issuer.dopa.go.th#key-2025-1 + note over Issuer: SD-JWT VC payload — registered JWT claims (iss, iat, exp), vct, cnf และ claims ระดับบนสุดพร้อม _sd, claims เสริมขึ้นกับ Thai profile, ไม่ใช่ W3C VC JSON object + alt Immediate Issuance + Issuer-->>W: [23] credential: SD-JWT VC + notification_id (ถ้ามี) + else Deferred Issuance + Issuer-->>W: [23] transaction_id + W->>Issuer: [24] POST /deferred_credential + transaction_id + Issuer-->>W: [25] credential: SD-JWT VC + notification_id (ถ้ามี) + end + end + + rect rgb(245, 245, 220) + note over W,Issuer: ⚙️ Trusted List Freshness Check (ก่อน STEP 6.5) + W->>ETDA: GET https://trustme.etda.or.th/.well-known/iss.txt + If-None-Match + alt 304 Not Modified + ETDA-->>W: ใช้ไฟล์เต็มใน Local Cache + else 200 OK + ETDA-->>W: Trusted List ที่เกี่ยวข้องฉบับเต็ม (JWT) + ETag + W->>W: Verify + Update Local Trusted List + ETag + end + end + + rect rgb(255, 237, 200) + note over W,Issuer: 🟠 [ETDA Trust Model] STEP 6.5 — TC-3: Trust Check: Issuer + W->>W: [26] แกะ iss, jti, sub จาก JWT payload + W->>W: [27] Lookup iss ใน Local Trusted List → พบ status = active + W->>W: [27.1] ตรวจ Issuer/identifier/type ตามรูปแบบ Credential และ Thai profile + W->>W: [27.2] หากเป็น W3C VCDM ให้ตรวจ issuer.id/id, @context และ type ตาม profile + W->>W: [27.3] หากเป็น SD-JWT VC ให้ตรวจ iss/vct และ claims ตาม Thai credential profile (ไม่สมมติ W3C VC body) + end + + rect rgb(220, 240, 255) + note over W,Issuer: 🔵 [W3C VC-JOSE-COSE] Resolve Issuer Public Key ผ่าน JWKS + W->>Issuer: [28] GET https://issuer.dopa.go.th/.well-known/jwt-vc-issuer + Issuer-->>W: [29] JWKS — รายการ key พร้อม kid, alg, x, y + W->>W: [29.1] kid matching: หา key ใน JWKS ที่ตรงกับ JOSE header, ดึง publicKeyJwk + end + + rect rgb(255, 237, 200) + note over W,Issuer: 🟠 [ETDA Trust Model] STEP 6.5 ต่อ — Verify + Validity + IETF Status + W->>W: [30] Verify SD-JWT signature ด้วย publicKeyJwk (kid matched) + W->>W: [30.1] ตรวจ `nbf`/`exp` และ `sub` เฉพาะตาม Thai credential profile, ห้าม map เป็น `validFrom`/`credentialSubject.id` โดยปริยาย + W->>W: [30.2] ตรวจช่วงเวลาใช้ได้ตาม JWT claims หรือ field mapping ที่ Thai profile กำหนดสำหรับ format นั้น + W->>SL: [30.3] GET status.status_list.uri (Accept: application/statuslist+jwt) + SL-->>W: [30.4] statuslist+jwt + W->>W: [30.5] Validate statuslist+jwt ตามกฎ JWT และ key-resolution/trust model ใน Thai profile, ตรวจ typ, sub = uri และ claims ที่มี + W->>W: [30.6] ตรวจ iat/freshness policy, ตรวจ exp เมื่อมี และใช้ ttl เมื่อ cache + W->>W: [30.7] Decompress lst แล้วอ่าน status slot ที่ idx = 0 ตาม bits (0x00 VALID), หาก idx อยู่นอกขอบเขตหรือ status ไม่รองรับ → ปฏิเสธ + end + + rect rgb(220, 240, 255) + note over W,Issuer: 🔵 [OID4VCI] STEP 7 — Notification (แจ้งว่ารับแล้ว, Optional) + W->>Issuer: [31] POST /notification notification_id (จาก Credential Response หรือ Deferred Credential Response) + event=credential_accepted + Issuer-->>W: [32] 204 No Content + W-->>EU: [33] บันทึกเอกสารรับรองดิจิทัลสำเร็จ + end + +``` + +**คำอธิบายแต่ละขั้นตอนโดยสังเขป:** + +| ช่วง STEP | ขั้นตอน | คำอธิบาย | +|-----------|---------|----------| +| **SETUP** | ดึง ETDA Public Key + Trusted List เมื่อมีธุรกรรม | Wallet และ Issuer ดึง ETDA Public Key จาก `/.well-known/did.json` แล้วดึง Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จาก CDN เมื่อเกิดการเชื่อมต่อ โดยไม่ใช้ API key หรือ background polling | +| **SETUP** | Wallet Attestation (WUA) | เมื่อใช้ client authentication ตาม OID4VCI Appendix E, Wallet Provider ออก Wallet Attestation; Wallet ส่ง Attestation และ Client Attestation PoP ใน PAR หรือ Token Request. Key Attestation ใน `proofs.jwt` เป็นกลไกแยกต่างหาก | +| **STEP 1** | Credential Offer | ผู้ใช้กดขอเอกสารรับรอง → Issuer สร้าง QR Code + ส่ง OTP ทาง SMS (pre_authorized_code + tx_code) | +| **STEP 2** | Discovery + Scope Validation | Wallet ดึง Credential Issuer Metadata → ตรวจสอบว่า credential type ที่ต้องการมีใน Metadata หรือไม่ | +| **STEP 2.5** | TC-3-EARLY | Wallet ตรวจสอบ Issuer ใน Trusted List + credential type ที่ได้รับอนุมัติ + Credential Issuer Metadata | +| **STEP 3** | Authentication | Wallet ขอ OTP → ผู้ใช้กรอก OTP → พิสูจน์ตัวตน | +| **PRE-STEP 4** | DPoP Key Pair | Wallet สร้าง DPoP key pair ตาม Thai profile เมื่อกำหนดให้ใช้ DPoP | +| **STEP 4** | Token Request + TC-2 | ส่ง `pre-authorized_code` + OTP; หากเลือก client authentication ตาม Appendix E ให้ส่ง Wallet Attestation และ Client Attestation PoP ใน Token Request; Issuer ค้นหา Wallet Provider จาก Attestation ใน Trusted List ที่ตรวจแล้ว และยืนยันบทบาท กุญแจ สถานะองค์กร/บริการก่อนตรวจ client authentication และออก Token; DPoP ใช้เมื่อ profile กำหนด | +| **STEP 5** | Credential Request | Wallet สร้าง Holder Key + JWT Proof → POST /credential พร้อม `proofs.jwt`; Key Attestation ตาม Appendix D/F.1 อาจอยู่ใน JOSE header ของ proof JWT; Wallet Attestation ไม่ใช่ JSON member ของ body นี้ | +| **STEP 6** | Issue VC | Issuer สร้าง SD-JWT VC ครบ 3 ชั้น → ส่ง credential ทันที (Immediate) หรือให้รอ (Deferred) | +| **STEP 6.5** | TC-3 + TC-4 | Wallet ตรวจสอบ Issuer ใน Trusted List + verify ลายมือชื่อ + ตรวจสอบ validity window + ตรวจสอบ Status List | +| **STEP 7** | Notification (Optional) | Wallet แจ้ง Issuer ว่ารับเอกสารแล้ว — เสริมสำหรับ audit log | + +--- + +## D.1. คำอธิบายทุกขั้นตอนโดยละเอียด + +### D.1.1. SETUP — การเตรียมระบบก่อนเริ่มใช้งาน + +ขั้นตอนนี้ดำเนินการครั้งเดียวเมื่อติดตั้งกระเป๋าเอกสารดิจิทัล (Wallet) หรือนำระบบของผู้ออกเอกสาร (Issuer) ขึ้นสู่สภาพแวดล้อมการทำงานจริง ทุกฝ่ายต้องมีข้อมูลพื้นฐานสำหรับการตรวจสอบความน่าเชื่อถือระหว่างกันก่อนเริ่มกระบวนการใด ๆ + +#### การดึง ETDA Public Key (Root of Trust) + +กระเป๋าเอกสารดิจิทัลและผู้ออกเอกสาร MUST ดึง ETDA Public Key จาก DID Document ที่ สพธอ. ประกาศไว้ กุญแจสาธารณะนี้เป็นจุดเริ่มต้นของห่วงโซ่ความเชื่อถือ (root of trust) ใช้สำหรับตรวจสอบลายมือชื่อดิจิทัลบน Trusted List [REF-TRUSTLIST-CDN] + +**คำขอ (Request):** +```http +GET https://etda.or.th/.well-known/did.json +``` + +**คำตอบ (Response):** +```json +{ + "id": "did:web:etda.or.th", + "verificationMethod": [ + { "id": "did:web:etda.or.th#tl-signing-key-1", "type": "Ed25519VerificationKey2020" } + ] +} +``` + +> **ข้อสังเกต:** กุญแจนี้เป็นจุดเริ่มต้นของห่วงโซ่ความเชื่อถือ — สพธอ. อาจปรับปรุงกุญแจได้โดยไม่ต้องปรับใช้แอปพลิเคชันใหม่ + +#### การดึง Trusted List + +กระเป๋าเอกสารดิจิทัลและผู้ออกเอกสารต้องดึง Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จากโครงข่ายกระจายเนื้อหา (CDN) ของ สพธอ. แบบ lazy เมื่อเกิดการใช้งาน โดยไม่ต้องใช้ API key และไม่ใช้ delta หรือไฟล์ CBOR ผู้รับต้องตรวจลายมือชื่อของ สพธอ. ก่อนใช้ข้อมูล [REF-TRUSTLIST-CDN] + +**คำขอ (Request) ตัวอย่าง:** +```http +GET https://trustme.etda.or.th/.well-known/iss.txt +Accept: application/jwt +``` + +**คำตอบ (Response) ตัวอย่าง:** +```http +HTTP/1.1 200 OK +Content-Type: application/jwt +ETag: "urn:etda:trusted-list:issuer:2026-v43" + + +``` + +ภายหลังการตรวจสอบลายมือชื่อ JWT กระเป๋าเอกสารดิจิทัลและผู้ออกเอกสารต้องจัดเก็บไฟล์ฉบับเต็มและค่า `ETag` ไว้ใน Local Storage เมื่อเกิดการใช้งานครั้งถัดไป ให้ส่ง `If-None-Match` หากได้รับ `304 Not Modified` ให้ใช้ไฟล์เดิม หากได้รับ `200 OK` ให้ตรวจลายมือชื่อ ตรวจ `LoTE.ListAndSchemeInformation.LoTESequenceNumber` และ `NextUpdate` ก่อนแทนที่ cache [research/42](../research/42-trusted-list-lote-jwt-format.md) [REF-TRUSTLIST-CDN] + +#### Wallet Unit Attestation (WUA) + +Wallet Attestation ตาม OID4VCI Final Appendix E เป็นวิธียืนยัน Client สำหรับ Native App Wallet. Wallet ส่ง Attestation ใน Pushed Authorization Request หรือ Token Request และ MUST สร้าง Client Attestation PoP ตาม Section 5.2 ของ Attestation-Based Client Authentication ที่ Appendix E อ้างถึง [REF-OID4VCI]. Authorization Server MUST ตรวจลายมือชื่อ Attestation จาก Issuer ที่ตนเชื่อถือ. แยกจาก Key Attestation ใน Appendix D ซึ่งอาจอยู่ใน JOSE header ของ JWT proof ใน `proofs.jwt` ตาม Appendix F.1; เมื่อ Issuer ให้ `c_nonce`, nonce claim ใน Key Attestation ต้องใช้ค่า `c_nonce` นั้น. Thai profile กำหนด trust source และวิธีตรวจสถานะ Wallet Provider/Wallet เพิ่มเติม [REF-OID4VCI] + +ตัวอย่างนี้ไม่แสดง JWT header/payload หรือชื่อพารามิเตอร์ของ Wallet Attestation/Client Attestation PoP เพื่อไม่ให้ placeholder ถูกเข้าใจว่าเป็นรูปแบบมาตรฐาน. เมื่อใช้กลไกนี้ ผู้ติดตั้งระบบ MUST ใช้รูปแบบและวิธีส่ง Wallet Attestation ตาม OID4VCI Final Appendix E และ Client Attestation PoP ตาม Section 5.2 ของ Attestation-Based Client Authentication ที่ Appendix E อ้างถึง [REF-OID4VCI]; Thai profile กำหนด trust source, key resolution, สถานะ Wallet Provider/Wallet และผลเมื่อการตรวจไม่ผ่านเพิ่มเติม [REF-OID4VCI]. + +--- + +### D.1.2. STEP 1 — Credential Offer (การออกคำเชิญ) + +ผู้ใช้เข้าระบบบริการของผู้ออกเอกสารและกดขอเอกสารรับรองดิจิทัล ผู้ออกเอกสารดำเนินการสร้างข้อมูลสองรายการสำหรับ session นั้น: + +| ข้อมูล | ค่าตัวอย่าง | อายุใช้งาน | หน้าที่ | +|--------|------------|------------|--------| +| `pre_authorized_code` | `pac-a1b2c3d4e5f6` | 5 นาที | รหัสลับที่กระเป๋าเอกสารดิจิทัลใช้แลก Access Token | +| `tx_code` (OTP) | `512847` | 5 นาที | รหัสที่ผู้ใช้กรอกเพื่อยืนยันตัวตน | + +**QR Code** บรรจุ Credential Offer Object ในรูปแบบ URI: + +```json +openid-credential-offer://?credential_offer={ + "credential_issuer": "https://issuer.dopa.go.th", + "credential_configuration_ids": ["ThaiNationalIDCredential-config"], + "grants": { + "urn:ietf:params:oauth:grant-type:pre-authorized_code": { + "pre-authorized_code": "pac-a1b2c3d4e5f6", + "tx_code": { "input_mode": "numeric", "length": 6 } + } + } +} +``` + +ผู้ออกเอกสารส่ง SMS OTP ไปยังผู้ใช้พร้อมแสดง QR Code บนหน้าจอ ผู้ใช้สแกน QR Code ด้วยกระเป๋าเอกสารดิจิทัล — กระเป๋าเอกสารดิจิทัลทราบข้อมูลเบื้องต้นว่าต้องขอเอกสารจากผู้ออกเอกสารใด และเป็นเอกสารประเภทใด + +--- + +### D.1.3. STEP 2 — Discovery (การค้นหาข้อมูลผู้ออกเอกสาร) + +กระเป๋าเอกสารดิจิทัลดึง Credential Issuer Metadata จากผู้ออกเอกสาร เพื่อรับข้อมูล endpoint และ credential configuration ที่จำเป็นสำหรับขั้นตอนถัดไป: + +```http +GET https://issuer.dopa.go.th/.well-known/openid-credential-issuer +``` + +**คำตอบ (Response):** Credential Issuer Metadata ต่อไปนี้เป็นตัวอย่างย่อ โดย `token_endpoint` มาจาก OAuth Authorization Server Metadata ตาม OID4VCI ไม่ใช่ field ของ Credential Issuer Metadata: +```json +{ + "credential_issuer": "https://issuer.dopa.go.th", + "credential_endpoint": "https://issuer.dopa.go.th/credential", + "notification_endpoint": "https://issuer.dopa.go.th/notification", + "credential_configurations_supported": { + "ThaiNationalIDCredential-config": { + "format": "dc+sd-jwt", + "vct": "https://credentials.dopa.go.th/ThaiNationalIDCredential", + "cryptographic_binding_methods_supported": ["jwk"], + "proof_types_supported": { + "jwt": { "proof_signing_alg_values_supported": ["ES256"] } + }, + "credential_metadata": { + "display": [{ "name": "บัตรประชาชนดิจิทัล", "locale": "th-TH" }], + "claims": [ + { "path": ["ชื่อ"], "display": [{ "name": "ชื่อ-นามสกุล", "locale": "th-TH" }] }, + { "path": ["เลขบัตร"], "display": [{ "name": "เลขบัตรประชาชน", "locale": "th-TH" }] }, + { "path": ["วันเกิด"], "display": [{ "name": "วันเกิด", "locale": "th-TH" }] } + ] + } + } + } +} +``` + +**Scope Validation [7.1]:** กระเป๋าเอกสารดิจิทัล MUST ตรวจสอบว่า `credential_configuration_id` จาก QR Code ตรงกับรายการใน `credential_configurations_supported` หรือไม่ — หากไม่พบ MUST หยุดดำเนินการทันที + +--- + +### D.1.4. STEP 2.5 — TC-3-EARLY: การตรวจสอบความน่าเชื่อถือของผู้ออกเอกสารก่อนยืนยันตัวตน + +> **ตำแหน่งใหม่ (MASA-158) [REF-MASA-158]:** ขั้นตอนนี้ดำเนินการภายหลัง Discovery (STEP 2) แต่ก่อน Authentication (STEP 3) — กระเป๋าเอกสารดิจิทัลใช้ข้อมูลจาก Credential Issuer Metadata ประกอบกับ Trusted List ที่จัดเก็บในเครื่อง เพื่อตรวจสอบความน่าเชื่อถือของผู้ออกเอกสาร + +กระเป๋าเอกสารดิจิทัลดำเนินการฝ่ายเดียว โดยใช้ Trusted List ที่จัดเก็บในเครื่อง (Local Trusted List) ไม่ต้องเรียกผ่านเครือข่าย: + +1. **ตรวจสอบผู้ออกเอกสารใน Trusted List [7.2–7.3]:** ค้นบริการ Issuer ใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` โดยเทียบ `ServiceInformation.ServiceTypeIdentifier` และข้อมูลจาก Credential Issuer Metadata ต้องยืนยันสถานะระดับองค์กรและบริการเป็น `active` ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) +2. **ตรวจสอบประเภทเอกสารรับรอง [7.4]:** ประเภท VC ที่ `credential_configuration_id` อ้างถึงต้องอยู่ในขอบเขตสิทธิ์ของ Issuer ตาม Thai profile; หากขอบเขตยังไม่ประกาศหรือยืนยันไม่ได้ ต้องปฏิเสธ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +หากขั้นตอนใดไม่ผ่าน — กระเป๋าเอกสารดิจิทัล MUST หยุดดำเนินการทันที โดยไม่ร้องขอ OTP จากผู้ใช้ ทั้งนี้ เพื่อป้องกันการส่งข้อมูลยืนยันตัวตนของผู้ใช้ไปยังผู้ออกเอกสารที่ไม่ผ่านการรับรองจาก Trusted List + +--- + +### D.1.5. STEP 3 — Authentication (การยืนยันตัวตนด้วย OTP) + +กระเป๋าเอกสารดิจิทัลแสดง UI ขอให้ผู้ใช้กรอก OTP ที่ได้รับทาง SMS ผู้ใช้กรอก OTP — เป็นการพิสูจน์ว่าผู้ใช้เป็นเจ้าของหมายเลขโทรศัพท์ที่ผูกกับบัญชี + +--- + +### D.1.6. PRE-STEP 4 — DPoP Key Pair (สำหรับ IAL2.3+/AAL2+) + +เมื่อ Thai profile กำหนดให้ใช้ DPoP, กระเป๋าเอกสารดิจิทัลต้องสร้าง DPoP Key Pair ก่อนดำเนินการ STEP 4. ตารางและตัวอย่างด้านล่างเป็นข้อเสนอรอการยืนยันจากคณะทำงาน; ยังไม่ใช่ข้อกำหนดของ Thai profile. + +{/* TODO: นโยบาย DPoP ยังไม่ได้รับการยืนยัน (To be confirmed) — รอมติคณะทำงาน VC */} + +| สิ่งที่สร้าง | สถานที่จัดเก็บ | ส่งไปที่ใด | +|-------------|----------------|------------| +| DPoP Private Key | Trusted Execution Environment (TEE) | ห้ามส่งออกนอกอุปกรณ์ | +| DPoP Public Key | ใส่ใน DPoP Proof JWT header ตาม RFC 9449 | ส่งใน `DPoP` header เมื่อใช้ DPoP | +| DPoP Proof JWT | สร้างใหม่ทุกคำขอที่ต้องมี proof | ส่งเป็น `DPoP` header ตาม RFC 9449 | + +> **หลักการ DPoP (Demonstrating Proof of Possession — RFC 9449 [REF-DPOP]):** Access Token แบบ Bearer หากถูกขโมยสามารถนำไปใช้จากอุปกรณ์ใดก็ได้ DPoP ผูก Token กับกุญแจบนอุปกรณ์ — ขโมย Token ไปใช้จากอุปกรณ์อื่นไม่ได้ + +**DPoP Proof JWT ตัวอย่าง:** +```json +{ + "header": { + "typ": "dpop+jwt", + "alg": "ES256", + "jwk": { "kty": "EC", "crv": "P-256", "x": "dpop_pub_x", "y": "dpop_pub_y" } + }, + "payload": { + "jti": "unique-per-request-abc123", + "htm": "POST", + "htu": "https://issuer.dopa.go.th/token", + "iat": 1750000100 + } +} +``` + +| Field | ค่า | หน้าที่ | +|-------|-----|--------| +| `typ` | `dpop+jwt` | ระบุประเภท JWT | +| `jwk` | DPoP Public Key | ใช้ตรวจสอบลายมือชื่อ + ผูก Token | +| `jti` | รหัสสุ่มเฉพาะคำขอ | ป้องกันการโจมตีแบบ replay | +| `htm` | `POST` | ต้องตรงกับ HTTP method ที่ใช้ | +| `htu` | URL ของ endpoint | ต้องตรงกับ endpoint ที่เรียก | +| `iat` | เวลาปัจจุบัน | ป้องกัน Proof เก่าถูกนำมาใช้ซ้ำ | + +--- + +### D.1.7. STEP 4 — Token Request (การขอ Access Token) + +กระเป๋าเอกสารดิจิทัลส่งคำขอ: + +```http +POST https://issuer.dopa.go.th/token +Content-Type: application/x-www-form-urlencoded +DPoP: eyJhbG...rand... ← เมื่อ Thai profile กำหนด + +grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code +&pre-authorized_code=pac-a1b2c3d4e5f6 +&tx_code=512847 +``` + +ตัวอย่าง HTTP นี้ละรายละเอียดการส่ง Wallet Attestation และ Client Attestation PoP; ให้นำส่งตามรูปแบบที่กำหนดใน OID4VCI Appendix E และมาตรฐาน client authentication ที่อ้างถึง โดยไม่อนุมานชื่อ header หรือ field จากตัวอย่างนี้. + +Authorization Server ตรวจ Wallet Attestation ที่ลงนามโดยผู้ให้บริการที่ตนเชื่อถือ และตรวจ Client Attestation PoP ตามมาตรฐานที่อ้างถึง. จากนั้นตรวจ `pre_authorized_code`, `tx_code` และการใช้ code ซ้ำตาม OID4VCI. Thai profile กำหนด trust source และการตรวจสถานะ Wallet เพิ่มเติม. ตรวจ DPoP และผูก Access Token กับ DPoP public key เฉพาะเมื่อ profile กำหนดให้ใช้ DPoP. + +**คำตอบ (Response) — ตัวอย่าง Bearer ตาม OID4VCI Final:** +```json +{ + "access_token": "eyJhbGci...token...", + "token_type": "Bearer", + "expires_in": 300 +} +``` + +เมื่อใช้ DPoP token binding ตาม Thai profile ให้ใช้ `token_type: "DPoP"` และทำตาม RFC 9449. + +**ตัวอย่าง Nonce Endpoint ตาม OID4VCI Final §7 (ค่าตัวอย่าง):** + +```http +POST /nonce HTTP/1.1 +Host: issuer.dopa.go.th +Content-Length: 0 + +HTTP/1.1 200 OK +Content-Type: application/json +Cache-Control: no-store + +{ + "c_nonce": "c_nonce-returned-by-issuer" +} +``` + +--- + +### D.1.8. STEP 5 — Credential Request (การขอรับเอกสารรับรองดิจิทัล) + +Nonce Endpoint เป็น optional. หาก Issuer มี endpoint นี้ Wallet MUST ส่ง HTTP POST ไปยัง URL ใน Credential Issuer Metadata โดยไม่ต้องใช้ Access Token; Issuer ตอบด้วย HTTP 2xx และ JSON ที่มี `c_nonce` พร้อม `Cache-Control: no-store`. OID4VCI ไม่กำหนด `c_nonce_expires_in` ใน response นี้. หาก Issuer มี Nonce Endpoint Wallet MUST ใส่ `c_nonce` ใน `nonce` ของ proof. Wallet สร้าง Holder Key Pair แล้วลงนาม JWT Proof ด้วย private key; public key ที่จะผูกกับ Credential อยู่ใน JOSE header โดยเลือกใช้หนึ่งใน `jwk`, `kid` หรือ `x5c`. ตาม Appendix F.1, proof MUST มี `typ: "openid4vci-proof+jwt"`, `alg`, `aud` เป็น Credential Issuer Identifier และ `iat`. สำหรับ Pre-Authorized Code Flow แบบ anonymous ไม่ส่ง `iss` ใน proof JWT. ตัวอย่างนี้ใช้ `jwk` และแสดง claims ที่ถอดรหัสแล้ว: + +```json +{ + "header": { + "alg": "ES256", + "typ": "openid4vci-proof+jwt", + "jwk": { "kty": "EC", "crv": "P-256", "x": "wallet_pub_x", "y": "wallet_pub_y" } + }, + "payload": { + "aud": "https://issuer.dopa.go.th", + "iat": 1750000100, + "nonce": "c_nonce-returned-by-issuer" + } +} +``` + +> ตัวอย่างนี้ใช้ Pre-Authorized Code Flow แบบ anonymous จึงไม่ใส่ `iss` ใน proof JWT. ค่า `nonce` ตรงกับ `c_nonce` ที่ Issuer ส่งกลับจาก Nonce Endpoint. + +เมื่อ access token ผูกกับ DPoP key, กระเป๋าเอกสารดิจิทัลสร้าง DPoP Proof ใหม่สำหรับ endpoint `/credential` แล้วส่ง Credential Request: + +```http +POST https://issuer.dopa.go.th/credential +Authorization: Bearer *** ← หรือ DPoP *** เมื่อ access token ผูกกับ DPoP key +DPoP: eyJhbG...rand...DPoP_Proof... ← ส่งเมื่อ access token ผูกกับ DPoP key +Content-Type: application/json + +{ + "credential_configuration_id": "ThaiNationalIDCredential-config", + "proofs": { + "jwt": [ + "eyJhbG...rand..." + ] + } +} +``` + +--- + +### D.1.9. TC-2 — การตรวจ Client Attestation ใน OID4VCI + +กระเป๋าเอกสารดิจิทัลส่ง Wallet Attestation ตาม OID4VCI Appendix E พร้อม Client Attestation PoP ใน Token Request ของผู้ออกเอกสาร [REF-OID4VCI] โดยไม่ส่งใน Credential Request + +ผู้ออกเอกสารต้องตรวจตามลำดับดังนี้ + +1. แยก Wallet Provider ID และกุญแจตรวจสอบจาก Wallet Attestation +2. ค้นหา Wallet Provider ใน Wallet Provider Trusted List ที่ตรวจลายมือชื่อและความสดใหม่แล้ว +3. ยืนยันว่าบทบาทเป็น Wallet Provider กุญแจตรง และสถานะองค์กรกับบริการเป็น `active` +4. ตรวจสถานะของกระเป๋า (wallet instance) ตาม Thai profile +5. ตรวจลายมือชื่อ Wallet Attestation และ Client Attestation PoP + +หากข้อใดไม่ผ่าน ผู้ออกเอกสารต้องไม่ออก Access Token + +Thai profile ต้องกำหนดโครงสร้าง Trusted List ฟิลด์ที่ใช้ค้นหา และวิธีตรวจสถานะก่อนใช้งานจริง + +--- + +### D.1.10. STEP 6 — Issue VC (การออกเอกสารรับรองดิจิทัล) + +ผู้ออกเอกสารสร้าง SD-JWT VC ตามมาตรฐาน IETF SD-JWT VC (`dc+sd-jwt`) [REF-SDJWT] ซึ่งประกอบด้วยโครงสร้างสามชั้น: + +**JOSE Header (ชั้นที่ 1):** +```json +{ + "typ": "dc+sd-jwt", + "alg": "ES256", + "kid": "https://issuer.dopa.go.th#key-2025-1" +} +``` + +**JWT Payload (ชั้นที่ 2 — IETF SD-JWT VC `dc+sd-jwt`):** ตัวอย่างนี้แสดง claims ที่ถอดรหัสแล้วเพื่ออธิบายโครงสร้างเท่านั้น ไม่ใช่ compact SD-JWT ที่ลงลายมือชื่อแล้ว และไม่ใช่ W3C VC Data Model object; claims อยู่ที่ระดับบนสุดตาม Thai credential type (`vct`). `iat`, `exp`, `cnf` และ `status` แสดงเป็นตัวอย่างของ profile นี้ ไม่ได้หมายความว่า SD-JWT VC กำหนดให้ทุก credential ต้องมี claims เหล่านี้ +```json +{ + "iss": "https://issuer.dopa.go.th", + "iat": 1749945600, + "exp": 1907712000, + "vct": "https://credentials.dopa.go.th/ThaiNationalIDCredential", + "cnf": { "jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" } }, + "status": { + "status_list": { "idx": 42, "uri": "https://status.dopa.go.th/statuslist/1" } + }, + "ชื่อ": "สมชาย ใจดี", + "เลขบัตร": "1234567890123", + "วันเกิด": "1990-01-15", + "_sd_alg": "sha-256", + "_sd": [ + "X9yH3mK2vP...digest_ที่อยู่", + "aB7cD4eF1g...digest_รูปภาพ" + ] +} +``` + +| กรณี | ความหมาย | คำตอบ | +|------|---------|-------| +| **Immediate** | ออกเอกสารทันที (กรณีปกติ) | `{ "credential": "eyJ...SD-JWT...", "notification_id": "notif-dopa-456" }` โดย `notification_id` เป็น Optional | +| **Deferred** | ต้องรอ (เช่น ต้องตรวจสอบเพิ่มเติม) | คำตอบแรก: `{ "transaction_id": "txn-dopa-456" }`; เมื่อ Credential พร้อม Deferred Credential Response อาจมี `notification_id` ซึ่งเป็น Optional | + +Issuer สร้าง `notification_id` เพื่อระบุ issuance flow แล้วส่งให้ Wallet ใน Credential Response สำหรับการออกทันที หรืออาจส่งใน Deferred Credential Response สำหรับการออกแบบ deferred [REF-OID4VCI, §§8.3, 9.2] Wallet ต้องเก็บค่านี้และส่งค่าเดิมใน Notification Request หลังจัดเก็บ Credential สำเร็จ [REF-OID4VCI, §11.1] Wallet ไม่ได้สร้างค่านี้เอง หาก Issuer ไม่ส่ง `notification_id` มา Wallet ก็ไม่มีค่าที่จะใช้ส่ง Notification Request + +**Deferred Issuance — Polling Parameters:** + +> **[ค่าที่ยังไม่ได้กำหนดอย่างเป็นทางการ]** สพธอ. ยังมิได้ประกาศค่าที่แน่นอนสำหรับ polling interval และ timeout — ค่าด้านล่างเป็นแนวทางชั่วคราว + +| พารามิเตอร์ | ค่าแนะนำชั่วคราว | +|-------------|-------------------| +| Polling interval ขั้นต่ำ | ทุก 5 วินาที | +| Timeout รวม | 10 นาที | +| Max retry | ไม่จำกัด ภายใน timeout | +| Error — ตรวจสอบไม่ผ่าน | `credential_request_denied` + `error_description` | +| Error — transaction หมดอายุ | `invalid_transaction_id` — ต้องเริ่ม flow ใหม่ | + +--- + +### D.1.11. STEP 6.5 — TC-3 + TC-4: การตรวจสอบผู้ออกเอกสารและสถานะเอกสาร + +ดำเนินการโดยกระเป๋าเอกสารดิจิทัลภายหลังได้รับ SD-JWT VC: + +| ขั้นตอน | หยิบค่าจาก | ตรวจสอบกับ | ผ่านเมื่อ | +|---------|-----------|-----------|----------| +| **[26]** แยก claims | JWT payload | — | decode สำเร็จ | +| **[27]** Trusted List lookup | `iss` | บริการ Issuer ใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` และกฎจับคู่ตัวระบุตาม Thai profile | พบองค์กรและบริการที่ตรวจสถานะ `active` ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| **[27.1]** Issuer/identifier/type | Claims ตามรูปแบบ Credential | กฎจับคู่ตัวระบุและขอบเขตสิทธิ์ Issuer ใน Thai profile | ตรวจได้ตาม profile ของ format; ห้ามสมมติมี W3C VC body ใน SD-JWT VC | +| **[27.2]** W3C VCDM checks | `issuer.id`, `id`, `@context`, `type` | W3C VC body | ใช้เฉพาะเมื่อ Credential เป็น W3C VCDM | +| **[27.3]** SD-JWT VC checks | `iss`, `vct` และ claims ที่ profile กำหนด | Thai credential profile | ตรวจ issuer/type/claims ตาม profile; ห้ามถือว่า `sub`, `iat` หรือ `status` มีอยู่เสมอ | +| **[28]** GET JWKS | `credential_issuer` จาก Credential Issuer Metadata ที่ตรวจผูกกับ Issuer Trusted List แล้ว | — | HTTP 200 | +| **[29]** รับ JWKS | JWKS response | — | parse สำเร็จ | +| **[29.1]** kid match | `kid` จาก JOSE header | JWKS `keys[].kid` | พบ key → ได้ `publicKeyJwk` | +| **[30]** verify signature | SD-JWT signature | `publicKeyJwk` | verify สำเร็จ | +| **[30.1]** temporal/subject claims | `nbf`, `exp`, `sub` เมื่อมี | mapping และเงื่อนไขใน Thai credential profile | ตรวจเวลาตาม JWT claims; ตรวจ `sub`/binding เฉพาะเมื่อ profile กำหนดและ claim มีอยู่ | +| **[30.2]** validity window | JWT time claims หรือ profile-defined mapping | เวลาปัจจุบัน | token ยังอยู่ในช่วงใช้ได้; `validFrom`/`validUntil` ใช้เฉพาะเมื่อ format/profile กำหนด | +| **[30.3]** GET statuslist | `status.status_list.uri` | — | HTTP 200 | +| **[30.4]** รับ statuslist+jwt | response | — | parse สำเร็จ | +| **[30.5]** verify statuslist | Status List JWT (`typ: statuslist+jwt`) | key ที่ resolve ได้ตาม Status List trust model ใน Thai profile | ตรวจ claims/ลายมือชื่อผ่าน และ `sub` ตรงกับ status-list URI | +| **[30.6]** validity statuslist | `iat` ที่ REQUIRED; `exp` หากมี; `ttl` หากใช้ cache | now และ Thai-profile freshness policy | ตรวจ `iat`; ปฏิเสธเมื่อ `exp` มีค่าและหมดอายุ; cache ตาม `ttl` เมื่อมี | +| **[30.7]** decode status | `lst`, `bits`, `status_list.idx` | `bits` ที่ Status List JWT ระบุ | อ่าน status slot ตามความกว้าง `bits`; ยอมรับ `0x00 (VALID)` หรือสถานะที่ Thai profile อนุญาต [REF-TSL, draft-ietf-oauth-status-list-21 §4.1, §7.1] | + +--- + +### D.1.12. STEP 7 — Notification (การแจ้งเตือนการรับเอกสาร, Optional) + +กระเป๋าเอกสารดิจิทัล MAY ส่งการแจ้งเตือนไปยังผู้ออกเอกสารเพื่อยืนยันว่าได้รับเอกสารแล้ว: + +```http +POST https://issuer.dopa.go.th/notification +Authorization: Bearer *** +Content-Type: application/json + +{ + "notification_id": "notif-dopa-456", + "event": "credential_accepted" +} +``` + +ผู้ออกเอกสารตอบกลับ `204 No Content` — เป็นการบันทึก audit log เสริม มิได้ส่งผลกระทบต่อการใช้งานเอกสารรับรอง + +--- + +## D.2. ข้อเสนอนโยบาย DPoP แยกตามระดับ IAL/AAL + +{/* TODO: ข้อเสนอนโยบาย DPoP แยกตามระดับ IAL/AAL ด้านล่างนี้ยังไม่ได้รับการยืนยัน (To be confirmed) — รอมติคณะทำงานก่อนนำไปใช้ */} + +DPoP เป็นกลไกป้องกัน Access Token (แกน Authentication) จึงผูกกับระดับ **AAL** เป็นหลัก โดยมี IAL เป็นบริบทของความอ่อนไหวของข้อมูล: + +| ระดับ IAL (issue) | AAL เป้าหมาย | tx_code (OTP) | Wallet Attestation | ข้อเสนอ DPoP | เหตุผลประกอบข้อเสนอ | +|------------------|:-----------:|:-------------:|:------------------:|:------------:|----------------------| +| **IAL1** | AAL1 | ⚪ ไม่บังคับ | ⚪ ไม่บังคับ | ⚪ ไม่บังคับ — Bearer ใช้ได้ | ใช้เป็น baseline; ต้องประเมินความเสี่ยงของข้อมูลและธุรกรรม | +| **IAL2.1–2.2** | AAL2 | ✅ แนะนำ | 🟡 แนะนำ | 🟡 พิจารณาใช้ | ข้อมูลสำคัญ ควรประเมินมาตรการป้องกัน | +| **IAL2.3** | AAL2 (issue), AAL3 (present) | ✅ บังคับ | ✅ บังคับ | 🟡 พิจารณาใช้ตามผลประเมินความเสี่ยง | ข้อมูล identity หลัก | +| **IAL3** | AAL3 | ✅ บังคับ | ✅ บังคับ | 🟡 พิจารณาใช้ร่วมกับ key ที่เหมาะสม | ข้อมูลอ่อนไหวสูง | + +> **หมายเหตุ:** ตารางข้างต้นเป็นนโยบายที่ยังไม่ได้รับการยืนยัน (To be confirmed) รอมติคณะทำงาน VC ก่อนนำไปใช้จริง + +--- + +## D.3. การตรวจสอบที่ผู้ออกเอกสาร MUST ดำเนินการใน STEP 4 ตาม IAL/AAL + +| การตรวจสอบ | IAL1/AAL1 | IAL2.1–2.2/AAL2 | IAL2.3/AAL2-3 | IAL3/AAL3 | +|------------|:---------:|:---------------:|:-------------:|:---------:| +| `pre_authorized_code` valid + single-use | ✅ | ✅ | ✅ | ✅ | +| `tx_code` (OTP) ถูกต้อง | ⚪ | ✅ | ✅ | ✅ | +| Wallet Attestation ถูกต้อง | ⚪ | 🟡 | ✅ | ✅ | +| DPoP-Proof header มีและถูกต้อง | ⚪ | 🟡 | ✅ | ✅ | +| DPoP `jti` ไม่ซ้ำ (replay) | ⚪ | 🟡 | ✅ | ✅ | +| Hardware-backed key (SE/StrongBox) | ⚪ | ⚪ | ✅ (present) | ✅ | + +--- + +{/* METADATA (agent-only — not rendered to readers) +## อ้างอิง + +- [REF-OID4VCI] OpenID Foundation, "OpenID for Verifiable Credential Issuance 1.0 Final" — https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html +- [REF-SDJWT] IETF, “SD-JWT-based Verifiable Digital Credentials (SD-JWT VC),” draft-ietf-oauth-sd-jwt-vc-19, 31 August 2026 (Internet-Draft; status as of 1 October 2026: Waiting for AD Go-Ahead) — https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/ +- [REF-TSL] IETF, “Token Status List (TSL),” draft-ietf-oauth-status-list-21, 21 June 2026 (Internet-Draft; status as of 1 October 2026: RFC Editor — Awaiting First editor) — https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ +- [REF-DPOP] IETF RFC 9449, "OAuth 2.0 Demonstrating Proof-of-Possession" +- [REF-ARF] European Commission, "EUDI Architecture and Reference Framework v2.9.0" +- [REF-MASA-158] MASA-158 — Merge Full Flow from trust-guide into ARF, STEP 1.5 → 2.5 (2026-08-07) +- [REF-TRUSTLIST-CDN] คณะทำงาน VC, "แนวทางกระจาย Trustlist (LoTE) ผ่าน File/Edge CDN — ไม่ใช้ RabbitMQ", เวอร์ชัน 1.10, 4 กันยายน 2569 — [research/41](../research/41-trustlist-serving-filecdn-etag-binary.md) + +--- +*/} + +**การนำทาง:** [⬅️ กลับไปบทที่ 8 — Implementation Guidelines](08-implementation-guidelines.md) · [⬆️ สารบัญ ARF](README.md) · [ภาคผนวก 8.2 — OID4VP Full Flow ➡️](08.2-oid4vp-full-flow-detail.md) diff --git a/docs/thai-vc-arf/08.2-oid4vp-full-flow-detail.md b/docs/thai-vc-arf/08.2-oid4vp-full-flow-detail.md new file mode 100644 index 0000000..e9407eb --- /dev/null +++ b/docs/thai-vc-arf/08.2-oid4vp-full-flow-detail.md @@ -0,0 +1,403 @@ +--- +description: "ภาคผนวกบทที่ 8 — OID4VP Full Flow ทางเทคนิคโดยละเอียด — Thai VC ARF 2.0 DRAFT 0" +--- + +# ภาคผนวกบทที่ 8 — OID4VP Full Flow ทางเทคนิคโดยละเอียด + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารขยายเพิ่มเติมสำหรับบทที่ 8 Implementation Guidelines +> **เวอร์ชัน:** v1.1 +> **วันที่:** 7 สิงหาคม 2569 +> **แหล่งข้อมูล:** trust-guide/04-presentation-flow/02-full-flow/ (MASA-158) +> **เอกสารที่เกี่ยวข้อง:** [บทที่ 8 — Implementation Guidelines](08-implementation-guidelines.md) · [บทที่ 9 — Trust Model 3](09-trust-model-3.md) · [ภาคผนวก 8.1 — OID4VCI Full Flow](08.1-issuance-full-flow-detail.md) +*/} + +--- + +## วัตถุประสงค์ + +ภาคผนวกนี้จัดทำขึ้นเพื่ออธิบายรายละเอียดทางเทคนิคของกระบวนการแสดงและตรวจสอบเอกสารสำแดงดิจิทัลตามมาตรฐาน OID4VP (OpenID for Verifiable Presentations) [REF-OID4VP] แบบเต็มรูปแบบ (Full Flow) ตั้งแต่การเตรียมระบบ (SETUP) จนถึงการตรวจสอบสถานะเอกสาร พร้อมคำอธิบายระดับ field-by-field สำหรับสถาปนิกระบบ ผู้พัฒนาซอฟต์แวร์ และผู้ตรวจสอบความสอดคล้อง + +--- + +## E.0. แผนภาพ Sequence ฉบับสมบูรณ์ — Full Flow OID4VP (Step 1–6) + +แผนภาพต่อไปนี้แสดงภาพรวมของกระบวนการทั้งหมดตั้งแต่ SETUP จนถึงการตรวจสอบสถานะ: + +```mermaid +sequenceDiagram + autonumber + participant U as 👤 User + participant C as 📱 Holder + Wallet Instance
Key Store + participant W as 📱 Wallet (Holder) + participant V as 🏬 Verifier (RP) + participant TL as 🛡️ ETDA Trusted List
(signed by ETDA) + participant UR as 🔗 Universal DID Resolver + participant IR as 📋 Issuer Registry
(credential status endpoint) + + rect rgb(210, 240, 210) + note over W,TL: 🟢 [ETDA คุม] SETUP เมื่อมีธุรกรรม — ดึง ETDA Public Key + Trusted List + W->>TL: GET https://etda.or.th/.well-known/did.json + TL-->>W: ETDA DID Document + Public Key + W->>W: เก็บ ETDA Public Key ไว้ใน Local Storage + V->>TL: GET https://etda.or.th/.well-known/did.json + TL-->>V: ETDA DID Document + Public Key + V->>V: เก็บ ETDA Public Key ไว้ใน Local Storage + W->>TL: GET https://trustme.etda.or.th/.well-known/ver.txt (CDN, ไม่ต้อง API key) + TL-->>W: Trusted List ที่เกี่ยวข้องฉบับเต็ม (JWT) + ETag + W->>W: Verify JWT ด้วย ETDA Public Key + W->>W: เก็บไฟล์เต็ม + ETag + NextUpdate + V->>TL: GET https://trustme.etda.or.th/.well-known/iss.txt และ https://trustme.etda.or.th/.well-known/wp.txt (CDN, ไม่ต้อง API key) + TL-->>V: Trusted List ที่เกี่ยวข้องฉบับเต็ม (JWT) + ETag + V->>V: Verify JWT ด้วย ETDA Public Key + V->>V: เก็บไฟล์เต็ม + ETag + NextUpdate + end + + rect rgb(210, 230, 255) + Note over V: 📋 Step 1 — Prepare Authorization Request + V->>V: Generate nonce, state, request_id + V->>V: Build Request Object JWT incl. dcql_query (format: dc+sd-jwt) + V->>V: Sign with Verifier key + V->>V: Render QR + end + + rect rgb(210, 230, 255) + Note over W,V: 📱 Step 2 — Cross-device transfer + U->>V: Access RP website + V-->>U: Display QR + U->>W: Scan QR + end + + rect rgb(210, 230, 255) + Note over W,V: 📥 Step 2.5 — Fetch Request Object + W->>V: Request the Request Object + V-->>W: Signed Request Object JWT (with dcql_query) + end + + rect rgb(255, 225, 180) + Note over W,TL: 🟠 TC-1 — Verifier trust + DCQL scope + W->>W: Parse RO, Extract PK_v + W->>W: 🔎 L1: Verify TL signature + W->>W: 🔎 L2: Lookup PK_v in LoTE.TrustedEntitiesList[].TrustedEntityServices[] + W->>W: Verify RO sig, aud, exp/iat, nonce + W->>W: Check claims against Verifier scope in Thai profile + W->>W: Check purpose against Verifier scope in Thai profile + end + + rect rgb(210, 230, 255) + Note over W,U: ✅ Step 3 — DCQL local match + Consent + W->>W: Filter local SD-JWT VCs by format + vct_values + W->>W: Walk q.claims[].path natively + W->>U: Show consent UI (legal_name, purpose, claims) + U->>W: Approve, pick option, select claims + W->>C: Unlock holder + wallet instance keys + C-->>W: Authorized + end + + rect rgb(210, 230, 255) + Note over W,C: 🔐 Step 4 — Build SD-JWT Presentation + W->>W: Select disclosures matching claims path + W->>W: Concatenate: issuer_jwt~d1~d2~...~ + W->>W: Compute sd_hash = SHA-256 of presentation + W->>C: Sign KB-JWT with holder key + C-->>W: KB-JWT + W->>W: Assemble vp_token = issuer_jwt~d1~...~KB-JWT + opt Government Wallet (required) or private Wallet opts into TC-2 + W->>W: Use WIA issued by Wallet Provider + W->>C: Sign WIA-PoP with Wallet Instance key + C-->>W: wia_pop + end + end + + rect rgb(210, 230, 255) + Note over W,V: 📤 Step 5 — Authorization Response + W->>V: POST response_uri (direct_post) { vp_token, state } + opt Government Wallet (required) or private Wallet opts into TC-2 + W->>V: wallet_attestation + wallet_attestation_pop + end + Note over W,V: Government Wallet MUST send both, private Wallet MAY send both or omit both + V-->>W: 200 OK + end + + rect rgb(255, 225, 180) + Note over V,IR: 🟠 Step 6 — Verifier validates (TC-2 when applicable, TC-3, TC-4) + + Note over V: Determine Wallet type from trusted Provider profile, independently of WIA, reject VP if missing, stale, or unverifiable + alt Government Wallet, or private Wallet sends both attestations + Note over V,TL: ⓪ TC-2: Wallet Provider check + V->>V: Require and parse WIA → extract WP iss + PK_wp + V->>V: 🔎 L3: Lookup PK_wp in LoTE.TrustedEntitiesList[].TrustedEntityServices[] + V->>V: Verify WIA sig + WIA-PoP sig + nonce + jti + else Private Wallet sends neither attestation + V->>V: Skip TC-2, continue TC-3 and TC-4 + else Government evidence missing, private evidence partial, or any evidence invalid + V->>W: Reject VP + end + + Note over V: Per-id DCQL loop + V->>V: Split vp_token → issuer_jwt, disclosures[], KB-JWT + + Note over V,UR: ① KB-JWT — Holder binding + V->>V: Verify KB-JWT sig with issuer_jwt.cnf.jwk + V->>V: Verify KB-JWT.sd_hash matches + + Note over V,TL: ② TC-3: Issuer check + V->>UR: GET /1.0/identifiers/⟨issuer DID⟩ + UR-->>V: DID Resolution Result + V->>V: 🔎 L6: Dereference kid → publicKeyJwk (PK_iss) + V->>V: 🔎 L4: Lookup PK_iss in LoTE.TrustedEntitiesList[].TrustedEntityServices[] + V->>V: Check entity and service status + Issuer scope + V->>V: Verify issuer_jwt sig + disclosures hash check + V->>V: DCQL match against reconstructed_credential + + Note over IR: ③ TC-4: Status check + V->>IR: GET status.status_list.uri + IR-->>V: statuslist+jwt + V->>V: 🔎 L7: ตรวจ Referenced Token ก่อน แล้วอ่าน status slot ที่ idx ตาม bits = 0x00 (VALID) + end + + alt All checks pass + V-->>U: ✅ Service granted + else Any failure + V-->>W: 400 invalid_request + end +``` + +**คำอธิบายแต่ละขั้นตอนโดยสังเขป:** + +| ช่วง Step | ขั้นตอน | คำอธิบาย | +|-----------|---------|----------| +| **SETUP** | ดึง ETDA Public Key + Trusted List เมื่อมีธุรกรรม | Wallet และ Verifier ต่างดึง ETDA Public Key จาก `/.well-known/did.json` แล้วดึง Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จาก CDN เมื่อเกิดการเชื่อมต่อ โดยไม่ใช้ API key หรือ background polling | +| **Step 1** | Prepare Authorization Request | Verifier สร้าง nonce, state + Request Object JWT พร้อม dcql_query + ลงนาม + สร้าง QR | +| **Step 2** | Cross-device transfer | ผู้ใช้สแกน QR บนหน้าจอ Verifier ด้วย Wallet | +| **Step 2.5** | Fetch Request Object | Wallet ดึง Request Object JWT ฉบับเต็มจาก request_uri | +| **TC-1** | Wallet ตรวจ Verifier | Wallet ตรวจ PK_v ใน Trusted List + verify RO sig + ตรวจขอบเขต claims + วัตถุประสงค์ | +| **Step 3** | DCQL match + Consent | Wallet ค้นหา VC ในเครื่องที่ตรง DCQL → แสดง consent UI → ผู้ใช้อนุมัติ | +| **Step 4** | Build Presentation + WIA-PoP (ตามประเภท Wallet) | Wallet สร้าง SD-JWT presentation และ KB-JWT; Wallet รัฐบาลสร้าง WIA-PoP ส่วน Wallet เอกชนเลือกทำ TC-2 ได้ | +| **Step 5** | Authorization Response | Wallet ส่ง `vp_token` และ `state` ด้วย `direct_post`; Wallet รัฐบาล MUST ส่ง WIA และ WIA-PoP ส่วน Wallet เอกชน MAY ส่งทั้งคู่หรือไม่ส่งทั้งคู่ | +| **Step 6 ⓪** | TC-2: Wallet Provider check (เมื่อมีข้อกำหนด) | Verifier ตรวจ WIA และ WIA-PoP เมื่อ Wallet รัฐบาลส่งมา หรือ Wallet เอกชนเลือกส่ง | + +| **Step 6 ①** | KB-JWT — Holder binding | Verifier verify KB-JWT sig → ตรวจ sd_hash → ยืนยัน Holder ควบคุม key จริง | +| **Step 6 ②** | TC-3: Issuer check | Verifier resolve DID → lookup PK_iss ใน Trusted List → verify issuer sig + disclosures | +| **Step 6 ③** | TC-4: Status check | Verifier ดึง statuslist+jwt → อ่าน status ที่ idx ตามความกว้าง `bits` → ประมวลผลตามค่า | + +--- + +## E.1. คำอธิบายทุกขั้นตอนโดยละเอียด + +### E.1.1. SETUP — การเตรียมระบบก่อนเริ่มใช้งาน + +ก่อนเริ่มใช้งาน กระเป๋าเอกสารดิจิทัล (Wallet) และผู้ตรวจสอบเอกสาร (Verifier) MUST ดำเนินการเช่นเดียวกับ SETUP ในกระบวนการออกเอกสาร (ภาคผนวก 8.1): + +1. ดึง ETDA Public Key จาก `GET https://etda.or.th/.well-known/did.json` เพื่อใช้ตรวจสอบลายมือชื่อ Trusted List +2. ดึง Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จาก CDN โดย Wallet ดึง `https://trustme.etda.or.th/.well-known/ver.txt` และ Verifier ดึง `https://trustme.etda.or.th/.well-known/iss.txt` กับ `https://trustme.etda.or.th/.well-known/wp.txt` ทั้งหมดไม่ใช้ API key, delta หรือไฟล์ CBOR +3. จัดเก็บไฟล์เต็มและค่า `ETag` ใน Local Storage แล้วส่ง `If-None-Match` เมื่อเกิดการใช้งานครั้งถัดไป [REF-TRUSTLIST-CDN] + +**ข้อแตกต่างสำคัญ:** ใน OID4VP ผู้ดึง Trusted List คือ **กระเป๋าเอกสารดิจิทัลและผู้ตรวจสอบเอกสาร** (ใน OID4VCI คือ กระเป๋าเอกสารดิจิทัลและผู้ออกเอกสาร) + +--- + +### E.1.2. Step 1 — ผู้ตรวจสอบเอกสารเตรียม Authorization Request + +ผู้ตรวจสอบเอกสารดำเนินการดังนี้: + +1. **สร้างค่าสุ่ม** — `nonce` (ป้องกัน replay), `state` (เก็บ transaction context), `request_id` +2. **สร้าง Request Object JWT** — บรรจุ `dcql_query` ระบุ: + - `format: "dc+sd-jwt"` — credential format ที่รองรับ + - `vct_values` — ประเภท credential ที่ต้องการ + - `claims[].path` — path ของ claims ที่ขอ + - `credential_sets` — เงื่อนไข credential ที่จำเป็น +3. **ลงนาม** Request Object ด้วย private key ของผู้ตรวจสอบเอกสาร +4. **สร้าง QR Code** — บรรจุ `request_uri` เพื่อให้กระเป๋าเอกสารดิจิทัลดึง Request Object ฉบับเต็ม + +--- + +### E.1.3. Step 2 — การโอนข้ามอุปกรณ์ (Cross-device Transfer) + +1. ผู้ใช้เข้าถึงบริการของผู้ตรวจสอบเอกสาร (เช่น เว็บไซต์) +2. ผู้ตรวจสอบเอกสารแสดง QR Code บนหน้าจอ +3. ผู้ใช้สแกน QR Code ด้วยกระเป๋าเอกสารดิจิทัล + +> **เหตุผล:** ใช้ cross-device flow — กระเป๋าเอกสารดิจิทัลอยู่คนละอุปกรณ์กับผู้ตรวจสอบเอกสาร QR Code เป็นสะพานเชื่อมสองอุปกรณ์โดยไม่ต้องมีการเชื่อมต่อเครือข่ายโดยตรง + +--- + +### E.1.4. Step 2.5 — การดึง Request Object ฉบับเต็ม + +กระเป๋าเอกสารดิจิทัลส่ง HTTP GET ไปยัง `request_uri` ที่ระบุใน QR Code — ผู้ตรวจสอบเอกสารตอบกลับด้วย Request Object JWT ฉบับเต็มที่ลงนามแล้ว + +> **เหตุผล:** QR Code มีขนาดจำกัด ไม่สามารถบรรจุ Request Object ทั้งหมดได้ จึงส่งเป็น URI แทน + +--- + +### E.1.5. TC-1 — กระเป๋าเอกสารดิจิทัลตรวจสอบผู้ตรวจสอบเอกสาร (ระหว่าง Step 2.5 และ Step 3) + +**ความสำคัญ:** นี่คือจุดควบคุมความปลอดภัยที่สำคัญที่สุดของฝั่งกระเป๋าเอกสารดิจิทัล MUST ผ่านก่อนส่งข้อมูลใด ๆ + +กระเป๋าเอกสารดิจิทัลดำเนินการ: + +1. **แยก PK_v** — จาก Request Object +2. **ตรวจสอบลายมือชื่อ Trusted List** — L1: verify Trusted List signature ด้วย ETDA public key +3. **ค้นหา PK_v ใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]`** — L2: ต้องพบบริการ Verifier และยืนยันสถานะระดับองค์กรและบริการเป็น `active` ตาม Thai profile ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) +4. **หากไม่พบ** → หยุดดำเนินการทันที ไม่ส่งข้อมูลใด ๆ +5. **หากพบ** → ดำเนินการตรวจสอบต่อ: + - ตรวจสอบลายมือชื่อ Request Object ด้วย PK_v + - `aud` ตรงกับ wallet ID + - `exp`/`iat` ยังไม่หมดอายุ + - `nonce` มีความยาว ≥ 128-bit (การตรวจสอบเชิงโครงสร้าง) + - **ขอบเขต claims:** `dcql_query.claims[].path` ต้องอยู่ในขอบเขตที่ Thai profile ของบริการ Verifier อนุญาต + - **วัตถุประสงค์:** `purpose` ต้องอยู่ในขอบเขตที่ Thai profile ของบริการ Verifier อนุญาต; JWT ตัวอย่างยังไม่มีฟิลด์นี้ จึงต้องปฏิเสธหากตรวจไม่ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +--- + +### E.1.6. Step 3 — การจับคู่ DCQL ในเครื่องและการขอความยินยอม + +กระเป๋าเอกสารดิจิทัลดำเนินการ: + +1. **DCQL Match** — สำหรับแต่ละ query ใน `dcql_query.credentials`: + - กรอง credential ในเครื่องด้วย `format == "dc+sd-jwt"` และ `vct_values` + - เดินตาม `claims[].path` ในแต่ละ credential เพื่อหา disclosure ที่ตรง +2. **ประเมิน credential_sets** — ตรวจสอบว่า credential ในเครื่องตอบสนองเงื่อนไขที่จำเป็นได้หรือไม่ +3. **แสดง Consent UI** — แจ้งผู้ใช้: + - ผู้ตรวจสอบเอกสารคือหน่วยงานใด (`legal_name` จาก Trusted List) + - วัตถุประสงค์การขอข้อมูล (`purpose`) + - Claims ที่จะเปิดเผย +4. **ผู้ใช้ตัดสินใจ** — เลือกตัวเลือก (หากมีหลายทาง) และเลือก claims ที่ประสงค์จะเปิดเผย +5. **ปลดล็อกกุญแจ** — ใช้ Biometric/PIN เพื่อปลดล็อก holder key และ wallet instance key + +--- + +### E.1.7. Step 4 — การสร้าง SD-JWT Presentation และ WIA-PoP (ตามประเภท Wallet) + +สำหรับแต่ละ credential ที่ผู้ใช้เลือก: + +1. **เลือก Disclosures** — เก็บเฉพาะ disclosures ที่ตรงกับ claims path ที่ผู้ตรวจสอบเอกสารขอ +2. **ประกอบ Presentation:** `issuer_jwt~disclosure_1~disclosure_2~...~` +3. **คำนวณ sd_hash:** `SHA-256(presentation_string)` (รวม `~` ตัวสุดท้าย) +4. **สร้าง KB-JWT (Key Binding JWT)** — ลงนามด้วย holder key: + - Header: `typ: "kb+jwt"` + - Payload: `iss=holder`, `aud=client_id`, `nonce=⟨nonce จากผู้ตรวจสอบ⟩`, `sd_hash`, `iat` +5. **ประกอบขั้นสุดท้าย:** `issuer_jwt~disclosure_1~...~KB-JWT` → `vp_token[id]` + +**Wallet รัฐบาล MUST สร้าง WIA-PoP** — Proof of Possession ลงนามด้วย wallet instance key เมื่อใช้ TC-2: +- `iss = wallet instance ID`, `aud = client_id`, `nonce = ⟨nonce จากผู้ตรวจสอบ⟩`, `jti`, `iat`, `exp` + +Wallet เอกชน MAY เลือกใช้ TC-2 โดยส่ง WIA และ WIA-PoP ครบคู่ หรือไม่ส่งทั้งคู่ก็ได้ Wallet เอกชนที่ไม่เลือกใช้ TC-2 ไม่ต้องส่ง WIA หรือ WIA-PoP + +> **หมายเหตุ:** ตามมาตรฐาน IETF SD-JWT VC (`dc+sd-jwt`) ไม่ต้องมี JWT-VP ห่ออีกชั้น — KB-JWT ทำหน้าที่ Holder Binding ในตัว + +--- + +### E.1.8. Step 5 — Authorization Response (`direct_post`) + +ตัวอย่างนี้ใช้ `direct_post` และแสดงค่าพารามิเตอร์ก่อน form-encoding ให้ตรงกับตัวอย่างในบท 5 §5.4.1: + +```json +{ + "vp_token": { + "transcript": ["eyJ0eXAiOiJkYytzZC1qd3QiLCJhbGciOiJFZERTQSJ9...LUp~WyIzanFjYjY3ejl3a3NybmR2Iiwgc3R1ZGVudCIsICJOYXR0YXBvbmcgTWV0aGF3b24iXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9...ziRjjF8Z3V5QkHEhZTFplqnqfk2BxEtZDA"] + }, + "state": "8c17d20f4a936be105fd7c28a6403b91" +} +``` + +Wallet ส่ง Authorization Response ไปยัง `response_uri` ด้วย HTTP POST และ `Content-Type: application/x-www-form-urlencoded` โดย serialize ค่า `vp_token` เป็น JSON string แล้ว form-encode ค่าพารามิเตอร์ ตาม OID4VP §8.2 [REF-OID4VP]. เมื่อส่งตาม Thai profile ให้ส่ง `wallet_attestation` และ `wallet_attestation_pop` เป็นพารามิเตอร์เพิ่มเติมในคำขอเดียวกัน ไม่ใช่สมาชิกของ `vp_token`. + +**ส่วนขยาย Thai profile:** ตัวอย่างข้างต้นแสดงพารามิเตอร์ OID4VP ตามรูปแบบเดียวกับบท 5 §5.4.1 สำหรับ Wallet รัฐบาล Thai profile กำหนดให้เพิ่ม `wallet_attestation` และ `wallet_attestation_pop` ใน Authorization Response ส่วน Wallet เอกชน MAY ส่งทั้งสองค่า หรือไม่ส่งทั้งสองค่า ฟิลด์เหล่านี้เป็นส่วนขยายของ profile ไม่ใช่พารามิเตอร์มาตรฐาน OID4VP + +--- + +### E.1.9. Step 6 — ผู้ตรวจสอบเอกสารดำเนินการตรวจสอบ + +ผู้ตรวจสอบเอกสารค้นหา transaction context จาก `state` และตรวจประเภท Wallet จากข้อมูล Wallet Provider ที่เชื่อถือได้ ซึ่ง Thai profile ต้องกำหนดวิธีประกาศและตรวจประเภทดังกล่าวโดยไม่ต้องอาศัย WIA ในกรณีที่ Wallet เอกชนไม่ส่ง WIA หากไม่มีข้อมูลประเภท Wallet ข้อมูลไม่สดใหม่ หรือตรวจประเภทไม่ได้ ให้ปฏิเสธ VP จากนั้นดำเนินการตรวจสอบตามลำดับ: + +#### ⓪ TC-2 — การตรวจสอบผู้ให้บริการกระเป๋า + +เมื่อใช้ TC-2 ต้องมีหลักฐานครบทั้งคู่ คือ WIA และ WIA-PoP: WIA ยืนยัน Wallet Provider/Wallet Unit ส่วน WIA-PoP แสดงการครอบครองกุญแจที่ผูกกับ WIA ในธุรกรรมปัจจุบัน ตาม Thai profile หากเป็น Wallet รัฐบาล Wallet MUST ส่งหลักฐานครบทั้งคู่; หากขาดรายการใดรายการหนึ่ง ให้ปฏิเสธ VP หากเป็น Wallet เอกชน Wallet MAY เลือกใช้ TC-2 โดยส่งหลักฐานครบทั้งคู่ หรือไม่ส่งทั้งคู่; หากไม่ส่งให้ข้าม TC-2 และตรวจ TC-3/TC-4 ต่อ แต่หากส่งมาเพียงรายการเดียว ให้ปฏิเสธ VP + +เมื่อทำ TC-2 ให้ดำเนินการดังนี้: + +1. แยก WIA → ได้ `iss` (WP ID) + `PK_wp` +2. **L3:** ค้นหา PK_wp ในบริการ Wallet Provider ของ `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` [research/42](../research/42-trusted-list-lote-jwt-format.md) +3. หากไม่พบ → `400 untrusted_wallet_provider` +4. หากพบ: + - ตรวจสอบลายมือชื่อ WIA ด้วย PK_wp → `iat ≤ now < exp` + - ตรวจสอบลายมือชื่อ WIA-PoP ด้วย `WIA.cnf.jwk` → `iss == WIA.sub`, `aud == client_id`, `nonce` ตรง + - Mark `jti` consumed (ป้องกัน replay) + +การตรวจประเภท Wallet ต้องใช้ข้อมูลที่ Verifier ตรวจสอบได้จากแหล่งที่เชื่อถือได้ และเป็นอิสระจาก WIA เพื่อใช้ตัดสินกรณีที่ WIA ไม่มี ทั้งนี้ หากแหล่งข้อมูลขัดข้อง ข้อมูลขาดหาย หรือไม่ผ่านการตรวจสอบ ผู้ตรวจต้องปฏิเสธ VP โดยไม่ถือว่าการไม่มี WIA เป็นกรณี Wallet เอกชน + +#### ① KB-JWT — Holder Binding (SD-JWT native) + +สำหรับแต่ละ credential ใน `vp_token`: + +1. แยก `vp_token[id]` ด้วย `~` → `issuer_jwt`, `disclosures[]`, `KB-JWT` +2. จาก `issuer_jwt`: ดึง `cnf.jwk` (holder key) + `iss` (issuer DID) + `kid` +3. ตรวจสอบลายมือชื่อ KB-JWT ด้วย `issuer_jwt.cnf.jwk`: + - `aud == client_id` + - `nonce == issued_nonce` + - `iat` เป็นเวลาปัจจุบัน +4. คำนวณ `sd_hash` ใหม่จาก `issuer_jwt~d1~d2~...~` → ตรงกับ `KB-JWT.sd_hash` +5. Mark nonce + KB-JWT `jti` consumed + +#### ② TC-3 — การตรวจสอบผู้ออกเอกสาร + +1. **L6:** แก้ไขปัญหา issuer DID ผ่าน Universal Resolver → dereference `kid` ใน `didDocument.assertionMethod[]` → ได้ `publicKeyJwk` (PK_iss) +2. **L4:** ค้นหา PK_iss ในบริการ Issuer ของ `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` [research/42](../research/42-trusted-list-lote-jwt-format.md) +3. หากไม่พบ → `400 untrusted_issuer (key)` +4. หากพบ: + - สถานะระดับองค์กรและบริการต้องเป็น `active` ตาม Thai profile + - `vct` ต้องอยู่ในขอบเขตประเภท VC ที่ Issuer ได้รับอนุญาตตาม Thai profile + - หากต้องตรวจช่วงเวลากุญแจหรือ DID method ต้องมีข้อมูลดังกล่าวใน Thai profile ที่ประกาศใช้; หากไม่มี ให้ปฏิเสธ +5. ตรวจสอบลายมือชื่อ `issuer_jwt` ด้วย PK_iss → `nbf ≤ now ≤ exp` +6. ตรวจสอบ disclosures: `hash(d)` ∈ `issuer_jwt._sd` → parse `[salt, claim_name, claim_value]` → build `reconstructed_credential` +7. DCQL match กับ reconstructed credential → claims path มีค่า และ value ตรง (หากระบุ) + +#### ③ TC-4 — การตรวจสอบสถานะเอกสาร + +หาก `issuer_jwt` มี `status.status_list`: + +1. อ่าน `uri` และ `idx` จาก `status.status_list` +2. GET `uri` (Accept: `application/statuslist+jwt`) → statuslist+jwt +3. ตรวจสอบ statuslist JWT: + - `typ == "statuslist+jwt"` + - `sub == URI` ของ Status List + - `now > iat`, `now < exp` + - ลงนามโดย PK_iss (จาก L6) +4. ประมวลผล Referenced Token ก่อน รวมทั้งลายมือชื่อและ `exp`; token ที่ไม่ผ่านหรือหมดอายุต้องปฏิเสธ แม้ status จะเป็น `0x00`. +5. Decode `lst`: base64url → DEFLATE ตามรูปแบบ ZLIB → อ่านค่า status ที่ `idx` ตามความกว้าง `bits` [REF-TSL, draft-ietf-oauth-status-list-21 §4.1]. +6. ตีความ: `0x00 = VALID` / `0x01 = INVALID` / `0x02 = SUSPENDED`; `0x03 = rotated` ใช้ได้เฉพาะเมื่อ Thai profile กำหนด semantics และ processing rules ไว้; ปฏิเสธค่า status ที่ไม่รู้จักหรือไม่รองรับ [REF-TSL, draft-ietf-oauth-status-list-21 §7.1]. + +--- + +## E.2. สรุปจุดตรวจ L1–L7 ใน Full Flow OID4VP + +| จุดตรวจ | ผู้ดำเนินการตรวจ | สิ่งที่ตรวจ | อ้างอิง Trusted List | +|:-------:|-----------------|-----------|:-----------------:| +| **L1** | Wallet / Verifier | ตรวจสอบลายมือชื่อ JWT ของ Trusted List ด้วย ETDA public key | ETDA DID Document | +| **L2** | Wallet | ค้นหา Verifier PK ในบริการที่เกี่ยวข้อง | `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` | +| **L3** | Verifier | ค้นหา Wallet Provider PK ในบริการที่เกี่ยวข้อง เมื่อทำ TC-2 | `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` | +| **L4** | Verifier | ค้นหา Issuer PK ในบริการที่เกี่ยวข้อง | `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` | +| **L6** | Verifier | Dereference kid ใน DID Document → publicKeyJwk | DID Resolver | +| **L7** | Verifier | Decode Status List status slot | Issuer Status List | + +> **หมายเหตุ:** L5 (reserved สำหรับ Wallet Attestation) — ตรวจสอบ WUA Status List แบบ privacy-preserving [REF-CIR-WUA] เมื่อ Wallet ส่ง WIA และ TC-2 ทำงาน + +--- + +{/* METADATA (agent-only — not rendered to readers) +## อ้างอิง + +- [REF-OID4VP] OpenID Foundation, "OpenID for Verifiable Presentations 1.0 Final" — https://openid.net/specs/openid-4-verifiable-presentations-1_0.html +- [REF-SDJWT] IETF, “SD-JWT-based Verifiable Digital Credentials (SD-JWT VC),” draft-ietf-oauth-sd-jwt-vc-19, 31 August 2026 (Internet-Draft; status as of 1 October 2026: Waiting for AD Go-Ahead) — https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/ +- [REF-TSL] IETF, “Token Status List (TSL),” draft-ietf-oauth-status-list-21, 21 June 2026 (Internet-Draft; status as of 1 October 2026: RFC Editor — Awaiting First editor) — https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ +- [REF-DID-CORE] W3C, "Decentralized Identifiers (DIDs) v1.0", 19 July 2022 +- [REF-ARF] European Commission, "EUDI Architecture and Reference Framework v2.9.0" +- [REF-CIR-WUA] (EU) CIR 2024/2979, "Wallet Unit Attestation" +- [REF-MASA-158] MASA-158 — Merge OID4VP Full Flow from trust-guide into ARF (2026-08-07) +- [REF-TRUSTLIST-CDN] คณะทำงาน VC, "แนวทางกระจาย Trustlist (LoTE) ผ่าน File/Edge CDN — ไม่ใช้ RabbitMQ", เวอร์ชัน 1.10, 4 กันยายน 2569 — [research/41](../research/41-trustlist-serving-filecdn-etag-binary.md) + +--- +*/} + +**การนำทาง:** [⬅️ กลับไปบทที่ 8 — Implementation Guidelines](08-implementation-guidelines.md) · [⬆️ สารบัญ ARF](README.md) · [ภาคผนวก 8.1 — OID4VCI Full Flow ➡️](08.1-issuance-full-flow-detail.md) diff --git a/docs/thai-vc-arf/09-trust-model-3.md b/docs/thai-vc-arf/09-trust-model-3.md new file mode 100644 index 0000000..625f507 --- /dev/null +++ b/docs/thai-vc-arf/09-trust-model-3.md @@ -0,0 +1,325 @@ +--- +description: "9. Trust Model 3 — ระบบรายชื่อที่น่าเชื่อถือแบบกระจายศูนย์ด้วย DID (Decentralized Trusted List) — Thai VC ARF 2.0 DRAFT 0" +--- + +# 9. Trust Model 3 — ระบบรายชื่อที่น่าเชื่อถือแบบกระจายศูนย์ด้วย DID (Decentralized Trusted List) + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📊 ส่วนขยาย (extension) — จัดทำเพิ่มเติมโดยคณะทำงาน VC มิได้เป็นส่วนหนึ่งของรายงานต้นฉบับ Thai VC ARF เวอร์ชัน 1.1 +> **เวอร์ชันเอกสาร:** 1.0.2 (2026-09-25) — ปรับแผนภาพ §9.4 ให้แสดงลำดับ Credential Offer, metadata, Token Request, Credential Request และการตรวจ Trusted List ให้ตรงกับ flow OID4VCI +> **แหล่งข้อมูล:** สังเคราะห์จากการวิเคราะห์สถาปัตยกรรมความน่าเชื่อถือ [T1] [T2] [T3] รูปแบบ JWT ที่อ้างอิง LoTE [T8] Trust Framework Template [T12] คู่มือ Trust Guide (th/public-site/vcdoc) [T9] [T10] [T11] และมาตรฐาน OID4VCI 1.0 [T4] OID4VP 1.0 [T5] W3C DID Core [T6] +> **เอกสารที่เกี่ยวข้อง:** [§3.3 ระบบทะเบียนเอกสารรับรอง](03-usage-overview.md) · [§6.5 กลไกการสร้างความน่าเชื่อถือ (ภาพรวม Model 1–3)](06.5-trust-building-mechanism.md) · [§6 DID Methodologies](06-did-methodologies.md) · [§7 การทำงานร่วมกันข้ามระบบนิเวศ](07-cross-ecosystem-interoperability.md) · [ดัชนี ARF](README.md) +*/} + +--- + +หัวข้อนี้เป็นส่วนขยายของรายงาน Thai VC ARF เวอร์ชัน 1.1 จัดทำขึ้นเพื่อกำหนดแนวทางการทำงานของระบบรายชื่อที่น่าเชื่อถือ (trustlist) แบบกระจายศูนย์ (decentralized) ซึ่งเติมเต็มส่วนของระบบทะเบียนเอกสารรับรอง (verifiable data registry) ตาม [§3.3](03-usage-overview.md) ที่ระบุว่าระบบทะเบียนอาจจัดเก็บรายชื่อของเอนทิตี (entity) ได้ แต่มิได้กำหนดกลไกการกำกับดูแลและการตรวจสอบความน่าเชื่อถือของรายชื่อดังกล่าวไว้ + +รายงานฉบับนี้อ้างอิงการวิเคราะห์เปรียบเทียบสถาปัตยกรรมความน่าเชื่อถือ 3 รูปแบบ [T1] [T2] โดยกำหนดให้ Trust Model 3 (Trusted List + DID) เป็นแนวทางหลักในระยะแรก (พ.ศ. 2570–2571) ทั้งนี้ การกำหนดดังกล่าวสอดคล้องกับผลการรับฟังความเห็นของผู้ให้บริการในระบบนิเวศ [T2] + +หัวข้อนี้ครอบคลุมหลักการ องค์ประกอบ กระบวนการลงทะเบียน และกระบวนการตรวจสอบความน่าเชื่อถือภายใต้ Trust Model 3 ทั้งนี้ ภาพรวมของแบบจำลองความน่าเชื่อถือทั้ง 3 รูปแบบ ได้แก่ Trust Model 1 (CA Delegation) Trust Model 2 (ETDA SubCA) และ Trust Model 3 (Trusted List + DID) พร้อมทั้งความหมายและประโยชน์ของ Trusted List อยู่ใน [§6.5 กลไกการสร้างความน่าเชื่อถือ](06.5-trust-building-mechanism.md) ซึ่งควรอ่านก่อนหัวข้อนี้ อย่างไรก็ตาม หัวข้อนี้ไม่ครอบคลุมรายละเอียดของ Trust Model 1 และ Trust Model 2 ซึ่งมีรายละเอียดตาม [T1] [T2] และไม่ครอบคลุมการเชื่อมโยงข้ามพรมแดน ซึ่งมีรายละเอียดตาม [T3] + +--- + +## 9.1 หลักการของ Trust Model 3 + +Trust Model 3 ไม่ใช้ผู้ให้บริการออกใบรับรอง (certification authority: CA) เป็นสายความน่าเชื่อถือหลัก โดยสำนักงานพัฒนาธุรกรรมทางอิเล็กทรอนิกส์ (สพธอ.) ดำเนินการ 2 ประการ ดังนี้ + +1. ตรวจรับรอง (audit) เอนทิตีที่ประสงค์จะเข้าร่วมระบบ +2. กำกับดูแลและลงลายมือชื่อดิจิทัล (digital signature) กำกับ Trusted List + +เอนทิตีแต่ละราย ได้แก่ ผู้ออกเอกสาร (issuer) ผู้ตรวจสอบเอกสาร (verifier) และผู้ให้บริการกระเป๋าเอกสารดิจิทัล (digital document wallet provider) จำเป็นต้องสร้างคู่กุญแจ (key pair) และตัวระบุแบบกระจายศูนย์ (decentralized identifier: DID) ของตนเอง แล้วนำมาลงทะเบียนต่อ สพธอ. เมื่อผ่านการตรวจรับรอง สพธอ. จะจัดเก็บ DID หรือการอ้างอิงกุญแจ (key reference) พร้อมบทบาท (role) สถานะ (status) ชนิดเอกสารที่ออกได้ (allowed credential types) และช่วงเวลาที่มีผลใช้ได้ (validity) ไว้ใน Trusted List + +การจัดเก็บรายชื่อของเอนทิตีในลักษณะนี้ เป็นการดำเนินการตาม [§3.3](03-usage-overview.md) โดยเลือกใช้รูปแบบกระจายศูนย์ ทั้งนี้ กุญแจสาธารณะ (public key) ของแต่ละเอนทิตีจัดเก็บในรูปแบบ DID Document ตาม [§6](06-did-methodologies.md) [T6] องค์ประกอบของ Trust Model 3 แสดงได้ดังรูปที่ 9-1 + +```mermaid +flowchart TB + ISS["ผู้ออกเอกสาร / ผู้ตรวจสอบเอกสาร /
ผู้ให้บริการกระเป๋าเอกสารดิจิทัล"] + AUDIT["สพธอ. ตรวจรับรอง (Audit)"] + TL["ETDA Signed Trusted List
(DID/key + role + status + validity)"] + ALL["เอนทิตีจัดเก็บ Trusted List ไว้ในเครื่อง (cache)
แล้วตรวจสอบระหว่างกัน"] + ISS -->|"สร้าง key/DID แล้วยื่นลงทะเบียน"| AUDIT + AUDIT -->|"เมื่อผ่านการตรวจ จึงบันทึกลง Trusted List"| TL + TL -->|"ดึงข้อมูลไปจัดเก็บ (มี TTL)"| ALL +``` + +**รูปที่ 9-1 องค์ประกอบและการทำงานของ Trust Model 3** + +--- + +## 9.2 การลงทะเบียนและการตรวจรับรอง (onboarding) + +กระบวนการเข้าร่วมระบบภายใต้ Trust Model 3 ประกอบด้วยการยื่นให้ สพธอ. ตรวจรับรองเพื่อบันทึกลง Trusted List โดยเอนทิตีไม่จำเป็นต้องขอใบรับรอง (certificate) จาก CA ก่อน บทบาท คู่กุญแจ และข้อมูลที่จัดเก็บของแต่ละเอนทิตี เป็นดังนี้ + +| เอนทิตี | สร้าง key/DID เอง | การใช้งานกุญแจ | ข้อมูลที่จัดเก็บใน Trusted List | +|---------|:---:|------|------| +| ผู้ออกเอกสาร (issuer) | ต้อง | ลงลายมือชื่อ VC | DID/key + role + status + allowed credential types | +| ผู้ตรวจสอบเอกสาร (verifier) | ต้อง | ลงลายมือชื่อคำขอ VP | DID/key + role + status | +| ผู้ให้บริการกระเป๋าเอกสารดิจิทัล | ต้อง | ลงลายมือชื่อ Wallet Unit Attestation (WUA) | DID/key + role + status | +| กระเป๋าเอกสารดิจิทัล (wallet unit) | ต้อง (device key) | ผูกผู้ถือเอกสารกับอุปกรณ์ (holder/device binding) | ไม่จัดเก็บโดยตรง ตรวจสอบผ่าน WUA และ status ของผู้ให้บริการ | +| สพธอ. | — | ตรวจรับรองและลงลายมือชื่อ Trusted List | เป็นผู้กำกับดูแลและลงลายมือชื่อ Trusted List | + +บทบาทของเอนทิตีข้างต้นเป็นไปตาม [§3.1](03-usage-overview.md) + +--- + +## 9.3 กลไกการทำงานของ Trusted List + +สพธอ. ลงลายมือชื่อดิจิทัลกำกับ Trusted List ที่เกี่ยวข้องในรูปแบบ JWT และเผยแพร่เป็นไฟล์ฉบับเต็มผ่าน CDN เอนทิตีแต่ละรายดึง Trusted List ที่เกี่ยวข้องเมื่อเกิดการตัดสินใจด้านความน่าเชื่อถือ จัดเก็บไฟล์และ `ETag` ไว้ในเครื่อง (local cache) แล้วตรวจสอบระหว่างกันโดยไม่จำเป็นต้องเรียกไปยัง สพธอ. ทุกครั้ง กลไกการทำงานแสดงได้ดังรูปที่ 9-2 [T14] + +```mermaid +flowchart LR + ETDA["สพธอ. ลงลายมือชื่อ Trusted List
(JWT ฉบับเต็ม)"] + CACHE["เอนทิตีดึงมาจัดเก็บ (cache)
ไฟล์เต็ม + ETag"] + VERIFY["ตรวจ DID/key + status
→ resolve เป็น public key
→ ตรวจลายมือชื่อ"] + REFRESH["เมื่อเกิด connection
→ GET + If-None-Match"] + ETDA --> CACHE --> VERIFY + CACHE -.->|"ครบกำหนด TTL"| REFRESH --> ETDA +``` + +**รูปที่ 9-2 กลไกการลงลายมือชื่อ การจัดเก็บ และการตรวจสอบ Trusted List** + +ข้อกำหนดสำคัญของกลไกนี้ มีดังนี้ + +1. ผู้รับ Trusted List ต้องตรวจสอบลายมือชื่อดิจิทัลของ สพธอ. ทุกครั้ง โดยดึงไฟล์จาก CDN ได้โดยไม่ต้องใช้ API key [T14] +2. การดึงข้อมูล (resolve) DID ที่เอนทิตีไม่สามารถดำเนินการได้เอง ให้ใช้ Universal Resolver ตาม [§6.3](06-did-methodologies.md) และ [§7](07-cross-ecosystem-interoperability.md) +3. ในระหว่างที่ยังใช้ cache อยู่ เอนทิตีจะไม่สามารถทราบการเพิกถอน (revocation) ที่เกิดขึ้นภายหลังการดึงข้อมูลครั้งล่าสุด ดังนั้น รายงานฉบับนี้กำหนดให้ต้องระบุนโยบายให้ชัดเจนว่า เมื่อ cache หมดอายุและไม่สามารถดึงข้อมูลใหม่ได้ ระบบต้องปฏิเสธไว้ก่อน (fail-closed) หรืออนุญาตให้ดำเนินการต่อ (fail-open) + +--- + +## 9.4 กระบวนการออกเอกสารรับรองดิจิทัล (OID4VCI ร่วมกับ Trusted List) + +กระบวนการออก VC ตามมาตรฐาน OID4VCI [T4] มีจุดตรวจ 3 จุด ได้แก่ ตรวจผู้ออกเอกสารก่อนขอรหัส OTP ตรวจผู้ให้บริการกระเป๋าก่อนออก VC และตรวจผู้ออกเอกสารกับลายมือชื่อ VC ก่อนจัดเก็บ ดังรูปที่ 9-3 ลำดับ OID4VCI ฉบับเต็มอยู่ใน [§8.1](08.1-issuance-full-flow-detail.md) ส่วนจุดตรวจ Trusted List อ้างอิง [T11] + +```mermaid +sequenceDiagram + actor U as ผู้ถือเอกสาร (holder) + participant W as กระเป๋าเอกสารดิจิทัล + participant I as ผู้ออกเอกสาร (issuer) + participant TL as ETDA Trusted List + U->>I: เข้าสู่บริการและขอออก VC + I-->>U: แสดง Credential Offer เป็น QR Code และส่งรหัส OTP + U->>W: สแกน QR Code + W->>I: GET Credential Issuer Metadata + I-->>W: Metadata และรายละเอียด Credential Offer + Note over W,TL: ดึง Trusted List ฉบับเต็มจาก CDN เมื่อจำเป็น โดยตรวจลายมือชื่อและใช้ ETag ตรวจความใหม่ + W->>TL: GET Issuer Trusted List JWT ฉบับเต็ม + TL-->>W: Signed JWT + W->>W: จุดตรวจ 1 — TC-3-EARLY: ตรวจสถานะและขอบเขตสิทธิ์ของ Issuer ก่อนขอ OTP + W->>U: ขอรหัส OTP + U-->>W: ส่งรหัส OTP + W->>I: POST /token (pre-authorized_code + tx_code + Client Attestation/PoP ตาม Thai profile) + I->>TL: ตรวจ trust source ของ Wallet Provider ตาม Thai profile + TL-->>I: Trusted List JWT ฉบับเต็ม + I->>I: จุดตรวจ 2 — TC-2: ตรวจ Client Attestation และ PoP ก่อนออก Access Token + I-->>W: access_token + W->>I: POST /credential (proofs.jwt) + I->>I: ตรวจ Access Token และ holder-key proof, ตรวจประเภท VC ตามขอบเขตสิทธิ์ของ Issuer + I->>I: ลงลายมือชื่อ SD-JWT VC + I-->>W: Credential (SD-JWT VC) + W->>TL: GET Issuer Trusted List พร้อม If-None-Match + alt 304 Not Modified + TL-->>W: ใช้ Trusted List ใน cache ที่ตรวจลายมือชื่อแล้ว + else 200 OK + TL-->>W: Trusted List JWT ฉบับใหม่ + ETag + W->>W: ตรวจลายมือชื่อและปรับปรุง cache + end + W->>W: จุดตรวจ 3 — TC-3: ตรวจสถานะและขอบเขตของ Issuer + W->>I: GET Issuer JWKS ตาม kid ใน JOSE Header + I-->>W: Public key สำหรับตรวจลายมือชื่อ + W->>W: ตรวจลายมือชื่อ SD-JWT VC + W-->>U: จัดเก็บ VC +``` + +**รูปที่ 9-3 กระบวนการออก VC ร่วมกับการตรวจสอบ Trusted List** + +--- + +## 9.5 กระบวนการแสดงเอกสารสำแดงดิจิทัล (OID4VP ร่วมกับ Trusted List) + +กระบวนการแสดง VP ตามมาตรฐาน OID4VP [T5] กำหนดให้กระเป๋าเอกสารดิจิทัลตรวจสอบผู้ตรวจสอบเอกสารก่อนส่งข้อมูล และผู้ตรวจสอบเอกสารตรวจสอบผู้ออกเอกสารและผู้ให้บริการกระเป๋าภายหลังรับข้อมูล ดังรูปที่ 9-4 + +```mermaid +sequenceDiagram + actor U as ผู้ถือเอกสาร (holder) + participant W as กระเป๋าเอกสารดิจิทัล + participant V as ผู้ตรวจสอบเอกสาร (verifier) + participant TL as ETDA Trusted List + V-->>U: แสดง QR Code (Authorization Request) + U->>W: สแกน QR Code + W->>V: ขอ Request Object + V-->>W: Signed Request Object + W->>TL: GET Verifier Trusted List JWT ฉบับเต็ม + TL-->>W: JWT แบบ LoTE + Note over W: จุดตรวจ 1 — ตรวจว่าผู้ตรวจสอบเอกสารอยู่ใน Trusted List หากไม่อยู่ ต้องไม่ส่งข้อมูล + W->>U: ขอความยินยอมและเลือกข้อมูลที่จะเปิดเผย (selective disclosure) + U-->>W: ให้ความยินยอม + W->>V: ส่ง VP + V->>TL: GET Wallet Provider และ Issuer Trusted List JWT ฉบับเต็ม + TL-->>V: JWT แบบ LoTE ทั้งสองรายการ + Note over V: จุดตรวจ 2 — ตรวจผู้ให้บริการกระเป๋าใน Trusted List (บังคับเฉพาะกระเป๋าของภาครัฐ) + Note over V: จุดตรวจ 3 — ตรวจ Issuer ใน Trusted List พร้อมสถานะและประเภท VC ตาม Thai profile + V->>V: ตรวจลายมือชื่อของ VC และ VP +``` + +**รูปที่ 9-4 กระบวนการแสดง VP ร่วมกับการตรวจสอบ Trusted List** + +--- + +## 9.6 ความสอดคล้องกับส่วนอื่นของรายงาน + +Trust Model 3 สอดคล้องและต่อยอดจากส่วนต่าง ๆ ของรายงาน Thai VC ARF ดังนี้ + +| หัวข้อ | ส่วนที่ Trust Model 3 กำหนดเพิ่มเติม | +|--------|--------------------------------------| +| [§3.1 บทบาทของผู้เกี่ยวข้อง](03-usage-overview.md) | ใช้บทบาทเดิม ไม่เพิ่มบทบาทใหม่ | +| [§3.3 ระบบทะเบียนเอกสารรับรอง](03-usage-overview.md) | กำหนดรายชื่อของเอนทิตีให้เป็นรูปธรรมในรูปแบบกระจายศูนย์ คือ ETDA Signed Trusted List | +| [§5 มาตรฐานและข้อปฏิบัติ](05-standards-and-compliance.md) | ใช้ OID4VCI OID4VP และ SD-JWT VC เดิม โดยเพิ่มจุดตรวจสอบ Trusted List | +| [§6 DID Methodologies](06-did-methodologies.md) | ใช้ DID และ DID Document เป็นที่จัดเก็บ public key ของเอนทิตี | +| [§7 การทำงานร่วมกันข้ามระบบนิเวศ](07-cross-ecosystem-interoperability.md) | ใช้ Universal Resolver ในการ resolve DID ข้ามระบบนิเวศ | + +การเชื่อมโยงข้ามพรมแดนกับสหภาพยุโรป (EU) จำเป็นต้องดำเนินการเพิ่มเติม โดยจัดทำ bridge จาก DID ไปเป็น X.509 และจัดทำ List of Trusted Entities (LoTE) ตามมาตรฐาน ETSI TS 119 602 [T7] ทั้งนี้ มีรายละเอียดตาม [T3] + +--- + +## 9.7 ข้อกำหนดที่ต้องระบุให้ชัดเจน + +จากการวิเคราะห์ความเสี่ยงและการรับฟังความเห็นของผู้ให้บริการ [T2] รายงานฉบับนี้กำหนดให้ต้องระบุเรื่องต่อไปนี้ให้ชัดเจนตั้งแต่ระยะเริ่มต้น + +1. นโยบายการจัดการกุญแจ (key management) — ต้องกำหนด proof-of-possession การหมุนกุญแจ (key rotation) และบังคับใช้ฮาร์ดแวร์ความมั่นคงปลอดภัย (hardware security module: HSM) ที่ผ่านการรับรองอย่างน้อยระดับ FIPS 140-2 Level 3 เป็นเกณฑ์ขั้นต่ำ และยอมรับ FIPS 140-3 Level 3 เป็นมาตรฐานสืบทอด [T13] ทั้งนี้ ควรจัดทำบัญชีผู้ให้บริการ HSM และการเก็บรักษากุญแจ (key custody) ที่ได้รับการรับรอง โดยรายละเอียดเกณฑ์และการอ้างอิง NIST อยู่ใน [§12.2 และ §12.10](12-key-management-and-trustlist-deployment.md) +2. ความพร้อมใช้งานสูง (high availability) ของ Trusted List และ Universal Resolver — ควรจัดให้มี replicated endpoint, local cache, fallback resolver และกำหนดเป้าหมายระดับการให้บริการ (service level agreement: SLA) ที่ชัดเจน +3. ค่า TTL และนโยบาย fail-open/fail-closed — ต้องกำหนดเป็นค่าที่แน่นอน +4. สถานะทางกฎหมาย — ควรพิจารณาว่าลายมือชื่อที่ตรวจสอบผ่าน DID และ Trusted List เข้าเกณฑ์ลายมือชื่ออิเล็กทรอนิกส์ที่เชื่อถือได้ตามพระราชบัญญัติว่าด้วยธุรกรรมทางอิเล็กทรอนิกส์ พ.ศ. 2544 มาตรา 9 มาตรา 26 และมาตรา 28 หรือไม่ +5. แผนรองรับการเข้ารหัสลับยุคหลังควอนตัม (post-quantum cryptography: PQC) — ควรจัดทำแผนปรับเปลี่ยนไปสู่มาตรฐาน FIPS 203 FIPS 204 และ FIPS 205 +6. ข้อจำกัดสิทธิของผู้ถือเอกสาร — ผู้ถือเอกสารอาจถูกจำกัดมิให้ส่งเอกสารรับรองให้แก่ผู้ตรวจสอบเอกสารที่อยู่นอก Trusted List ดังนั้น ควรออกแบบแนวทางรองรับกรณีดังกล่าว + +--- + +## 9.8 โครงสร้างข้อมูลของ Trusted List (Trusted List Schema) + +JWT ตัวอย่างที่ถอดรหัสได้ใช้ `LoTE.TrustedEntitiesList[]` เป็นรายการองค์กร และใช้ `TrustedEntityServices[]` ภายในแต่ละองค์กรเพื่อแสดงบริการที่เกี่ยวข้อง กุญแจสาธารณะอยู่ใน `ServiceInformation.ServiceDigitalIdentity.PublicKeyValues[]` ส่วน DID อาจอยู่ใน `OtherIds[]` ตัวอย่าง issuer ฉบับปัจจุบันมี compact JWS ครบสามส่วนและถอด payload ได้ โดยพบองค์กร 1 รายและบริการออกเอกสาร 3 รายการ แต่ยังไม่แสดงสถานะระดับองค์กรและบริการ หรือประเภท VC ที่ออกได้ จึงต้องกำหนดฟิลด์และกฎดังกล่าวใน Thai profile ก่อนใช้ตัดสินความน่าเชื่อถือ [T8] + +Trusted List ฉบับเผยแพร่เป็น JWT ที่ลงลายมือชื่อและมี payload อ้างอิงโครงสร้าง LoTE ตาม ETSI TS 119 602 โดย JWT และกฎรายบทบาทเป็น Thai profile ผู้เข้าร่วมดึงรายการผ่าน CDN เมื่อจำเป็นต้องตัดสินความน่าเชื่อถือโดยไม่ใช้ API key หากดึงรายการหรือตรวจลายมือชื่อไม่ได้ ให้ปฏิเสธการเชื่อมต่อนั้น [research/42](../research/42-trusted-list-lote-jwt-format.md) [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +โครงสร้าง payload ที่พบใน JWT ตัวอย่างประกอบด้วยเขตข้อมูลดังนี้ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +| เขตข้อมูล | ชนิด | จำเป็น | คำอธิบาย | +|-----------|------|:---:|----------| +| `LoTE.ListAndSchemeInformation.LoTEVersionIdentifier` | Integer | พบในตัวอย่าง | เวอร์ชันโครงสร้างรายการ | +| `LoTE.ListAndSchemeInformation.LoTESequenceNumber` | Integer | พบในตัวอย่าง | ลำดับการเผยแพร่ | +| `LoTE.ListAndSchemeInformation.LoTEType` | URI | พบในตัวอย่าง | ชนิดของรายการ | +| `LoTE.ListAndSchemeInformation.ListIssueDateTime` | ISO8601 | พบในตัวอย่าง | เวลาเผยแพร่ | +| `LoTE.ListAndSchemeInformation.NextUpdate` | ISO8601 | พบในตัวอย่าง | เวลาที่ต้องตรวจความสดใหม่ | +| `LoTE.TrustedEntitiesList[]` | Array | พบในตัวอย่าง | รายการองค์กร | + +เขตข้อมูลระดับองค์กรและบริการที่พบในตัวอย่าง issuer และ verifier มีดังนี้ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +| ระดับ | เขตข้อมูล | คำอธิบาย | +|-------|-----------|----------| +| องค์กร | `TrustedEntityInformation.TEName[]` | ชื่อองค์กรพร้อมภาษา | +| บริการ | `ServiceInformation.ServiceTypeIdentifier` | URI ของประเภทบริการ | +| บริการ | `ServiceInformation.ServiceName[]` | ชื่อบริการพร้อมภาษา | +| บริการ | `ServiceInformation.ServiceDigitalIdentity.PublicKeyValues[]` | กุญแจสาธารณะที่ประกาศโดยตรง | +| บริการ | `ServiceInformation.ServiceDigitalIdentity.X509Certificates[]` | ใบรับรอง X.509 ที่พบในตัวอย่าง issuer | +| บริการ | `ServiceInformation.ServiceDigitalIdentity.OtherIds[]` | ตัวระบุอื่น เช่น DID | + +> **ข้อกำหนดที่ยังต้องระบุใน Thai profile:** ต้องกำหนดฟิลด์สถานะระดับองค์กรและระดับบริการ ขอบเขตสิทธิ์ และช่วงเวลาที่มีผลใช้ได้ให้ชัดเจน ตัวอย่าง JWT ที่ถอดรหัสยังไม่มีข้อมูลเหล่านี้ครบ ผู้ตรวจสอบข้อมูลต้องปฏิเสธเมื่อยืนยันสถานะหรือสิทธิ์ที่จำเป็นไม่ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +Trusted List เป็นแหล่งตัดสินว่าเอนทิตีมีสิทธิ์กระทำการใด ส่วน DID Document ใช้ประกาศกุญแจสำหรับตรวจลายมือชื่อ ดังนั้น consumer ต้องตรวจทั้งตัวตน กุญแจ สถานะ ช่วงเวลา และขอบเขตสิทธิ์จาก Trusted List ก่อนดำเนินการ [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +ขอบเขตสิทธิ์ต้องระบุแยกตามบทบาท ดังนี้ [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +| บทบาท | ข้อมูลขอบเขตสิทธิ์ขั้นต่ำ | เงื่อนไขการใช้งาน | +|--------|-----------------------------|-------------------| +| Issuer | ประเภท VC และ claims ที่ออกได้; research/42 ยก `ServiceInformation.credential_types` เป็นตัวอย่างที่เสนอ | ต้องตรวจตาม Thai profile ที่ประกาศใช้ | +| Verifier | claims ที่ขอได้และวัตถุประสงค์; JWT ตัวอย่างยังไม่แสดงฟิลด์ดังกล่าว | ต้องตรวจตาม Thai profile ที่ประกาศใช้ | +| Wallet Provider | กุญแจ สถานะ และช่วงเวลาสำหรับตรวจ WUA; JWT ตัวอย่างยังมี `TrustedEntitiesList[]` ว่าง | ต้องตรวจตาม Thai profile ที่ประกาศใช้ | + +การลงลายมือชื่อ Trusted List ของ สพธอ. ควรดำเนินการจาก HSM ระดับ FIPS 140-3 Level 3 โดยใช้เกณฑ์อนุมัติแบบ M-of-N เช่น 3-of-5 และมีกุญแจฉุกเฉินที่เก็บแบบออฟไลน์ [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +โครงสร้าง issuer ต่อไปนี้เป็นข้อเสนอใน [research/42](../research/42-trusted-list-lote-jwt-format.md) ส่วน issuer JWT ต้นทางมีบริการออกเอกสาร 3 รายการ ยังไม่มีฟิลด์สถานะหรือประเภท VC ที่ออกได้ และยังไม่ได้ตรวจลายมือชื่อ ค่า `ThaiIssuersList` ด้านล่างเป็น URI ที่เสนอ ส่วน JWT ตัวอย่างใช้ค่า `EUPubEAAProvidersList` + +```json +{ + "LoTE": { + "ListAndSchemeInformation": { + "LoTEType": "https://uri.etda.or.th/19602/LoTEType/ThaiIssuersList", + "LoTESequenceNumber": 1, + "ListIssueDateTime": "2026-09-24T00:00:00Z", + "NextUpdate": "2026-09-25T00:00:00Z" + }, + "TrustedEntitiesList": [{ + "TrustedEntityInformation": {"TEName": [{"lang": "th", "value": "หน่วยงานตัวอย่าง"}]}, + "TrustedEntityServices": [{"ServiceInformation": { + "ServiceTypeIdentifier": "https://uri.etda.or.th/19602/SvcType/CredentialIssuer", + "status": "active", + "ServiceDigitalIdentity": {"OtherIds": ["did:web:issuer.example.th"]}, + "credential_types": ["https://credentials.dopa.go.th/ThaiNationalIDCredential"] + }}] + }] + } +} +``` + +สำหรับ Verifier การตรวจ claims และวัตถุประสงค์ที่ขอได้ยังเป็นข้อกำหนดของไทย แต่ JWT ตัวอย่างใน research/42 ยังไม่แสดงชื่อฟิลด์ดังกล่าว จึงต้องกำหนดใน Thai profile ก่อนนำตัวอย่างไปใช้จริง [research/42](../research/42-trusted-list-lote-jwt-format.md) [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +--- + +## 9.9 การลงทะเบียนและ ETDA Trust Gateway + +เอนทิตีทุกรายต้องลงทะเบียนต่อ สพธอ. ก่อนเข้าร่วมระบบ เมื่อผ่านการตรวจ (audit) แล้วจึงถูกเพิ่มเข้า Trusted List [T10] + +การดึง Trusted List ใช้รูปแบบ CDN: แต่ละ entity ดึง Trusted List ที่เกี่ยวข้องฉบับเต็มในรูปแบบ JWT จาก **CDN** โดยตรง ได้แก่ `https://trustme.etda.or.th/.well-known/iss.txt` สำหรับ Issuer, `https://trustme.etda.or.th/.well-known/ver.txt` สำหรับ Verifier และ `https://trustme.etda.or.th/.well-known/wp.txt` สำหรับ Wallet Provider โดยไม่ต้องใช้ API key, delta หรือไฟล์ CBOR และต้องตรวจลายมือชื่อของ Trusted List ตาม §9.3 ก่อนใช้ข้อมูล [T14] + +ส่วน **API Key** ที่ได้รับตอนลงทะเบียน ใช้เฉพาะเรียก **ETDA API Gateway** เพื่อ resolve custom DID ที่ Resolver ส่วนกลางยังไม่รองรับเท่านั้น — ไม่เกี่ยวกับการดึง Trusted List [T12] + +รายงานฉบับนี้กำหนดระดับความเชื่อมั่นของผู้ตรวจสอบเอกสาร (verifier) เป็น 2 ระดับ [T10] ดังนี้ + +1. ระดับทั่วไป (standard) — ไม่จำเป็นต้องพิสูจน์กระเป๋าเอกสารดิจิทัล +2. ระดับเข้มงวด (enhanced) — จำเป็นต้องพิสูจน์กระเป๋าเอกสารดิจิทัล + +กรณีเอนทิตีประสงค์จะใช้ DID Method ที่ Resolver ส่วนกลางยังไม่รองรับ (custom DID) สพธอ. กำหนดทางเลือกการเชื่อมต่อไว้ 2 รูปแบบ [T10] ได้แก่ (1) เอนทิตีจัดส่ง DID Driver ให้ สพธอ. ติดตั้งใน Resolver ส่วนกลาง และ (2) เอนทิตีให้บริการ Resolver ของตนเอง แล้วอนุญาต IP ของ ETDA Gateway (whitelist) + +--- + +## 9.10 หลักการตรวจสอบความปลอดภัยพื้นฐาน (Core Security Rules) + +การตรวจสอบในทุก flow แบ่งตามต้นทุนได้ 3 ประเภท ได้แก่ (1) การเทียบข้อมูลใน Trusted List ที่จัดเก็บไว้ในเครื่อง (2) การตรวจลายมือชื่อ (cryptographic verification) และ (3) การเรียกผ่านเครือข่าย (network) เช่น การ resolve DID หรือการดึง Status List [T11] + +รายงานฉบับนี้กำหนดหลักการสำคัญว่า หากการเทียบข้อมูลใน Trusted List (ประเภทที่ 1) ไม่ผ่าน เอนทิตีต้องปฏิเสธทันที โดยไม่ต้องเรียกผ่านเครือข่าย (ประเภทที่ 3) ต่อ เพื่อมิให้สิ้นเปลืองทรัพยากรและมิให้เกิดความเสี่ยงจากการส่งคำขอไปยังปลายทางที่ไม่น่าเชื่อถือ [T11] + +จุดตรวจสอบ Trusted List ตลอดกระบวนการออกและแสดงเอกสาร สรุปได้ดังนี้ [T11] + +| จุดตรวจ | กระบวนการ | ผู้ตรวจ | ตรวจอะไร | เมื่อไม่ผ่าน | +|:---:|------|---------|----------|-------------| +| 1 | ออก VC | กระเป๋าเอกสารดิจิทัล | ผู้ออกเอกสารอยู่ใน Trusted List | หยุด ไม่ส่ง Transaction Code | +| 2 | ออก VC | ผู้ออกเอกสาร | ผู้ให้บริการกระเป๋าอยู่ใน Trusted List | ไม่ออก VC | +| 3 | ออก VC | กระเป๋าเอกสารดิจิทัล | ผู้ออกเอกสารอยู่ใน Trusted List | ไม่บันทึก VC | +| 4 | แสดง VP | กระเป๋าเอกสารดิจิทัล | ผู้ตรวจสอบเอกสารอยู่ใน Trusted List | ไม่ส่งข้อมูล | +| 5 | แสดง VP | ผู้ตรวจสอบเอกสาร | ผู้ให้บริการกระเป๋าอยู่ใน Trusted List (บังคับเฉพาะกระเป๋าภาครัฐ) | ปฏิเสธ | +| 6 | แสดง VP | ผู้ตรวจสอบเอกสาร | ผู้ออกเอกสารอยู่ใน Trusted List พร้อม status และสิทธิ์การออกเอกสาร | ไม่เชื่อถือข้อมูล | + +--- + +{/* METADATA (agent-only — not rendered to readers) +## บรรณานุกรม (References) + +- [T1] คณะทำงาน VC, "Trust Architecture: 3 โมเดลตรวจสอบความน่าเชื่อถือ" — [research/32-trustlist-3models.md](../research/32-trustlist-3models.md) +- [T2] คณะทำงาน VC, "3 Trust Architecture Models — Fact-Based Analysis" — [research/33-trust-architecture-models-factbase-analysis.md](../research/33-trust-architecture-models-factbase-analysis.md) +- [T3] คณะทำงาน VC, "Trusted List สำหรับ Domestic และ Cross-Border" — [research/34-trustlist-domestic-and-crossborder.md](../research/34-trustlist-domestic-and-crossborder.md) +- [T4] OpenID Foundation, "OpenID for Verifiable Credential Issuance 1.0", Final — https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html +- [T5] OpenID Foundation, "OpenID for Verifiable Presentations 1.0", Final — https://openid.net/specs/openid-4-verifiable-presentations-1_0.html +- [T6] World Wide Web Consortium (W3C), "Decentralized Identifiers (DIDs) v1.0", 19 July 2022 — https://www.w3.org/TR/did-core/ +- [T7] European Telecommunications Standards Institute (ETSI), "ETSI TS 119 602 — Lists of Trusted Entities (LoTEs)" — https://www.etsi.org/deliver/etsi_ts/119600_119699/119602/ +- [T8] คณะทำงาน VC, "การย้ายรูปแบบ Trusted List ไปสู่ JWT ที่อ้างอิง LoTE" — [research/42-trusted-list-lote-jwt-format.md](../research/42-trusted-list-lote-jwt-format.md) +- [T9] คู่มือ Trust Guide, "§5.3 ตัวอย่าง TL JSON" — [trust-guide/05-trust-infrastructure/02-tl-json-examples.md](../public-site/vcdoc/docs/trust-guide/05-trust-infrastructure/02-tl-json-examples.md) +- [T10] คู่มือ Trust Guide, "§6.1 ระดับความเชื่อมั่นของ Verifier และการลงทะเบียน" — [trust-guide/06-security-and-privacy/01-verifier-confidence-and-registration.md](../public-site/vcdoc/docs/trust-guide/06-security-and-privacy/01-verifier-confidence-and-registration.md) +- [T11] คู่มือ Trust Guide, "ภาคผนวก ก — Core Security Rules" — [trust-guide/09-appendix/01-core-security-rules.md](../public-site/vcdoc/docs/trust-guide/09-appendix/01-core-security-rules.md) +- [T12] คณะทำงาน VC, "Trust Framework Template — §5 (CDN Trusted List serving: ดึงจาก CDN ไม่ต้องใช้ API key; Gateway สำหรับ custom DID เท่านั้น)" — [th/draft/trust-framework-template.md](../draft/trust-framework-template.md) +- [T13] NIST, "FIPS PUB 140-2 — Security Requirements for Cryptographic Modules", พฤษภาคม ค.ศ. 2001 และ "FIPS PUB 140-3", มีนาคม ค.ศ. 2019 — https://csrc.nist.gov/pubs/fips/140-3/final +- [T14] คณะทำงาน VC, "แนวทางกระจาย Trustlist (LoTE) ผ่าน File/Edge CDN — ไม่ใช้ RabbitMQ", เวอร์ชัน 1.10, 4 กันยายน 2569 — [research/41](../research/41-trustlist-serving-filecdn-etag-binary.md) + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 8 — Implementation Guidelines (OID4VCI / OID4VP)](08-implementation-guidelines.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 10 — Wallet Unit Attestation (WUA) ➡️](10-wallet-unit-attestation.md) diff --git a/docs/thai-vc-arf/10-wallet-unit-attestation.md b/docs/thai-vc-arf/10-wallet-unit-attestation.md new file mode 100644 index 0000000..95d4206 --- /dev/null +++ b/docs/thai-vc-arf/10-wallet-unit-attestation.md @@ -0,0 +1,302 @@ +--- +description: "10. Wallet Unit Attestation (WUA) — Thai VC ARF 2.0 DRAFT 0" +--- + +# 10. Wallet Unit Attestation (WUA) + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📐 Specification +> **เวอร์ชันเอกสาร:** 1.7 (2026-08-07) — ประวัติการแก้ไขทั้งหมดอยู่ในภาคผนวก §ก.4 บันทึกการเปลี่ยนแปลง +> **แหล่งข้อมูลปฐมภูมิ (ต้นฉบับที่ระบบของไทยอ้างอิง):** +> - EUDI ARF 2.9.0 — Topic 9 / Discussion Topic C: Wallet Unit Attestation (WUA) and Key Attestation — [eudi.dev/2.9.0/discussion-topics/c-wallet-unit-attestation](https://eudi.dev/2.9.0/discussion-topics/c-wallet-unit-attestation/) +> - EUDI ARF 2.9.0 — Technical Specification TS3: Wallet Unit Attestations — [eudi.dev/2.9.0/technical-specifications/ts3-wallet-unit-attestation](https://eudi.dev/2.9.0/technical-specifications/ts3-wallet-unit-attestation/) +> - Commission Implementing Regulation (EU) 2024/2979 — Wallet requirements and Wallet Unit Attestations +> - OpenID for Verifiable Credential Issuance 1.0 (Final) — Annex D (Key Attestation), Annex E (Wallet Attestation) +> +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md), [บทที่ 8 — Implementation Guidelines (OID4VCI/OID4VP)](08-implementation-guidelines.md), [บทที่ 9 — Trust Model 3](09-trust-model-3.md), [บทที่ 12 — Key Management และ Trusted List Deployment](12-key-management-and-trustlist-deployment.md), [บทที่ 13 — VC Status และการเพิกถอน](13-vc-status-and-revocation.md), [00-glossary.md](../../00-glossary.md) +*/} + +--- + +Wallet Unit Attestation (WUA) เป็นแนวคิดที่กำหนดขึ้นโดยกรอบ EU Digital Identity Wallet Architecture and Reference Framework (EUDI ARF) และมีฐานทางกฎหมายใน eIDAS 2.0 (Regulation (EU) 2024/1183) และ Commission Implementing Regulation series (CIR 2024/2977, 2024/2979, 2024/2982) รายงานฉบับนี้ **นำ WUA มาใช้ตามมาตรฐานของ EUDI** เพื่อรับรองผู้ให้บริการกระเป๋าเอกสารดิจิทัล (wallet provider) และหน่วยกระเป๋าเอกสารดิจิทัล (wallet unit) มิได้คิดค้นหรือกำหนดกลไกใหม่ขึ้นเอง + +บทนี้มุ่งอธิบายเฉพาะ **วิธีที่ระบบของไทยใช้ WUA ในกระแสงาน OID4VCI และ OID4VP** และความสัมพันธ์ระหว่าง WUA กับ Trusted List ของสำนักงานพัฒนาธุรกรรมทางอิเล็กทรอนิกส์ (สพธอ.) ส่วนรายละเอียดเชิงมาตรฐาน ได้แก่ โครงสร้างข้อมูล ข้อกำหนดระดับสูง (High-Level Requirements) และฐานทางกฎหมายฉบับเต็ม รายงานฉบับนี้ไม่กล่าวซ้ำ แต่ให้ผู้อ่านศึกษาจากต้นฉบับของ EUDI ตามที่ระบุใน [ภาคผนวก ก](#ภาคผนวก-ก--เอกสารอ้างอิงและการอ่านเพิ่มเติม) + +--- + +## 10.1 ขอบข่ายและที่มา + +### 10.1.1 ที่มาของ WUA + +WUA เป็นกลไกที่ EUDI ARF กำหนดให้ wallet provider ออกให้แก่ wallet unit ของตน เพื่อให้ผู้พึ่งพา (เช่น ผู้ออกเอกสาร หรือผู้ตรวจสอบเอกสาร) ตรวจสอบได้ว่า wallet unit นั้นมาจาก wallet provider ที่เชื่อถือได้ และผูกกับกุญแจที่จัดเก็บในอุปกรณ์เข้ารหัสลับที่ได้รับการรับรอง ระบบของไทยนำกลไกนี้มาใช้เพื่อวัตถุประสงค์เดียวกันสองประการ ได้แก่ + +1. **รับรองผู้ให้บริการกระเป๋าเอกสารดิจิทัล (wallet provider)** ผ่านการที่ สพธอ. ตรวจประเมินและลงรายการใน Trusted List +2. **รับรองหน่วยกระเป๋าเอกสารดิจิทัล (wallet unit)** ผ่านการที่ wallet provider ลงลายมือชื่อ WUA และหน่วยกระเป๋านำเสนอ WUA ในขั้นตอนการทำงาน (runtime) + +กลไกทั้งสองทำงานร่วมกัน โดย Trusted List รับรองในระดับผู้ให้บริการ ส่วน WUA รับรองในระดับหน่วยกระเป๋า + +### 10.1.2 ขอบเขตของบทนี้ + +รายงานฉบับนี้กำหนดให้ระบบของไทยใช้ WUA **ตามที่ EUDI กำหนดไว้** โดยไม่ดัดแปลงนิยามหรือโครงสร้าง บทนี้ครอบคลุมเฉพาะ + +- วิธีที่ wallet unit นำเสนอ WUA และวิธีที่ผู้พึ่งพาตรวจสอบ WUA ในกระแสงาน OID4VCI (การออกเอกสาร) และ OID4VP (การแสดงเอกสาร) +- ความสัมพันธ์ระหว่าง WUA กับ Trusted List ของ สพธอ. +- การเพิกถอนและการเผยแพร่สถานะของ WUA ในบริบทของระบบไทย + +บทนี้ **ไม่ครอบคลุม** รายละเอียดต่อไปนี้ ซึ่งให้อ้างอิงต้นฉบับของ EUDI แทน + +- โครงสร้างข้อมูลและการเข้ารหัสฉบับเต็มของ WIA และ KA (ตาม EUDI Technical Specification TS3) +- ข้อกำหนดระดับสูง (HLR) รายข้อของ EUDI ARF Topic 9 +- ถ้อยคำของกฎหมายลูก (CIR) รายมาตรา + +--- + +## 10.2 นิยามและบทบาทโดยย่อ + +### 10.2.1 นิยามศัพท์ + +ศัพท์ที่ใช้ในบทนี้สอดคล้องกับ [00-glossary.md](../../00-glossary.md) และนิยามในข้อ 2.24 ของ [บทที่ 2 — บทนิยาม](02-definitions.md) รายละเอียดฉบับเต็มให้อ้างอิง EUDI ARF 2.9.0 + +| ศัพท์ | คำเต็มภาษาอังกฤษ | ความหมายโดยย่อ | +|------|-------------------|---------------| +| **WUA** | Wallet Unit Attestation | คำรวมของหลักฐานที่ Wallet Provider ออกให้แก่ Wallet Unit ตาม EUDI ARF 2.9.0 TS3; การนำมาใช้ในไทยและการจับคู่กับ protocol เป็นไปตาม Thai profile | +| **WIA** | Wallet Instance Attestation | ประเภทหนึ่งของ WUA รับรองความเป็นของแท้และความสมบูรณ์ของ wallet instance; มีวัตถุประสงค์ใกล้เคียงกับ Client authentication ของ Wallet Attestation ใน OID4VCI 1.0 Appendix E แต่รูปแบบ/ข้อกำหนดของ EUDI และ OID4VCI ไม่ควรถูกถือว่าเทียบเท่ากันโดยอัตโนมัติ | +| **KA** | Key Attestation | ประเภทหนึ่งของ WUA รับรองว่ากุญแจของ wallet unit ถูกสร้างและจัดเก็บใน WSCD ที่ได้รับการรับรอง และระบุระดับความมั่นคงปลอดภัยของกุญแจ; การผูกกับ OID4VCI Key Attestation ใน Appendix D ต้องเป็นไปตาม mapping ที่ profile ระบุ | +| **Wallet Unit** | Wallet Unit | หน่วยของกระเป๋าเอกสารดิจิทัล ประกอบด้วย wallet instance พร้อม WSCA และอุปกรณ์ที่เกี่ยวข้อง | +| **Wallet Provider** | Wallet Provider | ผู้ให้บริการ wallet unit ที่มีหน้าที่ออกและเพิกถอน WUA ทั้ง WIA และ KA | + +### 10.2.2 บทบาทที่เกี่ยวข้องกับ WUA + +```mermaid +flowchart LR + WP[Wallet Provider
สพธอ. รับรอง] -- "ออก WUA
ลงลายมือชื่อ" --> WU[Wallet Unit] + WU -- "นำเสนอหลักฐานที่ profile กำหนด
ณ จุดส่งตาม OID4VCI" --> ISS[ผู้ออกเอกสาร
Issuer / PID Provider] + WU -- "นำเสนอ WUA
ยืนยันตัวตนเท่านั้น" --> RP[ผู้ตรวจสอบเอกสาร
Relying Party] + ETDA[สพธอ.
Trusted List] -- "รับรอง Wallet Provider" --> WP + ETDA -- "เผยแพร่สถานะ Trusted List" --> ISS + ETDA -- "เผยแพร่สถานะ Trusted List" --> RP +``` + +WUA มีบทบาทต่างกันตามผู้รับ ผู้ออกเอกสารได้รับ WUA ฉบับที่มีข้อมูลความสามารถของอุปกรณ์ (EUDI เรียกว่า Use Case 2) ขณะที่ผู้ตรวจสอบเอกสารได้รับเฉพาะข้อมูลยืนยันตัวตนของ wallet unit (Use Case 1) เพื่อรักษาความเป็นส่วนตัว รายละเอียดของทั้งสอง Use Case ให้อ้างอิง EUDI ARF 2.9.0 Topic 9 + +### 10.2.3 WUA เป็นคำรวม ประกอบด้วย WIA และ KA + +WUA เป็นคำรวม (umbrella term) ของหลักฐานสองประเภทที่แยกหน้าที่กันตาม EUDI ARF 2.9.0 Technical Specification TS3 ได้แก่ + +1. **Wallet Instance Attestation (WIA)** รับรองว่า wallet instance คือตัวโปรแกรมกระเป๋า เป็นของแท้ มาจาก wallet provider ที่เชื่อถือได้ และไม่ถูกดัดแปลง +2. **Key Attestation (KA)** รับรองว่ากุญแจที่ wallet unit ใช้ผูกกับเอกสารถูกสร้างและจัดเก็บใน WSCD ที่ได้รับการรับรอง พร้อมระบุระดับความมั่นคงปลอดภัยของกุญแจนั้น + +หลักฐานทั้งสองประเภทมีวงจรชีวิตและกลไกสถานะแยกจากกัน ระบบของไทยนำการแบ่งประเภทและกลไกดังกล่าวมาใช้ตามที่ EUDI กำหนด มิได้กำหนดขึ้นเอง การแยกบทบาทของ WIA และ KA แสดงได้ดังนี้ + +| ประเภท | คำเต็ม | รับรองอะไร | หลักฐานคู่ขนานใน OID4VCI | +|--------|--------|-----------|--------------------------| +| WIA | Wallet Instance Attestation | ความเป็นของแท้และความสมบูรณ์ของ wallet instance | Client authentication ใน Wallet Attestation (Appendix E); mapping ตาม Thai profile | +| KA | Key Attestation | การจัดเก็บกุญแจใน WSCD และระดับความมั่นคงปลอดภัยของกุญแจ | Key Attestation ใน JOSE header ของ `proofs.jwt` (Appendix D/F.1); mapping ตาม Thai profile | + +รายละเอียดการเพิกถอนและสถานะของ WIA และ KA อยู่ใน [§10.5](#105-อายุการใช้งาน-การเพิกถอน-และสถานะของ-wua) ส่วนรายละเอียดโครงสร้างข้อมูลฉบับเต็ม ให้อ้างอิง EUDI ARF 2.9.0 TS3 + +--- + +## 10.3 การใช้ WUA ในกระแสงานของระบบไทย + +หัวข้อนี้เป็นสาระสำคัญของบท โดยอธิบายว่า WUA ปรากฏอยู่ที่จุดใดในกระแสงานมาตรฐาน กระแสงานฉบับเต็มพร้อมแผนภาพลำดับ (sequence diagram) และจุดตรวจ (checkpoint) ทั้งหมดอยู่ใน [บทที่ 8 — Implementation Guidelines](08-implementation-guidelines.md) + +### 10.3.1 การออกเอกสาร (OID4VCI) + +ในกระแสงานการออกเอกสารตาม OID4VCI ตาม [บทที่ 8 §8.2](08-implementation-guidelines.md), `proofs.jwt` ใน Credential Request พิสูจน์การครอบครองกุญแจที่จะผูกกับ Credential. หาก Thai profile ใช้ Wallet Attestation ตาม Appendix E, Wallet ส่ง Client Attestation ใน Pushed Authorization Request หรือ Token Request เพื่อยืนยัน Client ต่อ Authorization Server ก่อนออก Access Token; การส่งนี้แยกจาก Credential Request. + +Thai profile อาจกำหนดให้ใช้ WIA/KA และให้ Issuer ตรวจข้อมูล trust, key และสถานะ Wallet ก่อนออก Credential; ต้องระบุ mapping และจุดส่งให้ชัด. Wallet Attestation ตาม Appendix E ใช้ยืนยัน Client ต่อ Authorization Server. `proofs.jwt` ตาม Appendix F.1 พิสูจน์การครอบครองกุญแจใน Credential Request และอาจมี Key Attestation ตาม Appendix D ใน JOSE header. อย่าอนุมานว่า EUDI WUA ทั้งชุดส่งใน Credential Request หรือเป็นรูปแบบเดียวกับ OID4VCI โดยอัตโนมัติ. OID4VCI ไม่กำหนด Trusted List/Registry ของไทย จึงต้องกำหนด trust source, schema, freshness และวิธีตรวจสถานะใน profile. + +```mermaid +sequenceDiagram + autonumber + participant WU as Wallet Unit + participant ISS as ผู้ออกเอกสาร (Issuer) + participant TL as Trusted List (สพธอ.) + participant WPR as Wallet Provider
Token Status List + + WU->>ISS: Pushed Authorization Request หรือ Token Request + Client Attestation/PoP ตาม profile + ISS->>TL: ตรวจ trust source และ Wallet Provider ตาม Thai profile + ISS->>ISS: ตรวจ Client Attestation/PoP ก่อนออก Access Token + ISS-->>WU: Access Token + WU->>ISS: Credential Request + Access Token + proofs.jwt + ISS->>ISS: ตรวจ holder-key proof และ Key Attestation หาก Thai profile กำหนด + ISS->>WPR: ตรวจสถานะ Wallet/Key เมื่อ Thai profile กำหนด + WPR-->>ISS: ผลตรวจสถานะตามกลไก profile + alt ผ่านทั้งหมด + ISS-->>WU: ออกเอกสารผูกกับกุญแจใน Key Attestation + else ไม่ผ่าน + ISS-->>WU: ปฏิเสธการออก พร้อมเหตุผล + end +``` + +การตรวจสถานะของ WUA ใช้จุดตรวจสอบของ wallet provider โดยคงความเป็นส่วนตัว (privacy-preserving) ของผู้ใช้รายเครื่อง สอดคล้องกับ CIR 2024/2979 Art. 7 จุดตรวจนี้ตรงกับจุดตรวจ TC-2 ใน [บทที่ 8 §8.2.5](08-implementation-guidelines.md) + +### 10.3.2 การแสดงเอกสาร (OID4VP) + +ในกระแสงานการแสดงเอกสารตาม OID4VP ตาม [บทที่ 8 §8.3](08-implementation-guidelines.md) Thai profile กำหนดให้ Wallet รัฐบาลส่ง WIA และ WIA-PoP เพื่อให้ผู้ตรวจสอบเอกสารตรวจ TC-2; Wallet เอกชนเลือกส่งทั้งคู่หรือไม่ส่งทั้งคู่ได้ + +- เมื่อ Wallet ส่ง WIA ผู้ตรวจสอบเอกสารใช้ WIA เพื่อยืนยันแหล่งที่มาของ Wallet Provider และใช้ WIA-PoP เพื่อยืนยันการครอบครองกุญแจในธุรกรรมปัจจุบัน ตามกติกา TC-2 ใน [บทที่ 8 §8.4.2](08-implementation-guidelines.md) +- หาก Wallet เอกชนไม่ส่งทั้งสองรายการ ผู้ตรวจสอบเอกสารข้าม TC-2 และตรวจ TC-3/TC-4 ต่อ; หากส่งมาเพียงรายการเดียวหรือหลักฐานรายการใดตรวจไม่ผ่าน ให้ปฏิเสธ VP +- ผู้ตรวจสอบเอกสาร **ต้องไม่** ได้รับข้อมูลความสามารถของอุปกรณ์ (Use Case 2) ตามข้อกำหนดความเป็นส่วนตัวของ EUDI ARF (HLR WUA_24) +- การจัดประเภท Wallet และผลของการไม่มี WIA/WIA-PoP เป็นข้อกำหนดของ Thai profile ไม่ใช่ข้อกำหนดของ OID4VP หรือ EUDI WUA โดยตรง + +### 10.3.3 ข้อมูลที่แต่ละบทบาทเข้าถึงได้ + +| ผู้รับ WUA | ข้อมูลที่เข้าถึง | ที่มา (EUDI) | +|-----------|-----------------|--------------| +| ผู้ออกเอกสาร (Issuer / PID Provider) | ยืนยันตัวตน และความสามารถของอุปกรณ์และกุญแจ (Use Case 1 และ 2) | EUDI ARF 2.9.0 Topic 9 | +| ผู้ตรวจสอบเอกสาร (Relying Party) | ยืนยันตัวตนของ wallet unit เท่านั้น (Use Case 1) | HLR WUA_24 | + +--- + +## 10.4 ความสัมพันธ์กับ Trusted List ของ สพธอ. + +### 10.4.1 แบบจำลองความน่าเชื่อถือสองระดับ + +```mermaid +flowchart TD + WP[Wallet Provider
เช่น DGA หรือผู้ให้บริการเอกชน] -->|ลงทะเบียน| AUDIT[การตรวจประเมินของ สพธอ.] + AUDIT -->|ผ่าน| TL[Trusted List
did:web:vc.thaigov.go.th] + TL -->|รับรองระดับผู้ให้บริการ| WP + WP -->|ออก WUA ลงลายมือชื่อ| WU[Wallet Unit] + WU -->|WUA รับรองระดับหน่วย| ISS[ผู้ออกเอกสาร] + ISS -->|ตรวจ Wallet Provider| TL +``` + +### 10.4.2 การลงทะเบียน Wallet Provider + +การลงทะเบียนเป็นไปตาม [`../policy/01-verifier-registration-policy.md`](../policy/01-verifier-registration-policy.md) โดย สพธอ. ตรวจประเมิน 3 ด้าน ได้แก่ (ก) ความปลอดภัยของซอฟต์แวร์ (ข) การจัดการกุญแจ และ (ค) การคุ้มครองข้อมูลส่วนบุคคล เมื่อผ่านการประเมิน สพธอ. เพิ่ม wallet provider ใน Wallet Provider Trusted List ซึ่งเผยแพร่เป็น JWT ที่มี payload แบบ LoTE [รูปแบบอ้างอิง](../research/42-trusted-list-lote-jwt-format.md) ตัวอย่าง JWT ของรายการนี้ยังมี `LoTE.TrustedEntitiesList` เป็นอาร์เรย์ว่าง จึงยังไม่ยืนยันเส้นทางข้อมูลสำหรับ WUA ได้ Thai profile ต้องกำหนดวิธีประกาศและตรวจสอบข้อมูลต่อไปนี้ก่อนใช้งานจริง: + +1. DID ของ wallet provider +2. ใบรับรองสำหรับตรวจลายมือชื่อ WUA +3. รายการ wallet solution ที่ได้รับการรับรอง +4. ตำแหน่งจุดตรวจสอบสถานะการเพิกถอน WUA + +ข้อกำหนดด้านการจัดเก็บและจัดการกุญแจของ wallet provider ให้อ้างอิง [บทที่ 12 — Key Management และ Trusted List Deployment](12-key-management-and-trustlist-deployment.md) + +--- + +## 10.5 อายุการใช้งาน การเพิกถอน และสถานะของ WUA + +### 10.5.1 อายุการใช้งานของ WUA (WUA เป็นหลักฐานอายุสั้น) + +ตาม EUDI ARF 2.9.0 (HLR WUA_09) WUA โดยหลักการมีอายุการใช้งานสั้น wallet provider ต้องพิจารณาการใช้งานแบบออฟไลน์ การทำงานร่วมกัน (interoperability) และความเสี่ยงด้านการติดตามผู้ใช้ (linkability) ในการกำหนดอายุของ WUA และอาจใช้ WUA อายุสั้นเพื่อลดความเสี่ยงดังกล่าว + +เพื่อลดความเสี่ยงด้าน linkability ตามแนวทางของ EUDI (Discussion Topic A) wallet provider ควรออก WUA ในลักษณะใช้ครั้งเดียว (once-only) หรือออกเป็นชุด (batch) แล้วให้หน่วยกระเป๋าหมุนเวียนใช้ WUA คนละฉบับในแต่ละธุรกรรม ด้วยแนวทางนี้ WUA แต่ละฉบับจึงหมดอายุไปเองในเวลาอันสั้น การควบคุมวงจรชีวิตหลักของ WUA จึงเป็น **การหมดอายุระยะสั้น** เป็นด่านแรก ส่วนการเพิกถอน (revocation) เป็นด่านสำรองสำหรับกรณีที่ต้องยกเลิกก่อนหมดอายุ เช่น อุปกรณ์สูญหายหรือกุญแจรั่วไหล + +_ข้อสังเกต:_ ด้วยเหตุที่ WUA มีอายุสั้น ผลของการยกเลิกในทางปฏิบัติจึงมักเกิดจากการที่ wallet provider หยุดออก WUA ฉบับใหม่ให้หน่วยกระเป๋าที่ถูกยกเลิก มากกว่าการประกาศสถานะเพิกถอนของ WUA ฉบับเดิมที่ใกล้หมดอายุอยู่แล้ว + +### 10.5.2 การเพิกถอนและสถานะของ WIA และ KA + +WUA ประกอบด้วย WIA และ KA ซึ่งมีกลไกสถานะแยกจากกัน ระบบของไทยนำกลไกการเพิกถอนของทั้งสองประเภทมาใช้ตามที่ EUDI กำหนด มิได้กำหนดกลไกใหม่ ตาม EUDI ARF 2.9.0 TS3 การเพิกถอน WIA และ KA **ต้อง**ใช้ IETF Token Status List (draft-ietf-oauth-status-list) เป็นกลไกสถานะ โดยมีรายละเอียดดังนี้ + +**การเพิกถอนและสถานะของ WIA** + +- WIA มี object ชื่อ `client_status` ที่อ้างถึงรายการสถานะ (status list) ของ WIA +- `client_status.status` ชี้ไปยัง IETF Token Status List ที่ wallet provider เผยแพร่ เพื่อประกาศว่า wallet instance ใดถูกเพิกถอน เช่น กรณีผู้ใช้ร้องขอ หรือพบปัญหาด้านความปลอดภัย +- ผู้ออกเอกสารตรวจสถานะของ WIA จากรายการนี้ก่อนออกเอกสาร + +**การเพิกถอนและสถานะของ KA** + +- KA มี object ชื่อ `key_storage_status` ที่อ้างถึงรายการสถานะของ KA +- `key_storage_status.status` ชี้ไปยัง IETF Token Status List ที่ wallet provider เผยแพร่ ซึ่งประกาศสถานะได้สองระดับ ได้แก่ (ก) สถานะของ WSCD หรือ keystore ทั้งประเภท โดย KA ทุกฉบับของประเภทเดียวกันใช้ index ร่วมกัน หรือ (ข) สถานะของ WSCD หรือ keystore ของ wallet unit แต่ละหน่วยแยกราย +- ผู้ออกเอกสารตรวจสถานะของ KA จากรายการนี้เพื่อยืนยันว่ากุญแจที่จะผูกกับเอกสารยังอยู่ในอุปกรณ์ที่เชื่อถือได้ + +การใช้ IETF Token Status List คงความเป็นส่วนตัวโดยธรรมชาติ เนื่องจากสถานะแต่ละรายการเป็นเพียง index ในรายการรวม มิได้ระบุตัวผู้ใช้เป็นรายบุคคล จึงสอดคล้องกับข้อกำหนดการเผยแพร่สถานะแบบคงความเป็นส่วนตัวใน CIR 2024/2979 Art. 7 ทั้งนี้ อำนาจการเพิกถอน WIA และ KA เป็นของ wallet provider ที่ออกหลักฐานนั้นเท่านั้น การลงลายมือชื่อ WIA, KA และ Token Status List ใช้อัลกอริทึม ES256, ES384 หรือ ES512 ตาม EUDI ARF 2.9.0 TS3 + +เงื่อนไขและการแจ้งเตือนการเพิกถอนสรุปได้ดังนี้ + +| ประเด็น | ข้อกำหนด | +|--------|----------| +| ผู้มีอำนาจเพิกถอน | เฉพาะ wallet provider ที่ออก WIA หรือ KA นั้น | +| เงื่อนไข | ผู้ใช้ร้องขอ (เช่น อุปกรณ์สูญหาย) กุญแจรั่วไหล การเปลี่ยนสถานะรับรองใน Trusted List หรือ wallet unit ไม่เป็นไปตามข้อกำหนดทางเทคนิค | +| การแจ้งผู้ใช้ | wallet provider ต้องแจ้งผู้ได้รับผลกระทบภายใน 24 ชั่วโมง พร้อมเหตุผล | +| การเผยแพร่สถานะ | IETF Token Status List ที่ wallet provider เผยแพร่ ผ่าน `client_status` สำหรับ WIA และ `key_storage_status` สำหรับ KA | +| ผลต่อเอกสารที่ผูกไว้ | เมื่อ WIA หรือ KA ถูกเพิกถอน ผู้ออกเอกสารที่เคยออกเอกสารให้ wallet unit นั้นต้องเพิกถอนเอกสารที่ผูกไว้ | + +### 10.5.3 ความแตกต่างระหว่างรายการสถานะของ WUA กับรายการสถานะของเอกสารรับรอง + +WIA, KA และเอกสารรับรอง (VC/PID/attestation) ต่างใช้รูปแบบ IETF Token Status List เหมือนกัน แต่เป็น **คนละรายการกัน** มีผู้เผยแพร่และวัตถุประสงค์ต่างกัน ผู้พัฒนาต้องไม่สับสนระหว่างรายการเหล่านี้ + +| รายการสถานะ | ผู้เผยแพร่ | ติดตามสถานะของ | ผู้ตรวจสถานะ | +|------|----------------|---------|--------------| +| สถานะ WIA (`client_status`) | wallet provider ที่ออก WIA | wallet instance คือตัวโปรแกรมกระเป๋า | ผู้ออกเอกสาร (กระแสงาน OID4VCI) | +| สถานะ KA (`key_storage_status`) | wallet provider ที่ออก KA | WSCD หรือ keystore ที่เก็บกุญแจ | ผู้ออกเอกสาร (กระแสงาน OID4VCI) | +| สถานะเอกสารรับรอง (VC/PID/attestation) | ผู้ออกเอกสารแต่ละราย | เอกสารรับรองที่ออกให้ผู้ถือ | ผู้ตรวจสอบเอกสาร (กระแสงาน OID4VP) | + +ความสัมพันธ์แบบต่อเนื่อง (cascade) มีดังนี้ เมื่อ wallet provider เพิกถอน WIA หรือ KA ผ่านรายการสถานะของตน ผู้ออกเอกสารที่เคยออกเอกสารให้ wallet unit นั้นต้องเพิกถอนเอกสารที่ผูกไว้ผ่าน IETF Token Status List **ของผู้ออกเอกสารเอง** ตาม CIR 2024/2977 Art. 5(4)(b) กล่าวคือ รายการสถานะของ WUA ใช้กับ wallet instance และกุญแจ ส่วนรายการสถานะของผู้ออกเอกสารใช้กับเอกสารที่ผูกกับ wallet unit นั้น ด้วยเหตุนี้ ผู้ออกเอกสารจึงต้องติดตามสถานะของ WIA และ KA ที่ตนเคยตรวจไว้เป็นระยะ เช่น รายวัน เพื่อเพิกถอนเอกสารที่ผูกกับ wallet unit ที่ถูกเพิกถอนแล้ว + +แผนภาพลำดับของ cascade นี้ (wallet provider เพิกถอน WIA/KA → ผู้ออกเอกสารเพิกถอน VC ที่ผูกไว้ → ผู้ตรวจสอบเอกสารรับรู้ผ่านสถานะ VC) อยู่ใน [บทที่ 13 §13.7.5](13-vc-status-and-revocation.md) พร้อมตารางสรุปรูปแบบการเพิกถอนทั้งหมดใน §13.7.6 รายละเอียดกลไกสถานะและการเพิกถอนเอกสารรับรอง ให้อ้างอิง [บทที่ 13 — VC Status และการเพิกถอน](13-vc-status-and-revocation.md) ส่วนรายละเอียดโครงสร้างและ field ของ WIA และ KA รวมถึงรายการสถานะ ให้อ้างอิง EUDI ARF 2.9.0 TS3 + +--- + +## 10.6 สถานะการบังคับใช้ในระบบของไทย + +```mermaid +flowchart LR + P1[ระบบไทย
Trusted List + การตรวจประเมิน
+ หลักฐานตาม Thai profile] --> P2[การยอมรับข้ามพรมแดน
mapping กับ EUDI TS3
เมื่อมี trust agreement] +``` + +**การใช้ในระบบไทย — ต้องระบุใน Thai profile:** + +- สพธอ. ใช้ Trusted List และการตรวจประเมินตามนโยบายการรับรอง Wallet Provider ของไทย +- CIR 2024/2979 Art. 6(1) กำหนดหน้าที่แก่ Wallet Provider ในขอบเขตของกฎหมายสหภาพยุโรป; ไม่ใช่ฐานกฎหมายที่กำหนดหน้าที่แก่ผู้ให้บริการไทยโดยอัตโนมัติ +- หาก Thai profile กำหนดให้ใช้ WUA, ต้องระบุหลักฐานที่ใช้ในแต่ละ protocol. สำหรับ OID4VCI ให้ส่ง Wallet Attestation ตาม Appendix E ใน PAR หรือ Token Request. Key Attestation ตาม Appendix D/F.1 อาจอยู่ใน JOSE header ของ `proofs.jwt` ใน Credential Request. อย่าถือว่า EUDI WUA ทั้งชุดเป็นสมาชิกเดียวกันของ Credential Request body. สำหรับ OID4VP ให้กำหนด WIA/WIA-PoP และวิธีตรวจตาม profile [บทที่ 8](08-implementation-guidelines.md) และ [บทที่ 9](09-trust-model-3.md). + +**การยอมรับข้ามพรมแดน — แนวทางในอนาคต:** + +- ระบบของไทยจะปรับรูปแบบ WUA ให้เป็นไปตาม EUDI Technical Specification TS3 เมื่อ EUDI เผยแพร่ฉบับสมบูรณ์ +- WUA ที่ออกในระบบของไทยจะได้รับการยอมรับข้ามพรมแดนกับ EUDI Wallet เมื่อมีข้อตกลงความน่าเชื่อถือ (trust agreement) ระหว่างไทยกับสหภาพยุโรป + +> **หมายเหตุเชิงประวัติ:** ฉบับร่างก่อนหน้าของโครงการใช้คำว่า Wallet Trust Evidence (WTE) รายงานฉบับนี้เปลี่ยนมาใช้ WUA ตาม EUDI ARF 2.9.0 การอ้างถึง WTE ให้ถือเป็นคำที่เลิกใช้แล้ว บันทึกการเปลี่ยนผ่านอยู่ใน [บทที่ 15 — ภาคผนวก §ข.3.4](15-appendix.md) + +--- + +{/* METADATA (agent-only — not rendered to readers) +## ภาคผนวก ก — เอกสารอ้างอิงและการอ่านเพิ่มเติม + +### ก.1 อ่านรายละเอียดฉบับเต็มของ WUA ได้ที่ EUDI + +ผู้อ่านที่ต้องการรายละเอียดเชิงลึกของ WUA ได้แก่ โครงสร้างข้อมูล ข้อกำหนดระดับสูง ฐานทางกฎหมาย และ Use Case ให้ศึกษาจากต้นฉบับของ EUDI ดังนี้ + +1. **EUDI ARF 2.9.0 — Discussion Topic C: Wallet Unit Attestation (WUA) and Key Attestation** — [https://eudi.dev/2.9.0/discussion-topics/c-wallet-unit-attestation/](https://eudi.dev/2.9.0/discussion-topics/c-wallet-unit-attestation/) +2. **EUDI ARF 2.9.0 — Technical Specification TS3: Wallet Unit Attestations** — [https://eudi.dev/2.9.0/technical-specifications/ts3-wallet-unit-attestation/](https://eudi.dev/2.9.0/technical-specifications/ts3-wallet-unit-attestation/) — นิยาม WUA เป็นคำรวมของ WIA และ KA พร้อม field สถานะ `client_status` (WIA) และ `key_storage_status` (KA) และกำหนดให้ใช้ IETF Token Status List เป็นกลไกเพิกถอนของทั้งสองประเภท ฉบับเต็มอยู่ที่ [eu-digital-identity-wallet/eudi-doc-standards-and-technical-specifications — docs/technical-specifications/ts3-wallet-unit-attestation.md](https://github.com/eu-digital-identity-wallet/eudi-doc-standards-and-technical-specifications/blob/main/docs/technical-specifications/ts3-wallet-unit-attestation.md) +3. **EUDI ARF 2.9.0 — Annex 2 (High-Level Requirements) Topic 9** — [https://eudi.dev/2.9.0/main/](https://eudi.dev/2.9.0/main/) + +### ก.2 มาตรฐานโปรโตคอลและกฎหมายที่ WUA อ้างอิง + +4. **OpenID for Verifiable Credential Issuance 1.0 (Final) — Annex D (Key Attestation), Annex E (Wallet Attestation)** — [https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) +5. **OpenID4VC High Assurance Interoperability Profile 1.0 — §4.4.1 Wallet Attestation, §4.5.1 Key Attestation** — [https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-04.html](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0-04.html) +6. **Regulation (EU) 2024/1183 (eIDAS 2.0)** — Article 5a(4)(a)(viii), 5a(8), 5a(9) +7. **CIR (EU) 2024/2977** — PID issuance — Article 3(5), 3(9), 5(4)(b) +8. **CIR (EU) 2024/2979** — Wallet requirements and Wallet Unit Attestations — Article 6, 7 +9. **CIR (EU) 2024/2982** — Protocols and interfaces — Article 3, 4 + +### ก.3 เอกสารภายในที่เกี่ยวข้อง + +10. [บทที่ 2 — บทนิยาม §2.24](02-definitions.md) — นิยาม WUA +11. [บทที่ 8 — Implementation Guidelines](08-implementation-guidelines.md) — กระแสงาน OID4VCI/OID4VP ฉบับเต็ม +12. [บทที่ 9 — Trust Model 3](09-trust-model-3.md) — แบบจำลองความน่าเชื่อถือ +13. [บทที่ 12 — Key Management และ Trusted List Deployment](12-key-management-and-trustlist-deployment.md) — การจัดการกุญแจและ WSCD +14. [บทที่ 13 — VC Status และการเพิกถอน](13-vc-status-and-revocation.md) — กลไกสถานะ +15. [00-glossary.md](../../00-glossary.md) — อภิธานศัพท์ + +### ก.4 บันทึกการเปลี่ยนแปลง (Changelog) + +| เวอร์ชัน | วันที่ | การเปลี่ยนแปลง | +|---------|-------|----------------| +| 1.8 | 1 ตุลาคม 2569 | ปรับข้อกำหนด WIA/WIA-PoP ใน OID4VP: Wallet รัฐบาลต้องส่งหลักฐานครบคู่ ส่วน Wallet เอกชนเลือกส่งครบคู่หรือไม่ส่งทั้งคู่ได้; หากส่งไม่ครบหรือตรวจไม่ผ่านให้ปฏิเสธ VP; หากข้อมูลจำแนกประเภท Wallet จากแหล่งที่เชื่อถือได้ขาดหาย ไม่สดใหม่ หรือตรวจสอบไม่ได้ ให้ปฏิเสธ VP และห้ามถือว่าข้อมูลที่ขาดหายหมายถึง Wallet เอกชน โดยไม่เปลี่ยนข้อกำหนด OID4VCI | +| 1.7 | 7 สิงหาคม 2569 | แก้ถ้อยคำ "คำร่ม" เป็น **"คำรวม"** ทุกจุด — คำว่า "คำร่ม" เป็นการแปลตรงตัว (calque) จาก umbrella term ซึ่งไม่ปรากฏในพจนานุกรมฉบับราชบัณฑิตยสถาน กฎการห้ามแปลตรงตัวเพิ่มไว้ใน `.agents/thai-regulator-writing-style.md` §7.5 (MASA-167) | +| 1.6 | 6 สิงหาคม 2569 | เพิ่ม cross-reference จาก §5.3 ไปยังแผนภาพลำดับ (sequence diagram) ของ cascade การเพิกถอน WIA/KA และตารางสรุปรูปแบบการเพิกถอนใน [บทที่ 13 §13.7.5–§13.7.6](13-vc-status-and-revocation.md) ตามข้อสังเกตของผู้ทบทวนที่ขอแผนภาพลำดับของทุกรูปแบบการเพิกถอน (MASA-158) | +| 1.5 | 6 สิงหาคม 2569 | แยก WUA เป็นคำรวม (umbrella term) ที่ประกอบด้วย Wallet Instance Attestation (WIA) และ Key Attestation (KA) — เพิ่ม WIA/KA ใน §2.1 เพิ่ม §2.3 อธิบายการแบ่งประเภท และเขียน §5.2–§5.3 ใหม่ให้การเพิกถอนและสถานะของ WIA และ KA เป็นไปตาม EUDI (IETF Token Status List ผ่าน field `client_status` และ `key_storage_status` ลงลายมือชื่อด้วย ES256/384/512) ตาม EUDI ARF 2.9.0 TS3 แก้ข้อความในเวอร์ชัน 1.4 ที่ระบุว่า WUA มิได้ใช้ IETF Token Status List ให้ถูกต้อง (สถานะ WUA ใช้ Token Status List แต่เป็นคนละรายการกับสถานะเอกสารรับรอง) (MASA-158) | +| 1.4 | 6 สิงหาคม 2569 | แก้ §5 ให้ถูกต้อง — เพิ่ม §5.1 อายุการใช้งานของ WUA (WUA เป็นหลักฐานอายุสั้น ใช้ครั้งเดียว/ออกเป็นชุด เพื่อลด linkability ตาม HLR WUA_09 และ Topic A) และเพิ่ม §5.3 แยกกลไกสถานะ WUA (กลไกของ wallet provider แบบ privacy-preserving ตาม EUDI TS3) ออกจากกลไกสถานะเอกสารรับรอง (IETF Token Status List) แก้ข้อความเดิมที่ระบุว่าสถานะ WUA สอดคล้องกับ IETF Token Status List ให้ถูกต้อง (MASA-158) | +| 1.3 | 6 สิงหาคม 2569 | ยุบเนื้อหาให้กระชับ ปรับกรอบให้ WUA เป็นแนวคิดของ EUDI ที่ไทยนำมาใช้ ตัดการกล่าวซ้ำรายละเอียดเชิงมาตรฐาน (ฐานกฎหมายรายมาตรา โครงสร้างข้อมูล ตาราง HLR การเปรียบเทียบ WTE) และชี้ไปยังต้นฉบับ EUDI คงเฉพาะการใช้งานในกระแสงาน OID4VCI/OID4VP และความสัมพันธ์กับ Trusted List (MASA-158) | +| 1.2 | 6 สิงหาคม 2569 | ปรับสถานะ WUA เป็นข้อบังคับ (MANDATORY) ในขั้นปัจจุบัน (MASA-157) | +| 1.1 | 6 สิงหาคม 2569 | ตัดการอ้างถึง Phase 2/Phase 3 และยุบแผนแบ่งระยะ (MASA-157) | +| 1.0 | 4 สิงหาคม 2569 | ฉบับร่างแรก — แทนที่ WTE ด้วย WUA ตาม EUDI ARF 2.9.0 (MASA-127) | + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 9 — Trust Model 3 (Trusted List + DID)](09-trust-model-3.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 11 — Cryptographic Suites ➡️](11-cryptographic-suites.md) diff --git a/docs/thai-vc-arf/11-cryptographic-suites.md b/docs/thai-vc-arf/11-cryptographic-suites.md new file mode 100644 index 0000000..5e816d9 --- /dev/null +++ b/docs/thai-vc-arf/11-cryptographic-suites.md @@ -0,0 +1,127 @@ +--- +description: "11. Cryptographic Suites — Thai VC ARF 2.0 DRAFT 0" +--- + +# 11. Cryptographic Suites + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📐 Specification — Draft สำหรับการทบทวนเชิงกำกับดูแล (S8, MASA-129 → MASA-150) +> **เวอร์ชันเอกสาร:** 1.0.1 (2026-08-06) — ลบกุญแจ Level 2 (Pairwise Key ต่อ Verifier) และแถว Pairwise pseudonym hash ออกจากตาราง algorithm profile ตามมติให้ยกเลิกการใช้ pairwise DID (MASA-157) +> **แหล่งข้อมูล:** เอกสารสถาปัตยกรรมภายใต้ `th/architecture/` เท่านั้น — ดู §11.5 References +> **ขอบเขต:** เอกสารกำกับดูแลและข้อกำหนดสำหรับการตรวจประเมิน ห้ามตีความเป็นผลทดสอบ production +> **เอกสารที่เกี่ยวข้อง:** [12-key-management-and-trustlist-deployment.md](12-key-management-and-trustlist-deployment.md), [13-vc-status-and-revocation.md](13-vc-status-and-revocation.md), [14-security-threat-model-and-conformance.md](14-security-threat-model-and-conformance.md), [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + +บทที่ 11 กำหนดข้อกำหนดด้าน cryptographic suites สำหรับกระเป๋าเอกสารดิจิทัล (Wallet) ผู้ออกเอกสาร (Issuer) และสำนักงานพัฒนาธุรกรรมทางอิเล็กทรอนิกส์ (สพธอ.) เดิมเนื้อหานี้เป็นส่วนหนึ่งของบทความมั่นคงปลอดภัยที่รวมสามเรื่องไว้ด้วยกัน คือ Cryptographic Suites, Key Management และ VC Status/Revocation รายงานฉบับนี้แยกเรื่อง Cryptographic Suites ออกมาเป็นบทเฉพาะ เพื่อให้อ้างอิงได้ชัดเจน ทั้งนี้ เรื่อง Key Management อยู่ในบทที่ 12 และเรื่อง VC Status/Revocation อยู่ในบทที่ 13 + +--- + +## 11.1 ขอบเขตและการจำแนกข้อความ + +บทนี้กำหนดข้อกำหนดด้าน Cryptographic Suites ที่ ARF v1.1 ยังไม่ครอบคลุม ครอบคลุม algorithm profile ของกุญแจฝ่าย Wallet suite ที่ปรากฏในเอกสารสถาปัตยกรรม และการเปลี่ยน algorithm พร้อมแผนระยะยาว ส่วนเรื่อง Key Management/Rotation อยู่ในบทที่ 12 และเรื่อง VC Revocation ด้วย IETF Token Status List อยู่ในบทที่ 13 + +**กฎการอ้างอิงของหัวข้อนี้:** ข้อกำหนดทุกข้อ **MUST** สืบย้อนไปยังไฟล์ภายใต้ `th/architecture/` มาตรฐานภายนอกอ้างผ่านไฟล์สถาปัตยกรรมที่ระบุมาตรฐานนั้นไว้แล้ว หัวข้อนี้ **MUST NOT** สร้างข้อกำหนดใหม่ที่ไม่มีฐานในเอกสารสถาปัตยกรรม + +การจำแนกข้อความ: + +| ป้าย | ความหมาย | +|------|----------| +| **[ข้อเท็จจริง]** | ข้อความที่ปรากฏในเอกสารสถาปัตยกรรมโดยตรง พร้อมเลขอ้างอิง | +| **[ข้อวิเคราะห์]** | การประมวลข้อเท็จจริงหลายแหล่งเข้าเป็นข้อกำหนดเชิงกำกับดูแล | +| **[ช่องว่าง]** | ประเด็นที่เอกสารสถาปัตยกรรมยังขัดกันเองหรือยังไม่กำหนด ต้องมีมติก่อนบังคับใช้ | + +คำ **MUST / MUST NOT / SHOULD / MAY** ใช้ตามความหมายเชิงข้อกำหนด: ต้อง / ห้าม / ควร / อาจ + +--- + +## 11.2 Algorithm Profile ของกุญแจฝ่าย Wallet + +**[ข้อเท็จจริง]** เอกสาร Key Derivation กำหนด algorithm profile ตามระดับกุญแจไว้ดังนี้ [A-KD01 §9] + +| ระดับกุญแจ | การใช้งาน | Recommended | รองรับเพิ่มเติม | +|-----------|-----------|:-----------:|:--------------:| +| Level 1 | Wallet Identity Key signing | `EdDSA` (Ed25519) | `ES256` | +| Level 3 | Per-credential `cnf` signing (KB-JWT) | `EdDSA` (Ed25519) | `ES256` (ค่าปริยายของ OID4VP) | +| — | BIP32 derivation | `HMAC-SHA512` | — | + +> **หมายเหตุ:** ตารางข้ามจาก Level 1 ไป Level 3 ตามมติให้ยกเลิก Level 2 ซึ่งเดิมกำหนด Pairwise Key ต่อ Verifier (MASA-157) รายงานฉบับนี้คงหมายเลข Level 1 และ Level 3 ไว้ตามเอกสารสถาปัตยกรรมต้นทาง +> +> **หมายเหตุเรื่อง Ed25519:** `EdDSA` (Ed25519) เป็น algorithm ที่แนะนำ (recommended) สำหรับทุกระดับกุญแจ อย่างไรก็ตาม KB-JWT ใน Level 3 มีค่าปริยายเป็น `ES256` ตามมาตรฐาน OID4VP Wallet จึงรองรับทั้งสองและเลือกตามความสามารถของฮาร์ดแวร์ — ดู §11.4 สำหรับแผน transition + +**[ข้อเท็จจริง]** KB-JWT ใช้ `ES256` เพราะเป็นค่าปริยายของ OID4VP ส่วนกุญแจระดับอื่นเลือก `EdDSA` ตามข้อกำหนด VCTF-06 [A-KD01 §9] + +**[ข้อวิเคราะห์ — การรองรับทั้ง Ed25519 และ ES256]** ระบบยังคงรองรับ `EdDSA` (Ed25519) และถือว่าเป็น algorithm หลัก อย่างไรก็ตาม อุปกรณ์ Apple (iOS) ไม่รองรับ Ed25519 ใน Secure Enclave ทำให้ไม่สามารถเก็บกุญแจ Ed25519 ใน hardware-backed keystore ของ Apple ได้ เมื่อใดก็ตามที่เป็นไปได้ ระบบจะใช้ `ES256` (ECDSA P-256) แทนบนอุปกรณ์ที่ไม่รองรับ Ed25519 ในฮาร์ดแวร์ โดย ES256 ได้รับการรองรับจาก Secure Enclave ของ Apple และ StrongBox ของ Android ทั้งคู่ สรุปคือ Wallet รองรับทั้ง `EdDSA` และ `ES256` และจะเลือกใช้ตามความสามารถของฮาร์ดแวร์ — ถ้าฮาร์ดแวร์รองรับ Ed25519 ก็ใช้ EdDSA ถ้าไม่ ก็ใช้ ES256 + +**[ข้อวิเคราะห์]** Wallet **MUST** รองรับทั้ง `EdDSA` และ `ES256` สำหรับทุกระดับกุญแจ Issuer และ Verifier **MUST** ยอมรับทั้งสอง suite เป็นอย่างน้อย มิฉะนั้น Wallet ที่ทำตามข้อกำหนดจะใช้งานร่วมกันไม่ได้ ส่วน `HMAC-SHA512` สำหรับ BIP32 derivation **MUST** รองรับเป็นกรณีบังคับ + +--- + +## 11.3 Suite ที่ปรากฏในเอกสารสถาปัตยกรรม + +**[ข้อเท็จจริง]** ตัวอย่างในเอกสารสถาปัตยกรรมประกาศ suite ดังนี้ + +| วัตถุ | ผู้ลงลายมือชื่อ | ค่า `alg` หรือกุญแจ | ที่มา | +|-------|----------------|--------------------|-------| +| Issuer metadata `proof_types_supported` | Wallet | `["EdDSA", "ES256"]` | [A-ARCH §ลำดับที่ 2B] | +| Holder binding `cnf` JWK | Wallet | `kty=OKP`, `crv=Ed25519` (recommended) หรือ `kty=EC`, `crv=P-256` | [A-ARCH §ลำดับที่ 2B] | +| VC Status List Token | Issuer | `EdDSA` หรือ `ES256` | [A-ARCH §ลำดับที่ 3] | +| Issuer DID Document verification key (ตัวอย่าง DOPA) | Issuer | `kty=OKP`, `crv=Ed25519` (recommended) หรือ `kty=EC`, `crv=P-256` | [A-ARCH §6] | + +**[ข้อวิเคราะห์]** ผู้ตรวจ **MUST** ผูก `alg` กับชนิดและ curve ของกุญแจให้ตรงกัน โดย `ES256` ต้องคู่กับ `kty=EC` และ `crv=P-256` ส่วน `EdDSA` ต้องคู่กับกุญแจ Ed25519 การพบ `alg` ที่ไม่ตรงกับกุญแจ **MUST** ถือว่าตรวจไม่ผ่านและปฏิเสธทันที + +**[ข้อวิเคราะห์]** ผู้ตรวจ **MUST NOT** เลือก algorithm จาก JWT header เพียงลำพัง ต้องตรวจชนิดกุญแจและ algorithm ที่อนุญาตตาม Thai profile ประกอบกับกุญแจที่ประกาศใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[].ServiceInformation.ServiceDigitalIdentity` ทั้งนี้ [JWT ตัวอย่าง](../research/42-trusted-list-lote-jwt-format.md) ยังไม่แสดงฟิลด์ suite รายบริการ จึงห้ามสมมติว่ามีฟิลด์ดังกล่าวใน Trusted List; หาก profile ที่ประกาศใช้ไม่ระบุวิธีจับคู่ algorithm กับกุญแจ ผู้ตรวจต้องปฏิเสธการตัดสินใจด้านความน่าเชื่อถือ สถาปัตยกรรมกำหนดให้ใช้ **Trusted List เป็นแหล่งข้อมูลอ้างอิงหลักของกุญแจ** [A-TM §6, §I7] + +--- + +## 11.4 การเปลี่ยน Algorithm และแผนระยะยาว + +**[ข้อวิเคราะห์ — แผน transition จาก Ed25519 ไป ES256]** `EdDSA` (Ed25519) เป็น algorithm ที่แนะนำและระบบรองรับอยู่ อย่างไรก็ตาม อุปกรณ์ Apple (iOS) ไม่รองรับ Ed25519 ใน Secure Enclave ทำให้ไม่สามารถเก็บกุญแจ Ed25519 ใน hardware-backed keystore ของ Apple ได้ ระบบจึงวางแผน transition ไปสู่ `ES256` (ECDSA P-256) ซึ่งได้รับการรองรับจาก Secure Enclave ของ Apple และ StrongBox ของ Android ทั้งคู่ ทั้งนี้ Wallet ยังคงรองรับ `EdDSA` ในระหว่าง transition และจะเลือกใช้ตามความสามารถของฮาร์ดแวร์ — ถ้าฮาร์ดแวร์รองรับ Ed25519 ก็ใช้ EdDSA ถ้าไม่ ก็ใช้ ES256 + +| ระยะ | algorithm | สถานะ | เงื่อนไข | +|------|----------|-------|----------| +| ปัจจุบัน | `EdDSA` (Ed25519) | Recommended | ฮาร์ดแวร์รองรับ (Android StrongBox) | +| ปัจจุบัน | `ES256` (P-256) | รองรับ | ฮาร์ดแวร์ไม่รองรับ Ed25519 (Apple Secure Enclave) | +| อนาคต | `ES256` (P-256) | เป้าหมาย transition | รอการยืนยันจากคณะทำงาน — To be confirmed | + +> **หมายเหตุ:** แผน transition ด้านบนยังไม่ได้รับการยืนยัน (To be confirmed) รอมติคณะทำงาน VC ระบบไม่ทิ้งการรองรับ Ed25519 แม้หลัง transition จะเสร็จ เพื่อรองรับ credential เดิมที่ลงลายมือชื่อด้วย EdDSA + +**[ข้อเท็จจริง]** การเลิกใช้ algorithm เป็นหนึ่งใน rotation trigger ที่กำหนดไว้ ตัวอย่างที่ระบุคือ `ES256 → ES384` [A-KD08 §1] + +**[ข้อเท็จจริง]** แผนย้ายไปสู่ Post-Quantum Cryptography (PQC) กำหนดกรอบเวลาปี 2028 เป็นต้นไป เส้นทางที่ระบุคือ `Ed25519 → ML-DSA-65 (CRYSTALS-Dilithium)` [A-TM §9] + +**[ข้อวิเคราะห์]** ETDA ในฐานะ Trust Framework Authority **MUST** ประกาศ algorithm registry แบบมีเวอร์ชัน ระบุสถานะ `allowed` / `deprecated` / `prohibited` และวันที่มีผล เพราะการเปลี่ยน algorithm เป็น rotation trigger ที่ทุกฝ่ายต้องตรวจสอบได้ร่วมกัน + +**[ข้อวิเคราะห์]** การรองรับหลาย suite พร้อมกัน **MUST NOT** เปิดช่องให้ downgrade กล่าวคือ credential ที่ลงลายมือชื่อด้วย suite หนึ่ง **MUST** ตรวจด้วย suite นั้นและกุญแจที่ผูกกันเท่านั้น + +--- + +{/* METADATA (agent-only — not rendered to readers) +## 11.5 References + +### 11.5.1 แหล่งข้อมูลหลัก — เอกสารสถาปัตยกรรมในโครงการ + +วันที่เข้าถึงทุกไฟล์: **2026-08-04** โดยอ้างสถานะ repository ณ เวอร์ชันเอกสาร 1.0.0 + +| รหัส | เอกสาร | เวอร์ชันที่ระบุในไฟล์ | +|------|--------|---------------------| +| A-ARCH | [../architecture/01-architecture.md](../architecture/01-architecture.md) — สถาปัตยกรรมความน่าเชื่อถือแบบ Trusted List | v2.3 | +| A-TM | [../architecture/phase2/trustlist/10-threat-model.md](../architecture/phase2/trustlist/10-threat-model.md) — Trust List Verification: Threat Model & Mitigations | Draft v1.0 | +| A-KD01 | [../architecture/phase2/key-derivation/01-key-derivation-architecture.md](../architecture/phase2/key-derivation/01-key-derivation-architecture.md) — Key Derivation Architecture | Draft v1.0 | +| A-KD08 | [../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md](../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md) — Key Rotation & Lifecycle | Draft v1.0 | + +### 11.5.2 มาตรฐานภายนอกที่อ้างผ่านเอกสารข้างต้น + +เอกสารนี้ไม่อ้างมาตรฐานภายนอกโดยตรง รายการต่อไปนี้คือมาตรฐานที่ไฟล์สถาปัตยกรรมข้างต้นระบุไว้เป็นแหล่งข้อมูลของตน เฉพาะส่วนที่เกี่ยวข้องกับ cryptographic suites ในบทนี้ + +| มาตรฐาน | ส่วนที่อ้าง | ไฟล์ที่อ้าง | +|---------|-----------|-----------| +| OID4VP 1.0 Final | ค่าปริยาย `alg` ของ KB-JWT (`ES256`) | A-KD01 §9, A-ARCH §ลำดับที่ 2B | +| SD-JWT VC (draft-ietf-oauth-sd-jwt-vc) | §4.1 Key Binding JWT | A-KD01 §9, A-ARCH §ลำดับที่ 2B | +| EU eIDAS ARF | เส้นทางการย้ายไป PQC (`Ed25519 → ML-DSA-65`) และการเลิกใช้ algorithm | A-TM §9, A-KD08 §1 | + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 10 — Wallet Unit Attestation (WUA)](10-wallet-unit-attestation.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 12 — Key Management และ Trusted List Deployment ➡️](12-key-management-and-trustlist-deployment.md) diff --git a/docs/thai-vc-arf/12-key-management-and-trustlist-deployment.md b/docs/thai-vc-arf/12-key-management-and-trustlist-deployment.md new file mode 100644 index 0000000..1122b79 --- /dev/null +++ b/docs/thai-vc-arf/12-key-management-and-trustlist-deployment.md @@ -0,0 +1,272 @@ +--- +description: "12. Key Management, Key Rotation และ Trusted List Deployment — Thai VC ARF 2.0 DRAFT 0" +--- + +# 12. Key Management, Key Rotation และ Trusted List Deployment + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📐 Specification — Draft สำหรับการทบทวนเชิงกำกับดูแล (S8, MASA-129 → MASA-153) +> **เวอร์ชันเอกสาร:** 1.2.5 (2026-09-23) — ประวัติการแก้ไขทั้งหมดดูได้ที่ §12.11 บันทึกการเปลี่ยนแปลง +> **แหล่งข้อมูล:** เอกสารสถาปัตยกรรมภายใต้ `th/architecture/` และงานวิจัยภายใน `th/research/` — ดู §12.10 References +> **ขอบเขต:** เอกสารกำกับดูแลและข้อกำหนดสำหรับการตรวจประเมิน ห้ามตีความเป็นผลทดสอบ production +> **เอกสารที่เกี่ยวข้อง:** [11-cryptographic-suites.md](11-cryptographic-suites.md), [13-vc-status-and-revocation.md](13-vc-status-and-revocation.md), [14-security-threat-model-and-conformance.md](14-security-threat-model-and-conformance.md), [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + +บทที่ 12 กำหนดข้อกำหนดการจัดการกุญแจ (key management) การหมุนเวียนกุญแจ (key rotation) และการวางระบบ Trusted List (Trusted List deployment) รวมถึงการควบคุมระดับเครือข่ายและการดึง Trusted List ผ่าน CDN ด้าน cryptographic suites อยู่ในบทที่ 11 และการเพิกถอน VC ด้วย status list อยู่ในบทที่ 13 รายละเอียดประเภท จำนวนไฟล์ รูปแบบ และโครงสร้างข้อมูลของ Trust List อยู่ใน [ข้อกำหนดการเผยแพร่ Trust List](12.1-trust-list-publication-profile.md) + +--- + +## 12.1 ขอบเขตและการจำแนกข้อความ + +หัวข้อนี้รวมข้อกำหนดด้านการจัดการกุญแจและการวางระบบ Trusted List คือ (1) Key Management และ Key Rotation (2) Resolver และ Network Controls และ (3) การดึง Trusted List แบบ lazy ผ่าน CDN ด้วย HTTP `GET` + +**กฎการอ้างอิงของหัวข้อนี้:** ข้อกำหนดทุกข้อ **MUST** สืบย้อนไปยังไฟล์ภายใต้ `th/architecture/` มาตรฐานภายนอกอ้างผ่านไฟล์สถาปัตยกรรมที่ระบุมาตรฐานนั้นไว้แล้ว หัวข้อนี้ **MUST NOT** สร้างข้อกำหนดใหม่ที่ไม่มีฐานในเอกสารสถาปัตยกรรม + +การจำแนกข้อความ: + +| ป้าย | ความหมาย | +|------|----------| +| **[ข้อเท็จจริง]** | ข้อความที่ปรากฏในเอกสารสถาปัตยกรรมโดยตรง พร้อมเลขอ้างอิง | +| **[ข้อวิเคราะห์]** | การประมวลข้อเท็จจริงหลายแหล่งเข้าเป็นข้อกำหนดเชิงกำกับดูแล | +| **[ช่องว่าง]** | ประเด็นที่เอกสารสถาปัตยกรรมยังขัดกันเองหรือยังไม่กำหนด ต้องมีมติก่อนบังคับใช้ | + +คำ **MUST / MUST NOT / SHOULD / MAY** ใช้ตามความหมายเชิงข้อกำหนด: ต้อง / ห้าม / ควร / อาจ + +--- + +## 12.2 ข้อกำหนดการเก็บกุญแจ + +**[ข้อเท็จจริง]** ข้อกำหนดการจัดการกุญแจแยกตามผู้ถือกุญแจสี่กลุ่ม ได้แก่ ผู้ออกเอกสาร (issuer) ระบบ Trusted List ของ สพธอ. กระเป๋าเอกสารดิจิทัลของผู้ถือเอกสาร (holder) และผู้ตรวจสอบเอกสาร (verifier) ทั้งนี้ ผู้ตรวจสอบเอกสารต้องมีกุญแจของตนเองเช่นกัน เนื่องจากต้องลงลายมือชื่อ Request Object คือ `client_metadata_jwt` ในคำขอ VP ตาม OID4VP เพื่อให้กระเป๋าเอกสารดิจิทัลตรวจได้ว่าคำขอมาจากผู้ตรวจสอบเอกสารตัวจริง [A-ACP §1, §5.2] + +| ผู้ถือกุญแจ | ข้อกำหนด | หลักฐาน | ที่มา | +|------------|---------|---------|-------| +| Issuer | กุญแจลงลายมือชื่ออยู่ใน HSM ระดับ FIPS 140-2 Level 3 | ใบรับรอง HSM | [A-ARCH §8 I1] | +| Issuer | พิธีสร้างกุญแจต้องมีพยานและบันทึกวิดีโอ | รายงานพิธี | [A-ARCH §8 I2] | +| Issuer | แผนหมุนเวียนกุญแจประจำปีเป็นเอกสาร | เอกสารนโยบาย | [A-ARCH §8 I3] | +| ETDA (Trusted List) | กุญแจอยู่ใน HSM ที่ export ไม่ได้ ระดับ FIPS 140-3 Level 3 ขึ้นไป | — | [A-TM §T1] | +| ETDA (Trusted List) | การลงลายมือชื่อ TL ต้องใช้ quorum แบบ M-of-N เช่น 3-of-5 | — | [A-TM §T1] | +| ETDA (Trusted List) | มี emergency key แบบ offline air-gapped สำหรับเพิกถอนกุญแจที่ถูก compromise | — | [A-TM §T1] | +| Wallet (Holder) | กุญแจต้องสร้างใน WSCD และ export ไม่ได้ | WSCD attestation chain | [A-TM §W4], [A-KD06] | +| Verifier (RP) | มีกุญแจลงลายมือชื่อ Request Object (`client_metadata_jwt`) เป็นของตนเอง โดยเลือกวิธีเก็บกุญแจตามระดับความเชื่อมั่นที่ §12.2.1 กำหนด | บันทึกวิธีเก็บกุญแจตามตารางใน §12.2.1 | [A-ACP §1, §5.2] | +| Verifier (RP) | public key ที่ใช้ตรวจ Request Object **MUST** ประกาศให้ตรวจสอบได้ ผ่านทางใดทางหนึ่ง คือ (ก) เผยแพร่ใน Trusted List entry ของผู้ตรวจสอบเอกสาร หรือ (ข) เผยแพร่เป็น DID Document ที่ `did:web` resolve ได้ที่ `/.well-known/did.json` | Trusted List entry หรือ DID Document | [A-VRP] | + +**[ข้อเท็จจริง]** Issuer **MUST** ปฏิเสธกุญแจที่ไม่มี `key_storage_verified` เป็น `hardware` หรือ `secure_enclave` [A-TM §W4] ส่วน flag `requires_hardware_key` ที่เอกสาร threat model เสนอ ยังไม่ปรากฏใน [JWT ตัวอย่างแบบ LoTE](../research/42-trusted-list-lote-jwt-format.md) จึงต้องกำหนดใน Thai profile ก่อนใช้เป็นข้อมูลจาก Trusted List + +**[ช่องว่าง G-01 — มีมติกำกับดูแลแล้ว]** เอกสารสถาปัตยกรรมกำหนด FIPS 140-2 Level 3 สำหรับ HSM ของ Issuer [A-ARCH §8 I1] แต่กำหนด FIPS 140-3 Level 3 ขึ้นไปสำหรับ HSM ของ ETDA [A-TM §T1] ทั้งสองข้อใช้กับผู้ถือกุญแจต่างกลุ่มจึงไม่ขัดกันในทางเทคนิค รายงานฉบับนี้กำหนดข้อยุติดังนี้ ผู้ถือกุญแจลงลายมือชื่อทุกกลุ่ม **ต้อง** ใช้ HSM ที่ผ่านการรับรองอย่างน้อยระดับ **FIPS 140-2 Level 3** ตาม NIST FIPS PUB 140-2 [N1] เป็นเกณฑ์ขั้นต่ำ ทั้งนี้ เนื่องจาก NIST กำหนดให้ใบรับรองตาม FIPS 140-2 เปลี่ยนสถานะเป็น Historical ตั้งแต่วันที่ 21 กันยายน 2569 (ค.ศ. 2026) [N3] จึงให้ยอมรับ **FIPS 140-3 Level 3** ตาม NIST FIPS PUB 140-3 [N2] เป็นมาตรฐานสืบทอดที่เทียบเท่าหรือสูงกว่า และ HSM ที่ได้รับการรับรองใหม่ภายหลังวันดังกล่าว **ต้อง** เป็น FIPS 140-3 ด้วยเหตุนี้ HSM ของ ETDA ที่กำหนดเป็น FIPS 140-3 Level 3 จึงเป็นไปตามเกณฑ์ขั้นต่ำนี้อยู่แล้ว + +### 12.2.1 ทางเลือกการเก็บกุญแจของผู้ตรวจสอบเอกสาร + +**[ช่องว่าง G-07 — มีมติกำกับดูแลแล้ว]** เอกสารสถาปัตยกรรมกำหนดให้ผู้ตรวจสอบเอกสารลงลายมือชื่อ Request Object คือ `client_metadata_jwt` ด้วยกุญแจของตนเอง [A-ACP §5.2] และกำหนดสภาพเป้าหมายให้เก็บกุญแจไว้ในฮาร์ดแวร์ที่ปลอดภัย เช่น HSM หรือ Secure Element แต่การบังคับใช้ HSM กับผู้ตรวจสอบเอกสารทุกรายมีต้นทุนสูง [A-HSM §2] ที่ประชุมกำกับดูแลจึงมีมติกำหนดทางเลือกการเก็บกุญแจของผู้ตรวจสอบเอกสาร 3 แบบตามระดับความเชื่อมั่นของกุญแจ (key assurance) โดยผู้ตรวจสอบเอกสาร **อาจ** เลือกใช้แบบใดแบบหนึ่งได้ตามความพร้อมและงบประมาณ ดังนี้ + +**[ข้อเท็จจริง]** นิยาม WSCD ครอบคลุมอุปกรณ์เข้ารหัสลับที่ปลอดภัยหลายชนิด ได้แก่ Secure Element, StrongBox, Trusted Execution Environment (TEE) และ HSM [บทที่ 2 §2.36](02-definitions.md) ทางเลือกแบบ TEE จึงรองรับการเก็บกุญแจด้วยฮาร์ดแวร์อีกประเภทหนึ่ง + +| ตัวเลือกการเก็บกุญแจ Verifier | ระดับข้อบังคับ | key assurance | ต้นทุน | การประกาศ public key | +|------------------------------|:-------------:|:-------------:|:------:|----------------------| +| Soft key (software key store) | **MAY** | ต่ำ | ต่ำ | Trusted List entry หรือ `did:web` ที่ `/.well-known/did.json` | +| TEE หรือ WSCD (Secure Element / StrongBox) | **MAY** | กลาง | กลาง | Trusted List entry หรือ `did:web` ที่ `/.well-known/did.json` | +| HSM | **MAY** | สูง | สูง | Trusted List entry หรือ `did:web` ที่ `/.well-known/did.json` | + +**[ข้อวิเคราะห์]** ทั้งสามแบบให้ระดับความเชื่อมั่นของกุญแจต่างกัน รายงานฉบับนี้จึงกำหนดข้อยุติดังนี้ (1) ผู้ตรวจสอบเอกสาร **อาจ** ใช้ soft key ได้ในระยะเปลี่ยนผ่านหรือเมื่อยังไม่พร้อมด้านฮาร์ดแวร์ (2) ผู้ตรวจสอบเอกสาร **ควร** ใช้ TEE หรือ WSCD เป็นอย่างน้อยเพื่อยกระดับความเชื่อมั่นของกุญแจ และ (3) ผู้ตรวจสอบเอกสารที่ต้องการความเชื่อมั่นสูงสุด **อาจ** ใช้ HSM ทั้งนี้ HSM ให้ความเชื่อมั่นสูงสุดแต่มีต้นทุนสูงตามที่ [A-HSM §2] ระบุ จึงไม่บังคับกับผู้ตรวจสอบเอกสารทุกราย + +**[ข้อวิเคราะห์]** ไม่ว่าผู้ตรวจสอบเอกสารเลือกวิธีเก็บกุญแจแบบใด **ต้อง** ทำตามเงื่อนไขทุกข้อดังนี้ + +- public key ที่คู่กับกุญแจลงลายมือชื่อ **MUST** ประกาศให้ตรวจสอบได้ ผ่านทางใดทางหนึ่ง คือ (ก) ผูกกับ Verifier entry ใน Trusted List ของผู้ตรวจสอบเอกสาร หรือ (ข) เผยแพร่เป็น DID Document ที่ `did:web` published ที่ `https:///.well-known/did.json` [A-VRP §7] +- Verifier entry **MUST** อยู่ใน Trusted List และผู้ตรวจสอบเอกสาร **MUST NOT** ใช้ `client_id` แบบ `redirect_uri` ที่ไม่มี DID เพราะกระเป๋าเอกสารดิจิทัลจะถือว่าเป็นผู้ตรวจสอบเอกสารที่ไม่ได้ลงทะเบียน [A-VRP §7] +- ผู้ตรวจสอบเอกสารที่ใช้ soft key **SHOULD** ย้ายไปเก็บกุญแจในฮาร์ดแวร์ที่ปลอดภัย คือ TEE, WSCD หรือ HSM เมื่อพร้อม เพื่อยกระดับความเชื่อมั่นของกุญแจ [A-HSM §2] + +--- + +## 12.3 Rotation Trigger + +**[ข้อเท็จจริง]** trigger ที่ทำให้ต้องหมุนเวียนกุญแจ [A-KD08 §1] + +| Trigger | การดำเนินการ | +|---------|-------------| +| ครบกำหนดเวลา | หมุนเวียนรายปีสำหรับ Level 1 และหมุนเวียนต่อใบเมื่อ VC ออกใหม่ | +| ผู้ใช้แจ้งอุปกรณ์สูญหาย (compromise) | ล้างข้อมูลฉุกเฉินแล้วกู้คืน | +| OS ยกเลิกกุญแจ (เปลี่ยน biometric) | iOS/Android ยกเลิกกุญแจ จึงต้องออก VC ใหม่ | +| ผู้ใช้ร้องขอ | หมุนเวียนด้วยตนเอง | +| เลิกใช้ algorithm | ย้าย algorithm เช่น `ES256 → ES384` | + +--- + +## 12.4 Lifecycle ตามระดับกุญแจ + +**[ข้อเท็จจริง]** นโยบายและผลกระทบของการหมุนเวียนแต่ละระดับ [A-KD08 §2] + +| ระดับ | ค่าปริยาย | ผลกระทบ | +|-------|----------|---------| +| Level 0 (Master Seed) | ไม่หมุนเวียน เพราะ mnemonic เป็นค่าถาวร | ถ้าสร้างใหม่เท่ากับได้ตัวตนใหม่ ต้องออก VC ใหม่ทั้งหมด | +| Level 1 (Wallet Identity) | หมุนเวียนรายปี เป็นข้อแนะนำ ไม่บังคับ | ได้ `did:key` หรือ `did:jwk` ใหม่ VC เดิมไม่กระทบ | +| Level 3 (Per-credential `cnf`) | เมื่อ VC ออกใหม่ | สร้างกุญแจใหม่ใน WSCD แล้วขอ VC ใหม่ | + +**[ข้อเท็จจริง]** ข้อแลกเปลี่ยนที่บันทึกไว้คือ หมุนเวียนถี่ได้ forward secrecy ดีขึ้นแต่บริการสะดุดและมีต้นทุน หมุนเวียนห่างระบบเสถียรแต่หน้าต่างความเสี่ยงจาก compromise ยาวขึ้น ข้อสรุปที่แนะนำคือหมุนเวียนรายปีสำหรับ Level 1 ร่วมกับหมุนเวียนตาม trigger ต่อใบ [A-KD08 §10] + +--- + +## 12.5 Rotation Endpoint + +**[ข้อเท็จจริง]** Issuer **SHOULD** เปิด endpoint `POST /credential/rotate` โดยรับ `old_vc`, `proof` ซึ่งเป็น PoP ที่ลงลายมือชื่อด้วยกุญแจ `cnf` เดิม และ `new_cnf` แล้วส่งกลับ credential ใหม่ [A-KD08 §7] + +**[ข้อเท็จจริง]** ลำดับการหมุนเวียนต่อใบคือ Wallet สร้างกุญแจใหม่ใน WSCD ลงลายมือชื่อคำขอด้วยกุญแจเดิมเพื่อพิสูจน์ความเป็นเจ้าของ Issuer ตรวจ PoP ด้วยกุญแจเดิม ออก VC ใหม่ที่ผูกกับ `cnf` ใหม่ แล้ว **mark VC เดิมใน Status List ว่า `rotated`** จากนั้น Wallet ลบ key handle เดิม [A-KD08 §3] + +**[ข้อวิเคราะห์]** ขั้นตอน mark VC เดิมเป็น `rotated` **MUST** สำเร็จก่อนที่ Wallet จะลบ key handle เดิม เพราะถ้าลบก่อนแล้วการ mark ล้มเหลว VC เดิมจะยังอยู่ในสถานะใช้ได้ โดยไม่มีผู้ใดถือกุญแจที่จะเพิกถอนได้ + +--- + +## 12.6 การจัดการกุญแจที่ถูก Compromise + +**[ข้อเท็จจริง]** ข้อกำหนดที่เกี่ยวข้องกับกุญแจ Issuer ที่ถูกขโมย [A-TM §I3, §I7] + +- Thai profile **MUST** กำหนดวิธีระบุช่วงเวลาที่กุญแจแต่ละรายการมีผล ก่อนใช้ Trusted List ตัดสินความน่าเชื่อถือ; JWT ตัวอย่างใน [research/42](../research/42-trusted-list-lote-jwt-format.md) ยังไม่แสดง `valid_from` และ `valid_until` ต่อกุญแจ +- Wallet **MUST** ตรวจช่วงเวลาของกุญแจตาม Thai profile ที่ประกาศใช้ หากไม่มีข้อมูลเพียงพอให้ปฏิเสธ +- การเพิกถอนฉุกเฉิน **MUST** แพร่กระจายภายใน 1 ชั่วโมง +- เมื่อ Issuer แจ้งหมุนเวียนกุญแจ ETDA **MUST** ตรวจแล้วเผยแพร่ TL ใหม่ภายใน 24 ชั่วโมง +- Wallet **MUST NOT** ใช้กุญแจจาก DID Document แทนกุญแจจาก TL แม้ DID Document จะถูกแก้ให้ดูถูกต้อง + +**[ข้อเท็จจริง]** การเพิกถอนแบบกลุ่มเมื่อผู้ใช้แจ้งอุปกรณ์สูญหายมีขั้นตอนคือ ผู้ใช้แจ้งผ่านพอร์ทัล ETDA พร้อมพิสูจน์ตัวตน ETDA บันทึกเหตุการณ์แล้วสั่งเพิกถอน VC ทั้งหมดที่ผูกกับ master seed ของผู้ใช้ จากนั้นผู้ใช้กู้คืนด้วย mnemonic บนอุปกรณ์ใหม่แล้วขอออก VC ใหม่ [A-KD08 §5] + +**[ข้อเท็จจริง]** เอกสารสถาปัตยกรรมบันทึกข้อกังวลด้าน privacy ไว้เองว่า การเพิกถอนแบบกลุ่มต้องให้ผู้ให้บริการ Wallet เก็บความเชื่อมโยงระหว่าง master seed กับประวัติการออก VC และแนวทางในอนาคตคือใช้ self-attested revocation token แทน [A-KD08 §5] + +**[ข้อเท็จจริง]** การปฏิบัติตาม CIR 2024/2979 ที่บันทึกไว้คือ หมุนเวียนกุญแจได้ กุญแจเดิมใช้ซ้ำไม่ได้เพราะ mark เป็น `rotated` ใน Status List มี audit trail จากประวัติ KSN และ rotation log และผู้ใช้ควบคุมการหมุนเวียนได้เอง [A-KD08 §8] + +--- + +## 12.7 Resolver และ Network Controls + +**[ข้อเท็จจริง]** ระบบแยกหน้าที่ออกเป็นสองบริการที่ไม่ปะปนกัน (1) Trusted List ของ สพธอ. เป็นแหล่งข้อมูลอ้างอิงหลักของการอนุญาต โดย JWT ตัวอย่างมีรายการองค์กรที่ `LoTE.TrustedEntitiesList[]` และรายการบริการที่ `TrustedEntityServices[]` แต่ยังไม่แสดงฟิลด์สถานะและขอบเขตสิทธิ์ครบถ้วน Thai profile จึงต้องกำหนดฟิลด์เหล่านั้นก่อนใช้ตัดสินใจ; หากไม่พบองค์กรหรือบริการ หรือไม่อาจตรวจสถานะได้ ให้ถือว่าไม่ผ่าน (2) Universal Resolver ที่ `https://resolver.etda.or.th` ทำหน้าที่ resolve DID Document เฉพาะ custom DID (เช่น `did:ndid`) ที่เอนทิตี resolve เองไม่ได้เท่านั้น และส่งกลับเฉพาะ `didDocument` โดยไม่เกี่ยวข้องกับการค้นหรือการตัดสินสถานะใน Trusted List [A-ARCH §6] [รูปแบบ JWT](../research/42-trusted-list-lote-jwt-format.md) + +**[ข้อวิเคราะห์]** การตัดสิน trust **MUST** อาศัยผลการค้นใน Trusted List ผู้เรียกใช้ **MUST** ถือว่าการไม่พบบริการใน `LoTE.TrustedEntitiesList[].TrustedEntityServices[]` หรือไม่สามารถยืนยันสถานะ `active` ทั้งระดับองค์กรและบริการตาม Thai profile ได้เป็นผลลบ และ **MUST NOT** ใช้ `didDocument` ที่ resolve ได้เป็นหลักฐานการอนุญาต เพราะจะข้าม gate `TL_ENTRY_LOOKUP` และ `ENTRY_STATUS_CHECK` ที่ threat model กำหนดไว้ [A-TM §3.0] [รูปแบบ JWT](../research/42-trusted-list-lote-jwt-format.md) + +**[ข้อเท็จจริง]** มาตรการระดับเครือข่ายและการเผยแพร่ที่กำหนดไว้ + +| มาตรการ | ป้องกัน | ที่มา | +|---------|---------|-------| +| บังคับ DNSSEC สำหรับโดเมน `.go.th` ทั้งหมด | did:web hijack จาก DNS poisoning | [A-TM §I1] | +| CAA record และ DNS pinning ตาม Thai profile; JWT ตัวอย่างยังไม่แสดงฟิลด์ดังกล่าว | domain takeover | [A-TM §I1], [รูปแบบ JWT](../research/42-trusted-list-lote-jwt-format.md) | +| ตรวจ homograph แบบ IDN mixed-script และห้าม UI แสดง raw DID | lookalike DID | [A-TM §I2] | +| ลายมือชื่อ JWT บน TL โดย CDN ไม่ถือกุญแจลงลายมือชื่อ | CDN ถูก compromise | [A-TM §T2] | +| Multi-CDN แบบ primary กับ secondary และ tertiary พร้อม DNS fallback | eclipse attack แบบ DoS | [A-TM §T2, §T3] | +| Graceful degradation โดยแสดงคำเตือนและจำกัดฟีเจอร์ที่ต้องใช้ TL สด | eclipse attack | [A-TM §T3] | +| `client_id_scheme: x509_san_dns` สำหรับ verifier ที่อยู่ใน TL | client_id spoofing | [A-TM §V1] | +| `response_uri` ต้องตรงกับ SAN ของ `client_id` | MITM proxy | [A-TM §V4] | +| `nonce` กับ `aud` ใน VP token และอายุ token ไม่เกิน 5 นาที | cross-verifier replay | [A-TM §V5] | +| Cross-signed bridge TL และตรวจ scope ตามเขตอำนาจ | cross-border TL substitution | [A-TM §T7] | + +**[ข้อวิเคราะห์]** เอกสารนี้ **MUST NOT** ถูกใช้เป็นหลักฐานว่ามาตรการข้างต้นทำงานจริง การอนุมัติขึ้นใช้งาน **MUST** มีหลักฐานจากการทดสอบใน staging ได้แก่ signed fixture ผลการทดสอบเชิงลบ และรายงาน fault injection + +--- + +## 12.8 การดึง Trusted List แบบ Lazy ผ่าน CDN + +**[ข้อเท็จจริง]** Trusted List เป็นไฟล์ static ที่ลงลายมือชื่อโดย สพธอ. และเผยแพร่ผ่าน CDN ผู้ใช้บริการไม่ต้องเชื่อถือ CDN เพราะต้องตรวจลายมือชื่อของ Trusted List ก่อนใช้งาน [research/41] + +**[ข้อกำหนด]** Wallet, Issuer และ Verifier **MUST** ดึง Trusted List ด้วย HTTP `GET` เมื่อเกิด connection หรือก่อนตัดสินใจด้านความน่าเชื่อถือเท่านั้น ระบบ **MUST NOT** ใช้ message broker หรือ background polling เพื่อแจ้งหรือดึงการเปลี่ยนแปลงของ Trusted List [research/41] + +### 12.8.1 ขั้นตอนการดึงและตรวจสอบ + +1. เลือก Trusted List ที่เกี่ยวข้องตามบทบาท ได้แก่ `https://trustme.etda.or.th/.well-known/iss.txt` สำหรับ Issuer, `https://trustme.etda.or.th/.well-known/ver.txt` สำหรับ Verifier และ `https://trustme.etda.or.th/.well-known/wp.txt` สำหรับ Wallet Provider แล้วเรียก `GET` ไปยัง CDN พร้อม `If-None-Match` หากมีค่า `ETag` ที่เก็บไว้ +2. หาก CDN ตอบ `304 Not Modified` ให้ใช้ Trusted List ที่เกี่ยวข้องฉบับเต็มใน cache เดิม หากตอบ `200 OK` ให้รับ Trusted List ที่เกี่ยวข้องฉบับเต็มใหม่ในรูปแบบ JWT โดยไม่ใช้ API key, delta หรือไฟล์ CBOR +3. ตรวจลายมือชื่อ JWT ด้วย public key ของ สพธอ. แล้วตรวจ `LoTE.ListAndSchemeInformation.LoTEType`, `LoTESequenceNumber` ว่าไม่ย้อนกลับ และ `NextUpdate` ว่ายังไม่หมดอายุ [research/42](../research/42-trusted-list-lote-jwt-format.md) +4. หากตรวจผ่าน ให้บันทึกไฟล์และ `ETag` ใหม่ หากตรวจไม่ผ่าน ให้ปฏิเสธไฟล์และไม่เปลี่ยน cache เดิม + +### 12.8.2 Lazy pull มีผลต่อผู้ใช้บริการอย่างไร + +| ผู้ใช้บริการ | จุดที่เรียก `GET` | ผลที่ผู้ใช้เห็น | +|---|---|---| +| Wallet | เมื่อรับ VC หรือได้รับ VP Request | ลดการแสดงคำขอจาก Verifier ที่สถานะไม่ active และไม่ต้องเชื่อมต่อเพื่อ polling ระหว่างไม่ได้ใช้งาน | +| Issuer | เมื่อ Wallet ขอรับ VC | ตรวจสอบ Wallet Provider ที่เกี่ยวข้องก่อนออก VC โดยไม่ต้องดูแลช่องทางรับการแจ้งเตือนแยก | +| Verifier | เมื่อรับ VP | ตรวจสอบ Issuer และสถานะที่ใช้ตัดสินใจจากข้อมูลล่าสุดที่ CDN ส่งให้ โดยไม่ต้องติดตั้งช่องทางแจ้งเตือนแยก | + +แนวทางนี้เรียกว่า **lazy pulling** เพราะระบบดึงข้อมูลเมื่อมีการใช้งานจริง ไม่ดึงเป็นระยะในช่วงที่ไม่มี connection จึงลดการใช้แบตเตอรี่ CPU bandwidth และ request ของ CDN ขณะเดียวกันการตรวจ Trustlist เกิดตรงจุดที่มีการตัดสินใจจริง [research/41] + +```mermaid +graph LR + ADMIN[ETDA Admin
Emergency Revoke] --> PUB[TL Publisher] + PUB -->|"1. update signed TL file"| CDN[ETDA CDN] + CDN -->|"2. GET + If-None-Match
on connection"| V[Verifiers] + CDN -->|"2. GET + If-None-Match
on connection"| I[Issuers] + CDN -->|"2. GET + If-None-Match
on connection"| W[Wallets] + + style CDN fill:#e3f2fd + style ADMIN fill:#ffcdd2 +``` + +**รูปที่ 12-1 ภาพรวมการดึง Trusted List แบบ lazy ผ่าน CDN** + +--- + +## 12.9 ช่องว่างที่เกี่ยวข้อง + +**[ข้อวิเคราะห์]** ช่องว่าง G-01 เรื่องระดับการรับรอง HSM ได้ข้อยุติแล้ว โดยกำหนดเกณฑ์ขั้นต่ำเป็น FIPS 140-2 Level 3 และยอมรับ FIPS 140-3 Level 3 เป็นมาตรฐานสืบทอด และช่องว่าง G-07 เรื่องการเก็บกุญแจของผู้ตรวจสอบเอกสารได้ข้อยุติแล้วเช่นกัน โดยกำหนดทางเลือก 3 แบบ คือ soft key, TEE หรือ WSCD, และ HSM ซึ่ง HSM ให้ความเชื่อมั่นสูงสุดแต่มีต้นทุนสูง ทั้งนี้ ไม่ว่าเลือกวิธีเก็บกุญแจแบบใด ผู้ตรวจสอบเอกสาร **ต้อง** ประกาศ public key ให้ตรวจสอบได้ผ่าน Trusted List หรือ DID Document ที่ `did:web` รายละเอียด G-01 ปรากฏใน §12.2 และรายละเอียด G-07 ปรากฏใน §12.2.1 สรุปช่องว่างทั้งหมดที่ต้อง escalate รวมทั้งประเด็นที่เกี่ยวกับการหมุนเวียนกุญแจและการเพิกถอน รวมอยู่ในบทที่ 14 §14.4 + +--- + +{/* METADATA (agent-only — not rendered to readers) +## 12.10 References + +### 12.10.1 แหล่งข้อมูลหลัก — เอกสารสถาปัตยกรรมในโครงการ + +วันที่เข้าถึงทุกไฟล์: **2026-08-06** โดยอ้างสถานะ repository ณ เวอร์ชันเอกสาร 1.0.0 + +| รหัส | เอกสาร | เวอร์ชันที่ระบุในไฟล์ | +|------|--------|---------------------| +| A-ARCH | [../architecture/01-architecture.md](../architecture/01-architecture.md) — สถาปัตยกรรมความน่าเชื่อถือแบบ Trusted List | v2.3 | +| A-TM | [../architecture/phase2/trustlist/10-threat-model.md](../architecture/phase2/trustlist/10-threat-model.md) — Trust List Verification: Threat Model & Mitigations | Draft v1.0 | +| A-KD08 | [../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md](../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md) — Key Rotation & Lifecycle | Draft v1.0 | +| A-KD06 | [../architecture/phase2/key-derivation/06-wscd-key-attestation.md](../architecture/phase2/key-derivation/06-wscd-key-attestation.md) — WSCD Key Attestation | Draft v1.0 | +| A-ACP | [../architecture/phase2/trustlist/05-access-certificate-profile.md](../architecture/phase2/trustlist/05-access-certificate-profile.md) — Access Certificate Profile (ETSI TS 119 411-8) | Draft v1.0 | +| A-VRP | [../policy/01-verifier-registration-policy.md](../policy/01-verifier-registration-policy.md) — นโยบายการลงทะเบียนผู้ตรวจสอบ | Draft v1.0 | +| A-HSM | [../policy/07-hsm-and-schema-governance-policy.md](../policy/07-hsm-and-schema-governance-policy.md) — นโยบาย HSM Service Model และ Schema Governance | Draft v1.0 | + +### 12.10.2 แหล่งข้อมูลงานวิจัยภายในโครงการ + +| รหัส | เอกสาร | เวอร์ชันที่ระบุในไฟล์ | +|------|--------|---------------------| +| research/41 | [../research/41-trustlist-serving-filecdn-etag-binary.md](../research/41-trustlist-serving-filecdn-etag-binary.md) — Trustlist ผ่าน File/Edge CDN และ lazy revalidation | v1.10 | + +### 12.10.3 มาตรฐานภายนอกที่อ้างผ่านเอกสารข้างต้น + +เอกสารนี้ไม่อ้างมาตรฐานภายนอกโดยตรง รายการต่อไปนี้คือมาตรฐานที่ไฟล์สถาปัตยกรรมและงานวิจัยที่อ้างในหัวข้อ §12.2–§12.8 ระบุไว้เป็นแหล่งข้อมูลของตน + +| มาตรฐาน | ส่วนที่อ้าง | ไฟล์ที่อ้าง | +|---------|-----------|-----------| +| IETF Token Status List | Architecture baseline: `draft-ietf-oauth-status-list-18`; current technical reference: `draft-ietf-oauth-status-list-21` [13] (การเพิกถอน; `rotated` เป็น application-specific semantics ที่ Thai profile ต้องกำหนด) | A-KD08, A-TM, research/41 | +| CIR 2024/2979 | ข้อกำหนดการหมุนเวียนกุญแจ | A-KD08 | +| EU eIDAS ARF | §6 WSCD และการจัดการกุญแจ | A-KD08, A-KD06 | +| RFC 4033 | DNSSEC | A-TM §I1 | +| RFC 9162 | Certificate Transparency v2 | A-TM §I4 | +| RFC 5891 และ Unicode TR 39 | IDN และการตรวจ homograph | A-TM §I2 | + +### 12.10.4 แหล่งอ้างอิง NIST สำหรับข้อกำหนด HSM + +ระดับการรับรอง HSM ในหัวข้อนี้อ้างอิงมาตรฐานของ National Institute of Standards and Technology (NIST) โดยตรง เนื่องจากเอกสารสถาปัตยกรรม [A-ARCH §8 I1] และ [A-TM §T1] ระบุระดับ FIPS ไว้แล้ว รายการนี้จึงระบุแหล่งที่มาที่ตรวจสอบได้ของมาตรฐานดังกล่าว + +- [N1] NIST, "FIPS PUB 140-2 — Security Requirements for Cryptographic Modules", พฤษภาคม ค.ศ. 2001 (ปรับปรุงตาม Change Notice ธันวาคม ค.ศ. 2002) — https://csrc.nist.gov/pubs/fips/140-2/upd2/final +- [N2] NIST, "FIPS PUB 140-3 — Security Requirements for Cryptographic Modules", มีนาคม ค.ศ. 2019 — https://csrc.nist.gov/pubs/fips/140-3/final +- [N3] NIST, "Cryptographic Module Validation Program (CMVP) — FIPS 140-2 Transition" — ใบรับรองตาม FIPS 140-2 เปลี่ยนสถานะเป็น Historical ตั้งแต่วันที่ 21 กันยายน ค.ศ. 2026 — https://csrc.nist.gov/projects/cryptographic-module-validation-program + +--- + +## 12.11 บันทึกการเปลี่ยนแปลง + +| เวอร์ชัน | วันที่ | รายละเอียด | +|---------|-------|-----------| +| 1.2.5 | 2026-09-23 | เปลี่ยนการกระจาย Trusted List จาก message broker เป็นการดึงด้วย HTTP `GET` แบบ lazy ผ่าน CDN พร้อม `ETag` และเพิ่มผลกระทบต่อ Wallet, Issuer และ Verifier โดยอ้างอิง research/41 | +| 1.2.4 | 2026-08-06 | ลบกุญแจ Level 2 (Pairwise ต่อ Verifier) ออกจากตาราง lifecycle (§12.4) และ rotation trigger (§12.3) และตัดการอ้างถึง Phase 2/Phase 3 ใน §12.5 และ §12.6 ตามมติให้ยกเลิก pairwise DID และการแบ่งระยะ (MASA-157) | +| 1.2.3 | 2026-08-06 | ตัดข้อกำหนดและมาตรการที่อ้างอิงช่องทางสื่อสารแบบ transport ออกจากบทนี้ทั้งหมด เพราะการควบคุมระดับ transport อยู่นอกขอบเขตบท key management บทนี้จึงคงเฉพาะข้อกำหนดการเก็บกุญแจ การประกาศ public key และการหมุนกุญแจ (MASA-153) | +| 1.2.2 | — | ข้ามหมายเลข ไม่มีการออกเวอร์ชันนี้ | +| 1.2.1 | 2026-08-06 | ตัดกลไกประกาศ public key ของผู้ตรวจสอบเอกสารแบบฝังใน Verifier Attestation ออกจากขอบเขต คงไว้สองทางคือ Trusted List entry หรือ DID Document (`did:web`) (MASA-153) | +| 1.2.0 | 2026-08-06 | เพิ่มผู้ตรวจสอบเอกสาร (Verifier) เป็นผู้ถือกุญแจกลุ่มที่ 4 และมติ G-07 ทางเลือกการเก็บกุญแจ 3 แบบ (soft key / TEE-WSCD / HSM) พร้อมกำหนดการประกาศ public key ผ่าน Trusted List หรือ DID Document (`did:web`) และตัดข้อกำหนด Access Certificate ออกจากขอบเขต (MASA-153) | + +--- + +**สถานะเอกสาร:** Living document — ทบทวนพร้อม [A-TM] ทุกไตรมาส หรือเมื่อ Trusted List ขึ้น minor version +**ผู้ดูแล:** ETDA Security Team + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 11 — Cryptographic Suites](11-cryptographic-suites.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 13 — VC Status และ Revocation ➡️](13-vc-status-and-revocation.md) diff --git a/docs/thai-vc-arf/12.1-trust-list-publication-profile.md b/docs/thai-vc-arf/12.1-trust-list-publication-profile.md new file mode 100644 index 0000000..aa91bcd --- /dev/null +++ b/docs/thai-vc-arf/12.1-trust-list-publication-profile.md @@ -0,0 +1,150 @@ +--- +description: "12.1 ข้อกำหนดการเผยแพร่ Trusted List — Thai VC ARF 2.0 DRAFT 0" +--- + +# 12.1 ข้อกำหนดการเผยแพร่ Trusted List + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** ส่วนขยายของ Thai VC ARF เวอร์ชัน 2.1 — กำลังย้ายไปใช้ payload แบบ LoTE ใน JWT +> **วัตถุประสงค์:** ระบุประเภท จำนวนไฟล์ รูปแบบ โครงสร้างข้อมูล และตัวอย่างของ Trusted List ที่ใช้ในระบบกระเป๋าเอกสารดิจิทัล (Document Wallet) +> **เอกสารที่เกี่ยวข้อง:** [บทที่ 9 — Trust Model 3](09-trust-model-3.md) · [บทที่ 12 — Key Management และ Trusted List Deployment](12-key-management-and-trustlist-deployment.md) · [บทที่ 13 — VC Status และการเพิกถอน](13-vc-status-and-revocation.md) +*/} + +## 12.1.1 ขอบข่าย + +เอกสารฉบับนี้สรุปรูปแบบเป้าหมายของ Trust List สำหรับประเทศไทยตาม [ผลการวิเคราะห์ JWT ที่อ้างอิง LoTE](../research/42-trusted-list-lote-jwt-format.md) และ [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) โดยแยกข้อมูลที่พบใน JWT ตัวอย่างออกจากเขตข้อมูลที่ Thai profile ยังต้องกำหนดก่อนใช้งานจริง [1][2] + +ข้อกำหนดการใช้ Trusted List นี้ใช้ร่วมกันใน Trust Model 1, Trust Model 2 และ Trust Model 3 โดยความแตกต่างของแบบจำลองอยู่ที่วิธีผูกและตรวจสอบกุญแจหรือใบรับรองของเอนทิตี [บทที่ 6.5](06.5-trust-building-mechanism.md) + +ดังนั้น ไม่ว่าใช้แบบจำลองใด ระบบต้องดึง Trusted List ที่เกี่ยวข้อง ตรวจลายมือชื่อ JWT ตรวจสถานะ บทบาท และช่วงเวลาที่มีผลใช้ได้ก่อนตัดสินใจเชื่อถือเอนทิตี ห้ามใช้การตรวจสอบสายใบรับรองหรือ DID แทนการตรวจ Trusted List + +payload ของ JWT อ้างอิงชื่อและลำดับชั้นข้อมูลของ List of Trusted Entities (LoTE) ตาม ETSI TS 119 602 ส่วนการห่อด้วย JWT จุดเผยแพร่ และกฎรายบทบาทเป็น Thai profile จึงยังไม่อาจกล่าวอ้างความสอดคล้องกับ LoTE ของ EUDI โดยสมบูรณ์ การตรวจสถานะ VC รายฉบับใช้ Token Status List [1][2] + +## 12.1.2 ประเภทและจำนวนไฟล์ + +สพธอ. ต้องเผยแพร่ Trust List จำนวน 3 ไฟล์ โดยแยกตามบทบาทของเอนทิตี เพื่อให้ผู้ใช้งานดึงเฉพาะข้อมูลที่จำเป็นต่อการตัดสินใจด้านความน่าเชื่อถือ [1] + +| ลำดับ | Trusted List ที่เกี่ยวข้อง | Endpoint | เอนทิตีที่บันทึก | ผู้เรียกใช้หลัก | เหตุการณ์ที่ใช้ตรวจสอบ | +|:---:|---|---|---|---|---| +| 1 | Issuer Trusted List | `https://trustme.etda.or.th/.well-known/iss.txt` | ผู้ออกเอกสาร (issuer) | Wallet และ Verifier | ก่อนรับหรือก่อนตรวจสอบ VC | +| 2 | Verifier Trusted List | `https://trustme.etda.or.th/.well-known/ver.txt` | ผู้ตรวจสอบเอกสาร (verifier) | Wallet | ก่อนนำเสนอ VP | +| 3 | Wallet Provider Trusted List | `https://trustme.etda.or.th/.well-known/wp.txt` | ผู้ให้บริการกระเป๋าเอกสารดิจิทัล (wallet provider) | Issuer และ Verifier | ก่อนออก VC หรือก่อนตรวจสอบ Wallet Unit | + +การแยกไฟล์ดังกล่าวทำให้การเปลี่ยนแปลงข้อมูลของบทบาทหนึ่งไม่กระทบ cache ของอีกบทบาทหนึ่ง และทำให้แต่ละระบบดึงข้อมูลเฉพาะที่เกี่ยวข้องกับหน้าที่ของตน [1] + +## 12.1.3 รูปแบบไฟล์และการเผยแพร่ + +Trusted List ทั้ง 3 รายการมีเนื้อหาเป็น compact JWS ในรูป `header.payload.signature` ซึ่ง สพธอ. ลงลายมือชื่อและเผยแพร่ผ่าน CDN จุดเผยแพร่ที่ระบุใน §12.1.2 ใช้นามสกุล `.txt` โดยเนื้อหาภายในยังคงเป็น compact JWS ผู้ใช้ข้อมูลต้องตรวจลายมือชื่อด้วย trust anchor ของ สพธอ. ก่อนใช้ payload [1][2] + +การดึงข้อมูลต้องใช้ HTTP conditional request โดยเก็บค่า `ETag` จากการตอบกลับครั้งก่อนและส่ง `If-None-Match` เมื่อมีการตรวจสอบครั้งถัดไป หากได้รับ `304 Not Modified` ให้ใช้ Trusted List ที่เกี่ยวข้องซึ่งจัดเก็บไว้เดิม หากได้รับ `200 OK` พร้อมไฟล์ใหม่ ให้ดึง Trusted List ที่เกี่ยวข้องฉบับเต็ม ตรวจลายมือชื่อ และตรวจ `LoTE.ListAndSchemeInformation.LoTESequenceNumber` กับ `LoTE.ListAndSchemeInformation.NextUpdate` ก่อนแทนที่ cache ทั้งนี้ การเผยแพร่ระยะปัจจุบันไม่ใช้ API key, delta หรือไฟล์ CBOR [1][2][3] + +`ETag` ใช้ตรวจการเปลี่ยนแปลงของไฟล์ ส่วนความถูกต้องและความครบถ้วนของเนื้อหาต้องตรวจจากลายมือชื่อบน JWT [1] + +```text +GET https://trustme.etda.or.th/.well-known/iss.txt +If-None-Match: "urn:etda:trusted-list:issuer:2026-v42" + +304 Not Modified +``` + +## 12.1.4 โครงสร้างข้อมูล + +payload ที่ถอดรหัสจาก JWT ตัวอย่างใช้ `LoTE` เป็นวัตถุระดับบน มีข้อมูลรายการใน `ListAndSchemeInformation` และมีรายการองค์กรใน `TrustedEntitiesList[]` โดยแต่ละองค์กรมี `TrustedEntityInformation` และ `TrustedEntityServices[]` ตารางต่อไปนี้แสดงเฉพาะชื่อเขตข้อมูลที่พบในตัวอย่าง issuer, verifier และ wallet provider ส่วนฟิลด์บังคับทั้งหมดต้องกำหนดใน Thai profile [1] + +| เส้นทางใน payload | ข้อมูลที่พบในตัวอย่าง | +|---|---| +| `LoTE.ListAndSchemeInformation.LoTEVersionIdentifier` | เวอร์ชันของโครงสร้างรายการ | +| `LoTE.ListAndSchemeInformation.LoTESequenceNumber` | ลำดับของรายการ ใช้ตรวจ rollback | +| `LoTE.ListAndSchemeInformation.LoTEType` | ชนิดของรายการ; Thai profile ยังต้องตรึง URI ที่อนุญาต | +| `LoTE.ListAndSchemeInformation.SchemeTerritory` | เขตอำนาจของ scheme เช่น `TH` | +| `LoTE.ListAndSchemeInformation.SchemeName[]` | ชื่อ scheme พร้อมภาษา | +| `LoTE.ListAndSchemeInformation.ListIssueDateTime` | เวลาเผยแพร่รายการ | +| `LoTE.ListAndSchemeInformation.NextUpdate` | เวลาที่ต้องตรวจความสดใหม่ของรายการ | +| `LoTE.TrustedEntitiesList[]` | รายการองค์กร; ตัวอย่าง wallet provider ยังเป็นอาร์เรย์ว่าง | +| `TrustedEntitiesList[].TrustedEntityInformation.TEName[]` | ชื่อองค์กรพร้อมภาษา | +| `TrustedEntitiesList[].TrustedEntityServices[].ServiceInformation.ServiceTypeIdentifier` | ชนิดบริการ; Thai profile ยังต้องตรึง URI ที่อนุญาต | +| `TrustedEntitiesList[].TrustedEntityServices[].ServiceInformation.ServiceName[]` | ชื่อบริการพร้อมภาษา | +| `TrustedEntitiesList[].TrustedEntityServices[].ServiceInformation.ServiceDigitalIdentity.PublicKeyValues[]` | กุญแจสาธารณะของบริการ หากประกาศโดยตรง | +| `TrustedEntitiesList[].TrustedEntityServices[].ServiceInformation.ServiceDigitalIdentity.X509Certificates[]` | ใบรับรอง X.509 ของบริการ; พบในตัวอย่าง issuer | +| `TrustedEntitiesList[].TrustedEntityServices[].ServiceInformation.ServiceDigitalIdentity.OtherIds[]` | ตัวระบุอื่นของบริการ เช่น DID | + +OID4VP TC-2 กำหนดให้ Wallet รัฐบาลส่ง WIA/WIA-PoP ส่วน Wallet เอกชนเลือกส่งหรือไม่ส่งได้ ดังนั้น Thai profile ต้องกำหนดข้อมูลที่ Verifier ใช้จำแนก Wallet แต่ละรายการว่าเป็น Wallet รัฐบาลหรือเอกชน โดยข้อมูลดังกล่าวต้องเผยแพร่หรืออ้างอิงจากแหล่งที่เชื่อถือได้ ตรวจสอบความถูกต้องและความสดใหม่ได้ และต้องไม่อาศัยการมีหรือไม่มี WIA เป็นตัวตัดสินประเภท Wallet เอง วิธีบันทึกข้อมูลและชื่อ field ยังไม่ปรากฏในตัวอย่าง Trusted List จึงต้องกำหนดใน profile ก่อนใช้งานจริง หาก Verifier ไม่สามารถตรวจประเภทหรือความสดใหม่ของข้อมูลได้ ให้ปฏิเสธ VP; ห้ามถือว่าข้อมูลที่ขาดหายหมายถึง Wallet เอกชน + +หลักการของไทยกำหนดให้ตรวจสถานะระดับองค์กรและระดับบริการเป็น `active` และตรวจขอบเขตสิทธิ์ของ Issuer, Verifier และ Wallet Provider ตามบทบาท แต่ JWT ตัวอย่างทั้งสามยังไม่แสดงฟิลด์สถานะครบทั้งสองระดับ หรือฟิลด์ประเภท VC/claims ของ Issuer และ claims กับวัตถุประสงค์ของ Verifier จึงยังไม่อาจยืนยันชื่อและเส้นทางฟิลด์เหล่านั้นจากตัวอย่างได้ Thai profile ต้องกำหนดฟิลด์ดังกล่าวพร้อมกฎการตรวจสอบก่อนใช้งานจริง [1][2] + +ผู้ใช้ข้อมูลต้องปฏิเสธการตัดสินใจด้านความน่าเชื่อถือ หากไม่สามารถยืนยันสถานะระดับองค์กรและระดับบริการ ขอบเขตสิทธิ์ที่จำเป็น ลายมือชื่อ ชนิดรายการ ลำดับรายการ และเวลาที่มีผลใช้ได้จาก profile ที่ประกาศใช้ [1][2] + +## 12.1.5 ตัวอย่าง payload + +โครงสร้าง Issuer Trusted List ต่อไปนี้เป็นข้อเสนอใน [research/42](../research/42-trusted-list-lote-jwt-format.md) เพื่อแสดงข้อมูลที่ Thai profile ยังต้องกำหนด ส่วน JWT ตัวอย่างฉบับปัจจุบันมี compact JWS ครบสามส่วนและมีบริการออกเอกสาร 3 รายการ แต่ยังไม่มีฟิลด์สถานะและประเภท VC ที่ออกได้ และยังไม่ได้ตรวจลายมือชื่อ ค่า `LoTEType` ใน JWT ตัวอย่างเป็น URI `EUPubEAAProvidersList` ใน namespace ของ ETSI; ค่า `ThaiIssuersList` ด้านล่างเป็น URI ที่เสนอและยังรอการประกาศใช้ [1] + +```json +{ + "LoTE": { + "ListAndSchemeInformation": { + "LoTEType": "https://uri.etda.or.th/19602/LoTEType/ThaiIssuersList", + "LoTESequenceNumber": 1, + "ListIssueDateTime": "2026-09-24T00:00:00Z", + "NextUpdate": "2026-09-25T00:00:00Z" + }, + "TrustedEntitiesList": [ + { + "TrustedEntityInformation": { + "TEName": [{"lang": "th", "value": "หน่วยงานตัวอย่าง"}] + }, + "TrustedEntityServices": [ + { + "ServiceInformation": { + "ServiceTypeIdentifier": "https://uri.etda.or.th/19602/SvcType/CredentialIssuer", + "status": "active", + "ServiceDigitalIdentity": { + "OtherIds": ["did:web:issuer.example.th"] + }, + "credential_types": ["https://credentials.dopa.go.th/ThaiNationalIDCredential"] + } + } + ] + } + ] + } +} +``` + +โครงสร้างการตรวจสอบใช้ลำดับดังรูปแบบต่อไปนี้ โดยการตัดสินใจต้องเกิดภายหลังการตรวจลายมือชื่อและเวลาที่มีผลใช้ได้ของข้อมูลแล้ว [1] + +```mermaid +sequenceDiagram + participant C as Consumer + participant CDN as CDN + participant DID as ETDA DID Document + + C->>CDN: GET Trusted List ที่เกี่ยวข้อง พร้อม If-None-Match + alt ไม่เปลี่ยนแปลง + CDN-->>C: 304 Not Modified + C->>C: ใช้ cache เดิม + else มีไฟล์ใหม่ + CDN-->>C: 200 OK พร้อม JWT และ ETag ใหม่ + C->>DID: ดึง public key ของ สพธอ. + DID-->>C: public key + C->>C: ตรวจลายมือชื่อ LoTESequenceNumber และ NextUpdate + C->>C: ปรับปรุง cache เมื่อผ่านการตรวจสอบ + end +``` + +## 12.1.6 ข้อกำหนดสำหรับผู้ใช้ข้อมูล + +Wallet, Issuer และ Verifier ต้องใช้เฉพาะ JWT ที่ผ่านการตรวจลายมือชื่อแล้ว ต้องตรวจ `LoTEType`, `LoTESequenceNumber`, `ListIssueDateTime` และ `NextUpdate` ใน `LoTE.ListAndSchemeInformation` ก่อนใช้ข้อมูล และต้องไม่ถือว่า JWT ตัวอย่างที่ยังขาดสถานะหรือขอบเขตสิทธิ์เป็นรายการที่อนุญาตให้ใช้จริง [1][2] + +การเปลี่ยนสถานะของเอนทิตีใน Trust List เป็น `suspended` หรือ `withdrawn` ต้องมีผลต่อการตัดสินใจของระบบในครั้งถัดไปที่มีการเชื่อมต่อและตรวจสอบ Trust List ใหม่ ทั้งนี้ การตรวจสอบ Trust List ดำเนินการเมื่อเกิดการเชื่อมต่อที่ต้องมีการตัดสินใจด้านความน่าเชื่อถือ ไม่ใช้การ polling เบื้องหลัง [1] + +## 12.1.7 เอกสารอ้างอิง + +[1] [การย้ายรูปแบบ Trusted List ไปสู่ JWT ที่อ้างอิง LoTE](../research/42-trusted-list-lote-jwt-format.md), เวอร์ชัน 1.2, 25 กันยายน 2569 + +[2] [ชุด VC ของประเทศไทย — สรุปหนึ่งหน้า](00-minimal-interoperability-reference.md), หัวข้อ Trusted List + +[3] [แนวทางกระจาย Trusted List ผ่าน File/Edge CDN](../research/41-trustlist-serving-filecdn-etag-binary.md), เวอร์ชัน 1.10, 4 กันยายน 2569 (ใช้เฉพาะวิธีเผยแพร่และ ETag) + +--- + +**การนำทาง:** [⬅️ บทที่ 12 — Key Management และ Trusted List Deployment](12-key-management-and-trustlist-deployment.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 13 — VC Status และการเพิกถอน ➡️](13-vc-status-and-revocation.md) diff --git a/docs/thai-vc-arf/13-vc-status-and-revocation.md b/docs/thai-vc-arf/13-vc-status-and-revocation.md new file mode 100644 index 0000000..97cb2f2 --- /dev/null +++ b/docs/thai-vc-arf/13-vc-status-and-revocation.md @@ -0,0 +1,529 @@ +--- +description: "13. VC Status และ Revocation — Thai VC ARF 2.0 DRAFT 0" +--- + +# 13. VC Status และ Revocation + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📐 Specification — Draft สำหรับการทบทวนเชิงกำกับดูแล (S8, MASA-129 → MASA-150) +> **เวอร์ชันเอกสาร:** 1.3.0 (2026-08-07) — เพิ่ม §13.7.7 ผลกระทบเมื่อผู้ออกเอกสารหมุนเวียนกุญแจลงลายมือชื่อ (4 สถานะของกุญแจ 4 trigger T0–T3, ผลต่อ VC เดิม, กลไก wallet-side notification และ reissue campaign) และเพิ่มแถวในตารางสรุป §13.7.6 (MASA-166) แก้ถ้อยคำแปลตรงตัว "คำร่ม" → "คำรวม" และ "แหล่งความจริงเดียว" → "แหล่งข้อมูลอ้างอิงหลัก" (MASA-167) · เดิม: เพิ่ม §13.7 แผนภาพลำดับของรูปแบบการเพิกถอนทั้ง 5 แบบ (เพิกถอน VC, เพิกถอน Issuer/Verifier ผ่าน Trusted List, wallet provider เพิกถอน WIA/KA พร้อม cascade) และตารางสรุป §13.7.6 (MASA-158) +> **แหล่งข้อมูล:** เอกสารสถาปัตยกรรมภายใต้ `th/architecture/` เท่านั้น — ดู §13.9 References +> **ขอบเขต:** เอกสารกำกับดูแลและข้อกำหนดสำหรับการตรวจประเมิน ห้ามตีความเป็นผลทดสอบ production +> **เอกสารที่เกี่ยวข้อง:** [11-cryptographic-suites.md](11-cryptographic-suites.md), [12-key-management-and-trustlist-deployment.md](12-key-management-and-trustlist-deployment.md), [14-security-threat-model-and-conformance.md](14-security-threat-model-and-conformance.md), [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + +บทที่ 13 กำหนดกลไกการแสดงสถานะและการเพิกถอนเอกสารรับรองดิจิทัล (VC) ด้วย IETF Token Status List การเพิกถอนผู้ตรวจสอบเอกสาร (verifier) ผ่านการเปลี่ยนสถานะใน Trusted List และลำดับการตรวจสถานะระหว่างการนำเสนอ VP เรื่อง cryptographic suites อยู่ในบทที่ 11 และ key management อยู่ในบทที่ 12 + +--- + +## 13.1 ขอบเขตและการจำแนกข้อความ + +บทนี้รวมข้อกำหนดด้านการแสดงสถานะและการเพิกถอนที่เกี่ยวข้องกับการนำเสนอ VC สี่เรื่อง คือ (1) การเพิกถอน VC (VC Revocation) ด้วย IETF Token Status List (2) การเพิกถอนผู้ตรวจสอบเอกสาร (verifier) ผ่านการเปลี่ยนสถานะใน Trusted List (3) ลำดับการตรวจสถานะระหว่างการนำเสนอ VP และ (4) ผลกระทบเมื่อผู้ออกเอกสารหมุนเวียนกุญแจลงลายมือชื่อ (Issuer Key Rotation) ตามที่ §13.7.7 อธิบาย + +**กฎการอ้างอิงของบทนี้:** ข้อกำหนดทุกข้อ **MUST** สืบย้อนไปยังไฟล์ภายใต้ `th/architecture/` มาตรฐานภายนอกอ้างผ่านไฟล์สถาปัตยกรรมที่ระบุมาตรฐานนั้นไว้แล้ว บทนี้ **MUST NOT** สร้างข้อกำหนดใหม่ที่ไม่มีฐานในเอกสารสถาปัตยกรรม + +การจำแนกข้อความ: + +| ป้าย | ความหมาย | +|------|----------| +| **[ข้อเท็จจริง]** | ข้อความที่ปรากฏในเอกสารสถาปัตยกรรมโดยตรง พร้อมเลขอ้างอิง | +| **[ข้อวิเคราะห์]** | การประมวลข้อเท็จจริงหลายแหล่งเข้าเป็นข้อกำหนดเชิงกำกับดูแล | +| **[ช่องว่าง]** | ประเด็นที่เอกสารสถาปัตยกรรมยังขัดกันเองหรือยังไม่กำหนด ต้องมีมติก่อนบังคับใช้ | + +คำ **MUST / MUST NOT / SHOULD / MAY** ใช้ตามความหมายเชิงข้อกำหนด: ต้อง / ห้าม / ควร / อาจ + +--- + +## 13.2 รูปแบบที่เลือกใช้ + +**[ข้อเท็จจริง]** เอกสารสถาปัตยกรรมของโครงการระบุ Token Status List revision `draft-ietf-oauth-status-list-18` เป็นฐานที่เลือกใช้ [A-ARCH §2, §3] ส่วนการอธิบายพารามิเตอร์และกฎการประมวลผลในบทนี้ตรวจเทียบกับ revision ปัจจุบันที่ตรวจพบ `draft-ietf-oauth-status-list-21` (Internet-Draft; RFC Editor status: Awaiting First editor ณ 1 ตุลาคม 2569) [13] ทั้งสอง revision ยังเป็น draft จึงต้องระบุ revision ของ profile ที่นำไปใช้งานจริง + +**[ข้อเท็จจริง]** เหตุผลที่บันทึกไว้ในสถาปัตยกรรมคือสอดคล้องกับ SD-JWT VC ที่กำหนด `status.status_list.{idx, uri}`, ตรงกับ EUDI Wallet ARF และ media type `application/statuslist+jwt` ลงทะเบียนกับ IANA แล้ว [A-ARCH §3] + +**[ข้อเท็จจริง]** ความต่างจาก W3C Bitstring ตามที่สถาปัตยกรรมบันทึกไว้ คือใช้รูปแบบ token JWT แทน JSON-LD และการบีบอัดข้อมูลต่างกัน [A-ARCH §2] + +**[ข้อวิเคราะห์]** ก่อนนำไปใช้งานจริง ผู้ดูแล profile **MUST** กำหนด revision ที่แน่นอน แล้วประเมินความเข้ากันได้และผลกระทบเมื่อเปลี่ยน revision; ไม่ถือว่า draft-18 และ draft-21 ใช้แทนกันได้โดยอัตโนมัติ + +--- + +## 13.3 โครงสร้าง Status List Token ของ VC + +**[ข้อเท็จจริง]** สถานะใน VC อยู่ในรูป `status: { status_list: { idx, uri } }` และ Status List Token มีโครงสร้างดังนี้ [A-ARCH §ลำดับที่ 2B, §ลำดับที่ 3] + +``` +Header: +{ + "alg": "ES256", + "kid": "did:web:dopa.go.th#key-2026-001", + "typ": "statuslist+jwt" +} + +Payload: +{ + "sub": "https://status.dopa.go.th/statuslists/1", + "iat": 1742284800, + "exp": 1742371200, + "ttl": 43200, + "status_list": { "bits": 2, "lst": "eNrbuRgAAhcBXQ" } +} +``` + +**[ข้อเท็จจริง]** ความหมายของ `exp` กับ `ttl` คือ `exp` (86,400 วินาที = 24 ชั่วโมง) เป็นอายุสูงสุดของ JWT หลังจากนั้นต้อง fetch ใหม่ ส่วน `ttl` (43,200 วินาที = 12 ชั่วโมง) เป็นระยะเวลาที่ Verifier ควร cache และ `ttl` ≤ `exp` เสมอ [A-ARCH §ลำดับที่ 3] + +**[ข้อเท็จจริง]** ขั้นตอนการตรวจสถานะสี่ขั้น [A-ARCH §ลำดับที่ 3] + +1. ตรวจ Referenced Token ก่อน (เช่น claims ที่คาดหมาย ลายมือชื่อ และ `exp`); หากไม่ผ่าน ต้องปฏิเสธและห้ามตรวจ status ต่อ +2. เมื่อ Referenced Token ผ่าน ให้ดึง URI ของ Status List แล้วรับ JWT ที่มี `typ: statuslist+jwt` +3. ตรวจ Status List Token ตามกฎ JWT และ trust/key-resolution rules ที่ Thai profile กำหนด; ตรวจ claims ที่มีอยู่ รวม `sub` ว่าตรงกับ URI ใน `status.status_list.uri`, `iat` ตาม freshness policy และ `exp` หากมี +4. base64url-decode ค่า `lst` แล้วคลายข้อมูลด้วย decompressor ที่เข้ากันได้กับ DEFLATE/ZLIB +5. อ่านค่า status ที่ index `idx` ตามจำนวนบิตต่อรายการ (`bits`) ใน `status_list`; `bits` คือความกว้างของช่อง ไม่ใช่ค่า status และมีค่าได้ 1, 2, 4 หรือ 8 +6. ตรวจ status ตาม semantics ที่ IETF ลงทะเบียนหรือ Thai profile กำหนด; หาก `idx` อยู่นอกขอบเขตหรือ status ไม่รองรับ ให้ปฏิเสธแบบ fail-closed + +--- + +## 13.4 Status Value + +**[ข้อเท็จจริง]** draft IETF Token Status List ที่ตรวจพบกำหนด status ที่ลงทะเบียนไว้คือ `0x00 VALID`, `0x01 INVALID` และ `0x02 SUSPENDED`; ค่า `0x03` และช่วง `0x0C–0x0F` สงวนไว้สำหรับการใช้งานเฉพาะแอปพลิเคชันตาม draft-21; Thai profile ที่กำหนด `0x03 = rotated` ต้องระบุ semantics และ processing rules ให้ชัด ค่า `0x03` จึงไม่ใช่ status `rotated` ที่ IETF กำหนดความหมายให้ ส่วน IETF เปิดให้ use case กำหนดค่าเพิ่มเติมได้ โดย Thai profile อาจกำหนด `0x03` เป็น `rotated` ได้เมื่อระบุความหมายและกฎประมวลผลให้ชัดเจน [TSL draft-21 §§7.1, 14.5.2] + +| ค่า status | สถานะ | แหล่งความหมาย / การใช้ | +|:---:|-------|----------| +| `0x00` | `VALID` | IETF — token ใช้ได้ตามสถานะ (แต่ยังต้องผ่าน validation ของ token เอง) | +| `0x01` | `INVALID` | IETF — token ถูกเพิกถอนหรือยกเลิก | +| `0x02` | `SUSPENDED` | IETF — token ถูกระงับชั่วคราว | +| `0x03` | `rotated` | **Thai-profile extension** — ต้องกำหนดร่วมกันใน ecosystem และระบุการจัดการเมื่อพบค่านี้ | + +ตัวอย่าง: ใช้เฉพาะ `VALID`/`INVALID` รองรับ `bits: 1`; เมื่อรวม `SUSPENDED` ต้องใช้ `bits: 2`; หากใช้ `rotated = 0x03` ด้วยก็ยังใช้ `bits: 2` ได้ ทั้งนี้ `bits: 1` ใช้แทน status `0x02` หรือ `0x03` ไม่ได้ + +--- + +## 13.5 ข้อกำหนดการตรวจประเมินที่เกี่ยวกับการเพิกถอน + +**[ข้อเท็จจริง]** รายการตรวจสำหรับ Issuer [A-ARCH §8 I7–I9] + +| # | ข้อตรวจ | หลักฐาน | +|---|---------|---------| +| I7 | ต้องรองรับ IETF Token Status List (`statuslist+jwt`) | ทดสอบ endpoint | +| I8 | SLA อัปเดต Status List ≤ 15 นาที | เอกสาร SLA | +| I9 | Status List JWT ต้องผ่านการตรวจลายมือชื่อและ key resolution ตาม trust model ที่ Thai profile กำหนด; ตรวจ `exp` หากมี และใช้ `ttl` ตามนโยบาย cache | ตรวจ token | + +**[ข้อเท็จจริง]** รายการตรวจสำหรับ Verifier ข้อ V4 กำหนดให้ตรวจ IETF Token Status List JWT ทั้งลายมือชื่อ คลายข้อมูล และ status [A-ARCH §8 V4] การอ่านค่า status ต้องใช้ความกว้าง `bits` ที่ระบุใน JWT ไม่ใช่ตรวจบิตเดี่ยว + +**[ช่องว่าง G-03 — มีนัยด้านความมั่นคงปลอดภัย]** ค่าความสดของข้อมูลสามค่าในเอกสารสถาปัตยกรรมยังไม่สอดคล้องกัน Issuer มี SLA อัปเดต Status List ≤ 15 นาที [A-ARCH §8 I8] การเพิกถอนฉุกเฉินต้องแพร่กระจายภายใน 1 ชั่วโมง [A-TM §I3] แต่ `ttl` ที่ให้ Verifier cache คือ 12 ชั่วโมง และ `exp` คือ 24 ชั่วโมง [A-ARCH §ลำดับที่ 3] Verifier ที่ cache ตาม `ttl` จะไม่เห็นการเพิกถอนได้นานถึง 12 ชั่วโมง ทำให้ SLA 15 นาทีและเป้าหมาย 1 ชั่วโมงไม่มีผลจริงแบบ end-to-end โดย TSL กำหนด `exp` และ `ttl` เป็น RECOMMENDED ไม่ใช่ REQUIRED; Thai profile จึงต้องระบุค่าและเพดาน cache ให้สอดคล้องกับเป้าหมายการแพร่กระจาย หรือกำหนดกลไก force refresh สำหรับการเพิกถอนฉุกเฉิน + +--- + +## 13.6 การเพิกถอนผู้ตรวจสอบเอกสาร (verifier) ผ่าน Trusted List + +**[ข้อวิเคราะห์]** การเพิกถอนผู้ตรวจสอบเอกสาร (verifier) ดำเนินการผ่านการเปลี่ยนสถานะใน Trusted List เท่านั้น รายงานฉบับนี้ **ไม่ใช้** status list แยกต่างหากสำหรับผู้ตรวจสอบเอกสาร แหล่งข้อมูลอ้างอิงหลักของสถานะการอนุญาตของผู้ตรวจสอบเอกสารคือ Trusted List ของ สพธอ. โครงสร้าง entry และการจัดสถานะตามบทบาทมีรายละเอียดในบทที่ 9 + +**[ข้อเท็จจริง]** JWT ตัวอย่างจัดองค์กรใน `LoTE.TrustedEntitiesList[]` และจัดบริการใน `TrustedEntityServices[]` โดยชนิดบริการอยู่ที่ `ServiceInformation.ServiceTypeIdentifier` ตัวอย่างยังไม่แสดงฟิลด์สถานะครบทั้งระดับองค์กรและบริการ [research/42](../research/42-trusted-list-lote-jwt-format.md) + +**[ข้อกำหนด]** Thai profile ต้องกำหนดฟิลด์สถานะทั้งสองระดับและค่า `active`, `suspended`, `withdrawn` ให้ชัดเจน ผู้ตรวจสอบข้อมูล **ต้อง** ยืนยันสถานะเป็น `active` ทั้งระดับองค์กรและบริการ หากตรวจไม่ได้หรือพบ `suspended` หรือ `withdrawn` **ต้อง** ปฏิเสธ [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) [research/42](../research/42-trusted-list-lote-jwt-format.md) + +**[ข้อกำหนด]** เมื่อ สพธอ. ระงับผู้ตรวจสอบเอกสาร สถานะบริการ Verifier ตาม Thai profile ต้องเปลี่ยนเป็น `suspended` และเมื่อเพิกถอนถาวรต้องเปลี่ยนเป็น `withdrawn` โดยต้องยื่นลงทะเบียนใหม่ตามนโยบายการลงทะเบียนผู้ตรวจสอบ [A-VRP] [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +**[ข้อวิเคราะห์]** Wallet **MUST** ตรวจสถานะของผู้ตรวจสอบเอกสารใน Trusted List **ก่อน** แสดงหน้าขอความยินยอมทุกครั้ง หากยืนยันสถานะ `active` ไม่ได้ Wallet **MUST** ปฏิเสธหรือเตือนผู้ใช้แล้วหยุดการนำเสนอ เนื่องจากการแสดงหน้าขอความยินยอมของผู้ตรวจสอบเอกสารที่ถูกเพิกถอนแล้วเป็นการเปิดช่อง phishing [A-TM §V6, §V8] + +**[ข้อวิเคราะห์]** การเพิกถอนผู้ตรวจสอบเอกสารแบบฉุกเฉิน เช่น กรณีละเมิด PDPA ร้ายแรงหรือกุญแจถูก compromise ใช้การดึง Trusted List ด้วย HTTP `GET` จาก CDN เมื่อ Wallet หรือ Verifier มี connection และกำลังจะตัดสินใจ โดยใช้ `If-None-Match` เพื่อตรวจการเปลี่ยนแปลงและตรวจลายมือชื่อของไฟล์ใหม่ [research/41] ทั้งนี้ ระยะเวลาที่ผู้ตรวจสอบเอกสารและ Wallet เห็นการเพิกถอนขึ้นกับจังหวะการเชื่อมต่อและนโยบาย cache ที่กำหนดใน Trusted List + +--- + +## 13.7 แผนภาพลำดับของการตรวจสถานะและการเพิกถอน + +หัวข้อนี้รวมแผนภาพลำดับ (sequence diagram) ของการตรวจสถานะระหว่างการนำเสนอ VP และรูปแบบการเพิกถอนทั้งหมดในระบบ ประกอบด้วย 5 แผนภาพ ได้แก่ (1) การนำเสนอ VP พร้อมการตรวจสถานะ (2) การที่ผู้ออกเอกสารเพิกถอน VC (3) การเพิกถอนผู้ออกเอกสารผ่าน Trusted List (4) การเพิกถอนผู้ตรวจสอบเอกสารผ่าน Trusted List และ (5) การที่ wallet provider เพิกถอน WUA คือ WIA และ KA พร้อมผลต่อเนื่อง (cascade) โดยปิดท้ายด้วยตารางสรุปใน §13.7.6 และ §13.7.7 เพิ่มการวิเคราะห์ผลกระทบเมื่อผู้ออกเอกสารหมุนเวียนกุญแจลงลายมือชื่อ ทั้งนี้ ทุกแผนภาพประกอบจากลำดับที่บันทึกไว้ในเอกสารสถาปัตยกรรมและบทที่เกี่ยวข้อง มิได้สร้างข้อกำหนดใหม่ + +### 13.7.1 การนำเสนอ VP พร้อมการตรวจสถานะ + +**[ข้อวิเคราะห์]** แผนภาพนี้ประกอบจากลำดับที่บันทึกไว้ในเอกสารสถาปัตยกรรม คือ ลำดับการตรวจสถานะ VC [A-ARCH §ลำดับที่ 3] การตรวจสถานะผู้ตรวจสอบเอกสารใน Trusted List (ดู §13.6 และบทที่ 9) และ gate การตรวจตาม defense state machine [A-TM §3.0, §4.0] + +**Wallet ตรวจ Verifier ก่อนขอความยินยอม** + +```mermaid +sequenceDiagram + autonumber + actor U as ผู้ใช้ + participant W as Wallet + participant V as Verifier (RP) + participant TL as Trusted List
(ETDA) — แหล่งการอนุญาต + + V->>W: VP Request + Verifier Attestation + + Note over W: ตรวจ Verifier ก่อนเปิดเผยข้อมูล + W->>W: ตรวจ client_id_scheme กับ SAN ของ TLS cert (V1) + W->>W: ตรวจ response_uri อยู่ใน SAN เดียวกัน (V4) + W->>TL: GET Verifier Trusted List ฉบับเต็มจาก CDN + TL-->>W: JWT พร้อม LoTE.TrustedEntitiesList[] + W->>W: ตรวจลายมือชื่อ Attestation และ cnf (V2) + W->>W: ตรวจ iat กับ exp โดย clock skew ไม่เกิน 60 วินาที ห้าม cache (V7) + W->>W: ตรวจ status ของ Verifier ทั้งระดับองค์กรและระดับบทบาท + + alt status ของ Verifier เป็น suspended หรือ withdrawn + W-->>U: ปฏิเสธหรือเตือน แล้วหยุด (V8) + else status ของ Verifier เป็น active + W->>W: เทียบ claim ที่ขอกับขอบเขตสิทธิ์ตาม Thai profile (V3) + W-->>U: แสดงชื่อและ logo จาก TL เพื่อขอความยินยอม (V6) + U-->>W: ให้ความยินยอม + W->>W: WSCD ปล่อยกุญแจ Level 3 แล้วลงลายมือชื่อ KB-JWT ด้วย ES256 + W->>V: vp_token + KB-JWT + end +``` + +**Verifier ตรวจ Issuer และสถานะ VC** + +```mermaid +sequenceDiagram + autonumber + actor U as ผู้ใช้ + participant V as Verifier (RP) + participant TL as Trusted List
(ETDA) — แหล่งการอนุญาต + participant UR as Universal Resolver
resolve custom DID เท่านั้น + participant VSL as VC Status List
ของ Issuer + + Note over V: ตรวจฝ่าย Issuer และสถานะ VC + V->>TL: GET Issuer Trusted List JWT ฉบับเต็มจาก CDN + TL-->>V: JWT พร้อม LoTE.TrustedEntitiesList[] + V->>V: ตรวจลายมือชื่อและบริการ Issuer ตาม Thai profile + V->>UR: resolve DID ของ Issuer เฉพาะ custom DID (เช่น did:ndid) + UR-->>V: didDocument + V->>V: ตรวจลายมือชื่อ VC ด้วยกุญแจที่ผูกกับ TL (I7) + V->>V: ตรวจช่วงเวลากุญแจตาม Thai profile และปฏิเสธหากไม่มีข้อมูล + V->>V: ตรวจ KB-JWT กับ nonce และ aud ที่ต้องเป็น client_id ของตน (V5) + V->>VSL: GET status URI ที่ตรวจผูกกับ Issuer ตาม Thai profile (I6) + VSL-->>V: statuslist+jwt พร้อม typ กับ exp และ ttl + V->>V: ตรวจลายมือชื่อ แล้ว base64url-decode และคลาย ZLIB + V->>V: อ่าน status slot ที่ idx ตามความกว้าง `bits` + + alt สถานะเป็น Valid (0) + V-->>U: ยอมรับตามนโยบาย + else สถานะเป็น Invalid หรือ Suspended หรือ rotated + V-->>U: ปฏิเสธ พร้อมรหัสเหตุผลที่ไม่เปิดเผยข้อมูลส่วนบุคคล + end +``` + +**[ข้อวิเคราะห์]** จุดสำคัญสามข้อที่แผนภาพบังคับไว้ + +1. Wallet **MUST** ตรวจสถานะของ Verifier ใน Trusted List **ก่อน** แสดงหน้าขอความยินยอม เพื่อป้องกันการแสดงคำขอของ Verifier ที่ถูกเพิกถอนแล้ว [A-TM §V6, §V8] +2. Verifier **MUST** ตรวจว่า URI ใน `status` claim ของ VC อยู่ในแหล่งที่อนุญาตตาม Thai profile เพื่อกัน status endpoint takeover [A-TM §I6] ทั้งนี้ [JWT ตัวอย่างแบบ LoTE](../research/42-trusted-list-lote-jwt-format.md) ยังไม่แสดงฟิลด์ status URI จึงต้องกำหนดเส้นทางข้อมูลก่อนใช้เกณฑ์นี้ +3. การตรวจลายมือชื่อ **MUST** เกิดก่อนการคลาย ZLIB ทุกครั้ง เพราะการคลายข้อมูลที่ยังไม่ผ่านการตรวจลายมือชื่อคือการประมวลผลข้อมูลที่ผู้โจมตีควบคุมได้ + +### 13.7.2 การที่ผู้ออกเอกสารเพิกถอน VC + +**[ข้อวิเคราะห์]** ผู้ออกเอกสาร (issuer) เปลี่ยนค่า status ในรายการ IETF Token Status List ของตนเองเพื่อระบุสถานะเอกสารรับรอง (VC) ตามรูปแบบและขั้นตอนที่กำหนดไว้ใน §13.2–§13.4 [A-ARCH §2, §3; A-KD08 §9] แผนภาพนี้แสดงทั้งการกระทำเพิกถอนและการที่ผู้ตรวจสอบเอกสาร (verifier) รับรู้สถานะนั้นในการนำเสนอ VP ครั้งถัดไป + +```mermaid +sequenceDiagram + autonumber + participant EV as เหตุการณ์
(ผู้ถือร้องขอ / ข้อมูลเปลี่ยน / key รั่ว / cascade จาก WUA) + participant ISS as ผู้ออกเอกสาร (Issuer) + participant VSL as VC Token Status List
ของ Issuer + participant V as ผู้ตรวจสอบเอกสาร (Verifier) + + EV->>ISS: มีเหตุต้องเพิกถอน VC + ISS->>ISS: หาตำแหน่ง idx ของ VC ในรายการสถานะของตน + ISS->>VSL: เขียนค่า status ที่ idx เป็น 0x01 (INVALID), 0x02 (SUSPENDED) หรือ 0x03 (Thai-profile `rotated`) + ISS->>VSL: ลงลายมือชื่อและเผยแพร่ statuslist+jwt ฉบับใหม่ โดย `bits` ต้องเพียงพอแทนทุกค่าที่ใช้ (SLA ≤ 15 นาที) + Note over ISS,VSL: กรณีฉุกเฉินต้องแพร่กระจายภายใน 1 ชั่วโมง + V->>VSL: GET status URI ที่ตรวจผูกกับ Issuer ตาม Thai profile (ตอนตรวจ VP ครั้งถัดไป) + VSL-->>V: statuslist+jwt พร้อม exp และ ttl + V->>V: ตรวจลายมือชื่อ แล้ว base64url-decode และคลาย ZLIB + V->>V: อ่าน status slot ที่ idx ตามความกว้าง `bits` + alt status = 0x00 (VALID) + V-->>V: ยอมรับต่อเมื่อ Referenced Token ผ่าน validation และผ่านนโยบายอื่น + else status = 0x01 (INVALID) หรือ 0x02 (SUSPENDED) หรือ 0x03 (Thai-profile `rotated`) + V-->>V: ปฏิเสธตามนโยบาย status ที่ประกาศไว้ + else status ไม่รู้จัก / ใช้ร่วมกันไม่ได้ + V-->>V: ปฏิเสธแบบ fail-closed + end +``` + +**[ข้อวิเคราะห์]** อำนาจการเพิกถอน VC เป็นของผู้ออกเอกสารที่ออก VC นั้นเท่านั้น ผู้ตรวจสอบเอกสารรับรู้การเพิกถอนได้เร็วเพียงใดขึ้นกับค่า cache TTL ของรายการสถานะ ซึ่งเป็นช่องว่าง G-03 ที่บันทึกไว้ใน §13.5 ผู้ตรวจสอบเอกสารที่ cache ตาม `ttl` อาจไม่เห็นการเพิกถอนได้นานถึง 12 ชั่วโมง จึง **ควร** มีกลไก force refresh สำหรับการเพิกถอนฉุกเฉิน + +### 13.7.3 การเพิกถอนผู้ออกเอกสารผ่าน Trusted List + +**[ข้อวิเคราะห์]** การเพิกถอนผู้ออกเอกสาร (issuer) ทั้งรายอยู่ในอำนาจของสำนักงานพัฒนาธุรกรรมทางอิเล็กทรอนิกส์ (สพธอ.) ในฐานะผู้ดูแล Trusted List สพธอ. เปลี่ยนสถานะของผู้ออกเอกสารใน Trusted List เป็น `suspended` หรือ `withdrawn` [A-ARCH §6] แล้วเผยแพร่ไฟล์ที่ลงลายมือชื่อผ่าน CDN ผู้ตรวจสอบเอกสารเรียก `GET` พร้อม `If-None-Match` เมื่อรับ VP และก่อนตัดสินใจ [research/41] + +```mermaid +sequenceDiagram + autonumber + participant ETDA as สพธอ.
(ผู้ดูแล Trusted List) + participant TL as Trusted List
(CDN) + participant V as ผู้ตรวจสอบเอกสาร (Verifier) + + ETDA->>ETDA: พบเหตุร้ายแรง (key รั่ว / ประเมินไม่ผ่าน / ละเมิดนโยบาย) + ETDA->>TL: เปลี่ยนสถานะบริการ Issuer ใน TrustedEntityServices[] + Note over V: ตอนรับ VP ครั้งถัดไป + V->>TL: GET https://trustme.etda.or.th/.well-known/iss.txt
If-None-Match: ETag เดิม + TL-->>V: 304 หรือ 200 + Trusted List ที่เกี่ยวข้องฉบับเต็ม + V->>V: ตรวจ JWT signature, LoTESequenceNumber และ NextUpdate + V->>TL: ค้นหาบริการ Issuer ใน TrustedEntitiesList[] + TL-->>V: status ของ Issuer + alt status ของ Issuer = active + V->>V: ตรวจ VC และสถานะราย VC ตามปกติ (ดู §13.7.1) + else status ของ Issuer ≠ active + V-->>V: ปฏิเสธ VC ทุกใบของ Issuer นั้น โดยไม่ต้องอ่านสถานะราย VC + end +``` + +**[ข้อวิเคราะห์]** เมื่อผู้ออกเอกสารถูกเพิกถอนใน Trusted List VC ทุกใบที่ผู้ออกเอกสารนั้นเคยออกจะใช้ไม่ได้ในการตรวจสอบ โดยไม่จำเป็นต้องเปลี่ยนค่า status ในรายการสถานะราย VC เนื่องจากแหล่งข้อมูลอ้างอิงหลักของสถานะการอนุญาตของผู้ออกเอกสารคือ Trusted List หากยืนยันสถานะ `active` ไม่ได้ ผู้ตรวจสอบเอกสาร **ต้อง** ปฏิเสธ VC นั้น [A-ARCH §6, §8] การเพิกถอนผู้ออกเอกสารจึงมีผลต่อ VC ทุกใบของผู้ออกเอกสารรายนั้น + +### 13.7.4 การเพิกถอนผู้ตรวจสอบเอกสารผ่าน Trusted List + +**[ข้อวิเคราะห์]** การเพิกถอนผู้ตรวจสอบเอกสาร (verifier) ดำเนินการผ่าน Trusted List เท่านั้น ตาม §13.6 ผู้มีอำนาจคือ สพธอ. จุดสำคัญคือ กระเป๋าเอกสารดิจิทัล (wallet) **ต้อง** ตรวจสถานะของผู้ตรวจสอบเอกสาร **ก่อน** แสดงหน้าขอความยินยอมทุกครั้ง เพราะการแสดงหน้าขอความยินยอมของผู้ตรวจสอบเอกสารที่ถูกเพิกถอนแล้วเป็นการเปิดช่อง phishing [A-TM §V6, §V8] + +```mermaid +sequenceDiagram + autonumber + actor U as ผู้ใช้ + participant ETDA as สพธอ.
(ผู้ดูแล Trusted List) + participant TL as Trusted List
(CDN) + participant W as Wallet + + ETDA->>ETDA: พบเหตุ (ละเมิด PDPA ร้ายแรง / key รั่ว) + ETDA->>TL: เปลี่ยนสถานะบริการ Verifier ใน TrustedEntityServices[] + Note over W: ตอนได้รับ VP Request ครั้งถัดไป + W->>TL: GET https://trustme.etda.or.th/.well-known/ver.txt
If-None-Match: ETag เดิม + TL-->>W: 304 หรือ 200 + Trusted List ที่เกี่ยวข้องฉบับเต็ม + W->>W: ตรวจ JWT signature, LoTESequenceNumber และ NextUpdate + W->>TL: ค้นหาบริการ Verifier ใน TrustedEntitiesList[] + TL-->>W: status ของ Verifier + alt status = active + W-->>U: แสดงชื่อและ logo จาก TL เพื่อขอความยินยอม + else status ≠ active + W-->>U: ปฏิเสธหรือเตือน แล้วหยุดการนำเสนอ (กัน phishing) + end +``` + +**[ข้อวิเคราะห์]** ระยะเวลาที่ Wallet เห็นการเพิกถอนขึ้นกับจังหวะที่ได้รับ VP Request และผลการตรวจ `GET` จาก CDN ในครั้งนั้น ระบบไม่ใช้ message broker หรือ background polling การตรวจจึงเกิดตรงจุดที่ Wallet ต้องตัดสินใจว่าจะขอความยินยอมจากผู้ใช้หรือไม่ [research/41] + +### 13.7.5 การที่ wallet provider เพิกถอน WUA คือ WIA และ KA พร้อมผลต่อเนื่อง + +**[ข้อวิเคราะห์]** WUA เป็นคำรวม (umbrella term) ประกอบด้วย Wallet Instance Attestation (WIA) และ Key Attestation (KA) การเพิกถอนและสถานะของทั้งสองประเภทเป็นไปตาม EUDI คือใช้ IETF Token Status List ที่ wallet provider เผยแพร่ ผ่าน field `client_status` (WIA) และ `key_storage_status` (KA) รายละเอียดอยู่ใน [บทที่ 10 §10.5](10-wallet-unit-attestation.md) แผนภาพนี้แสดง 3 ผลต่อเนื่อง (chain) ที่เกิดขึ้นเมื่อ wallet provider เพิกถอน WUA + +```mermaid +sequenceDiagram + autonumber + participant EV as เหตุการณ์
(อุปกรณ์สูญหาย / key รั่ว / WSCD ถูก compromise) + participant WP as Wallet Provider + participant WSL as WUA Token Status List
client_status (WIA) / key_storage_status (KA) + participant ISS as ผู้ออกเอกสาร (Issuer) + participant VSL as VC Token Status List
ของ Issuer + participant V as ผู้ตรวจสอบเอกสาร (Verifier) + + EV->>WP: มีเหตุต้องเพิกถอน WUA + alt เพิกถอน WIA (wallet instance ถูก compromise) + WP->>WSL: ปรับค่า status ในรายการ client_status ของ WIA + else เพิกถอน KA (WSCD หรือกุญแจถูก compromise) + WP->>WSL: ปรับค่า status ในรายการ key_storage_status ของ KA + Note over WP,WSL: KA แบบ type-shared: 1 index ครอบทุก wallet unit ของ WSCD ประเภทนั้น + end + Note over ISS: chain 1 — Issuer ติดตามสถานะ WIA/KA ที่เคยตรวจไว้เป็นระยะ เช่น รายวัน + ISS->>WSL: ตรวจสถานะ WIA/KA ของ wallet unit ที่เคยออกเอกสารให้ + WSL-->>ISS: revoked + Note over ISS,VSL: chain 2 — Issuer ต้องเพิกถอน VC ที่ผูกกับ wallet unit นั้น (CIR 2024/2977 Art. 5(4)(b)) + ISS->>VSL: ตั้งค่า status ของ VC ที่ผูกไว้เป็น 0x01 (INVALID) ในรายการสถานะของตน + Note over V: chain 3 — Verifier ไม่อ่านสถานะ WUA โดยตรง (RP เห็นเฉพาะ Use Case 1) + V->>VSL: GET status uri ตอนตรวจ VP + VSL-->>V: statuslist+jwt + V->>V: อ่าน status slot ตาม bits และ idx แล้วปฏิเสธ VC ที่ผูกกับ wallet unit ที่ถูกเพิกถอน +``` + +**[ข้อวิเคราะห์]** แผนภาพนี้ตอบคำถามหลักสองข้อ + +1. **ผู้ออกเอกสารต้องเพิกถอน VC ที่ผูกกับ WUA หรือไม่** — **ต้อง** เมื่อ wallet provider เพิกถอน WIA หรือ KA ผู้ออกเอกสารที่เคยออก VC ให้ wallet unit นั้น **ต้อง** เพิกถอน VC ที่ผูกไว้ผ่าน IETF Token Status List **ของผู้ออกเอกสารเอง** ตาม CIR 2024/2977 Art. 5(4)(b) [บทที่ 10 §10.5.3](10-wallet-unit-attestation.md) รายการสถานะของ WUA ใช้กับ wallet instance และกุญแจ ส่วนรายการสถานะของผู้ออกเอกสารใช้กับ VC ที่ผูกกับ wallet unit นั้น จึงเป็นคนละรายการกัน +2. **ผู้ตรวจสอบเอกสารรับรู้ได้อย่างไร** — ผู้ตรวจสอบเอกสารได้รับ WUA เฉพาะรูปแบบยืนยันตัวตน (Use Case 1) และรับรู้ผลของการเพิกถอน WUA **โดยอ้อม** ผ่านรายการสถานะ VC ของผู้ออกเอกสาร (chain 3) ผู้ออกเอกสารตรวจสถานะ WUA โดยตรงในกระแสงาน OID4VCI ตาม [บทที่ 10 §10.3.1](10-wallet-unit-attestation.md) + +_ข้อสังเกต:_ กรณี KA แบบ type-shared การเพิกถอน 1 index มีผลต่อ wallet unit ทุกหน่วยที่ใช้ WSCD ประเภทเดียวกัน ผู้ออกเอกสารจึงต้องเพิกถอน VC ทุกใบที่ผูกกับ WSCD ประเภทนั้น ผลต่อเนื่องจึงกว้างกว่าการเพิกถอน WIA รายหน่วยมาก + +### 13.7.6 ตารางสรุปรูปแบบการเพิกถอน + +**[ข้อวิเคราะห์]** ตารางต่อไปนี้สรุปรูปแบบการเพิกถอนและการหมุนเวียนกุญแจทั้ง 6 แบบใน §13.7.2–§13.7.7 ว่าใครมีอำนาจ ใช้กลไกใด ใครต้องรับรู้ และรับรู้ได้อย่างไร + +| รูปแบบการเพิกถอน / การหมุนเวียนกุญแจ | ผู้มีอำนาจ | กลไก / รายการสถานะ | ผู้ที่ต้องรับรู้ และรับรู้ได้อย่างไร | เวลาเป้าหมาย / ช่องว่าง | +|---|---|---|---|---| +| เพิกถอน VC (§13.7.2) | ผู้ออกเอกสารที่ออก VC นั้น | VC Token Status List ของผู้ออกเอกสาร | ผู้ตรวจสอบเอกสารอ่านค่า status ตอนตรวจ VP | SLA ≤ 15 นาที (ฉุกเฉิน ≤ 1 ชั่วโมง) จำกัดด้วย cache TTL — ช่องว่าง G-03 | +| เพิกถอนผู้ออกเอกสาร (§13.7.3) | สพธอ. | สถานะใน Trusted List (`suspended`/`withdrawn`) | ผู้ตรวจสอบเอกสารเรียก `GET` จาก CDN ตอนรับ VP และตรวจลายมือชื่อ | ตรวจ ณ จุดตัดสินใจ; ปฏิเสธหากยืนยันสถานะ `active` ไม่ได้ [research/41] | +| เพิกถอนผู้ตรวจสอบเอกสาร (§13.7.4) | สพธอ. | สถานะใน Trusted List (`suspended`/`withdrawn`) | กระเป๋าเอกสารดิจิทัลเรียก `GET` จาก CDN **ก่อน** แสดงหน้าขอความยินยอม | ตรวจ ณ จุดตัดสินใจ; กัน phishing [A-TM §V6, §V8] | +| เพิกถอน WIA (§13.7.5) | wallet provider ที่ออก WIA | `client_status` list ของ wallet provider | ผู้ออกเอกสาร อ่านตอน OID4VCI และ poll เป็นระยะ → cascade เพิกถอน VC → ผู้ตรวจสอบเอกสารรับรู้ผ่านสถานะ VC | poll เป็นระยะ เช่น รายวัน + SLA ของ VC status | +| เพิกถอน KA (§13.7.5) | wallet provider ที่ออก KA | `key_storage_status` list (รายหน่วย หรือ type-shared) | เหมือน WIA; หาก type-shared จะ cascade ครอบ WSCD ทั้งประเภท | poll เป็นระยะ เช่น รายวัน + SLA ของ VC status | +| หมุนเวียนกุญแจลงลายมือชื่อของผู้ออกเอกสาร (§13.7.7) | ผู้ออกเอกสารเอง (T0–T3) | (ก) DID Document `assertionMethod` (ข) ข้อมูลกุญแจใน `ServiceInformation.ServiceDigitalIdentity` ของ Trusted List; การกำหนดช่วงเวลาต่อกุญแจยังต้องระบุใน Thai profile [research/42](../research/42-trusted-list-lote-jwt-format.md) (ค) Campaign Manifest ผ่าน `/.well-known/reissue-campaigns/{id}` (ง) VC Token Status List ค่า status `0x03` = `rotated` ตาม Thai-profile extension | กระเป๋าเอกสารดิจิทัลรับรู้ผ่าน push/pull ตาม Campaign Protocol | ขึ้นกับ trigger และนโยบายการเผยแพร่ | + +**[ข้อวิเคราะห์]** จุดที่ผู้พัฒนาต้องไม่สับสน คือ การเพิกถอนสามระดับใช้รายการสถานะคนละชุดกัน ได้แก่ (1) รายการสถานะ VC ของผู้ออกเอกสาร (2) รายการสถานะ WUA ของ wallet provider ผ่าน `client_status` และ `key_storage_status` และ (3) Trusted List ของ สพธอ. สำหรับสถานะการอนุญาตของผู้ออกเอกสารและผู้ตรวจสอบเอกสาร แม้ (1) และ (2) ใช้รูปแบบ IETF Token Status List เหมือนกัน แต่เป็นคนละรายการ มีผู้เผยแพร่และวัตถุประสงค์ต่างกัน ตาม §13.7.5 และ [บทที่ 10 §10.5.3](10-wallet-unit-attestation.md) + +### 13.7.7 ผลกระทบเมื่อผู้ออกเอกสารหมุนเวียนกุญแจลงลายมือชื่อ (Issuer Key Rotation) + +หัวข้อนี้ตอบคำถามเชิงกำกับดูแลสามข้อเกี่ยวกับการหมุนเวียนกุญแจลงลายมือชื่อของผู้ออกเอกสาร ได้แก่ (1) ผลกระทบต่อเอกสารรับรอง (VC) เดิมที่ลงลายมือชื่อด้วยกุญแจรุ่นก่อน (2) กลไกที่กระเป๋าเอกสารดิจิทัล (wallet unit) ใช้รับรู้ว่ากุญแจถูกหมุนเวียน (3) ความจำเป็นในการออก VC ใหม่ ทั้งนี้ หัวข้อนี้ประกอบจากข้อกำหนดในเอกสารสถาปัตยกรรม [A-KD08 §1–§9], [A-TM §I3, §I7] และงานวิจัยภายใน [research/07], [research/07-1] มิได้สร้างข้อกำหนดใหม่ที่ไม่มีฐานในเอกสารต้นทาง + +#### 13.7.7.1 สถานะของกุญแจลงลายมือชื่อผู้ออกเอกสาร 4 สถานะ + +**[ข้อเท็จจริง]** วงจรชีวิตของกุญแจลงลายมือชื่อของผู้ออกเอกสารแบ่งเป็น 4 สถานะ ดังนี้ [research/07 §สถานะของกุญแจผู้ออก] + +| สถานะ | ลงลายมือชื่อ VC ใหม่ | ตรวจ VC เก่า | อยู่ใน DID Document | +|-------|:---:|:---:|:---:| +| **Created** | ❌ | ❌ | ✅ (กำหนดวันเริ่มใช้ในอนาคต) | +| **Active** | ✅ | ✅ | ✅ (`authentication` + `assertionMethod`) | +| **Inactive** | ❌ | ✅ | ✅ (`assertionMethod` เท่านั้น) | +| **Decommissioned** | ❌ | ❌ | ❌ (ลบ + ทำลายกุญแจลับ) | + +**[ข้อวิเคราะห์]** กฎสำคัญคือ กุญแจเดิมต้องอยู่ใน `assertionMethod` จนกว่า VC ทั้งหมดที่ลงลายมือชื่อด้วยกุญแจนั้นจะหมดอายุ การลบกุญแจออกจาก DID Document ก่อนกำหนดจะทำให้ VC ที่ออกด้วยกุญแจนั้นตรวจสอบไม่ได้อีกต่อไป ทั้งนี้ ต้องเสริมด้วยข้อจำกัดของ W3C DID Core ที่ไม่มีช่องบอกสถานะรายกุญแจ สถานะ `Inactive` และ `Created` จึงเป็นวินัยฝั่งผู้ออกเอกสาร ผู้ตรวจสอบเอกสารตรวจข้อนี้โดยตรงไม่ได้ จึงต้องพึ่ง Trusted List ของ สพธอ. เป็นกลไกยืนยันภายนอกตามที่ §13.7.7.3 อธิบาย + +#### 13.7.7.2 รูปแบบการหมุนเวียนกุญแจ 4 รูปแบบ (T0–T3) + +**[ข้อเท็จจริง]** การหมุนเวียนกุญแจลงลายมือชื่อของผู้ออกเอกสารจำแนกเป็น 4 รูปแบบตาม trigger และความรุนแรง [A-KD08 §1], [research/07-1 §2] ดังนี้ + +| Trigger | ความรุนแรง | SLA แจ้งเตือน | Grace Period | VC เก่าใช้ได้หรือไม่ | +|---------|:---------:|:-------------:|:------------:|:---------------------:| +| **T0 — Scheduled Rotation** (รายปี ตามนโยบาย [A-KD08 §1]) | 🟢 ปกติ | ไม่ต้องแจ้ง | ไม่กำหนด (จนกว่า VCs จะหมดอายุ) | ✅ ใช้ได้ตราบเท่าที่กุญแจเดิมยังอยู่ใน `assertionMethod` | +| **T1 — Key Compromise** ([A-KD08 §1], [A-TM §I3]) | 🔴 ฉุกเฉิน | ≤ 15 นาที (push) | 0 (ทันที) | ❌ ต้องปฏิเสธเมื่อยืนยันสถานะกุญแจเดิมไม่ได้ตาม Thai profile [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| **T2 — Early Decommissioning** (compliance / ยุติใช้ก่อน VCs หมดอายุ) | 🟡 เร่งด่วน | ≥ 90 วันล่วงหน้า | ≥ 90 วัน | ✅ ใช้ได้จนถึง `deadline` | +| **T3 — Coordinated Migration** (algorithm upgrade เช่น `ES256 → EdDSA`) | 🟢 ปกติ | ≥ 30 วันล่วงหน้า | ≥ 30 วัน | ✅ ใช้ได้จนถึง `deadline` | + +**[ข้อวิเคราะห์]** ข้อแตกต่างสำคัญระหว่าง T0 กับ T1–T3 คือ T0 เป็นการหมุนเวียนแบบโปร่งใส (transparent) เนื่องจากกุญแจเดิมยังอยู่ใน `assertionMethod` ผู้ตรวจสอบเอกสารยังตรวจ VC เก่าได้ จึงไม่ต้องมีกลไกแจ้ง wallet ส่วน T1–T3 เป็นการหมุนเวียนที่มี deadline หรือเป็นเหตุฉุกเฉิน จำเป็นต้องมีกลไกแจ้ง wallet เพื่อให้ผู้ใช้ออก VC ใหม่ก่อนกุญแจเดิมถูกถอนออกจาก DID Document ตามแนวทาง Campaign Protocol ใน [research/07-1] + +#### 13.7.7.3 กลไกที่กระเป๋าเอกสารดิจิทัลใช้รับรู้การหมุนเวียนกุญแจ + +**[ข้อวิเคราะห์]** กระเป๋าเอกสารดิจิทัล (wallet unit) อาจรับรู้การหมุนเวียนกุญแจของผู้ออกเอกสารผ่าน 4 ช่องทางตามสถาปัตยกรรมที่เสนอ ทั้งนี้ JWT ตัวอย่างใน [research/42](../research/42-trusted-list-lote-jwt-format.md) ยังไม่แสดงช่วงเวลาที่มีผลของกุญแจแต่ละรุ่นใน Trusted List + +| ช่องทาง | สารที่ส่ง | ใช้กับ Trigger | ผู้เผยแพร่ | +|--------|----------|:---------------:|------------| +| (1) DID Document `assertionMethod` | กุญแจเดิมยังอยู่ใน `assertionMethod` หรือถูกถอนออก | T0 (มีอยู่) / T1–T3 (ถูกถอนเมื่อถึง deadline) | ผู้ออกเอกสาร | +| (2) Trusted List `ServiceInformation.ServiceDigitalIdentity` | กุญแจสาธารณะหรือ DID; ช่วงเวลาต่อกุญแจต้องกำหนดเพิ่มใน Thai profile | ทุก trigger เมื่อ profile รองรับ | สพธอ. | +| (3) Reissue Campaign Manifest ผ่าน `/.well-known/reissue-campaigns/{id}` | campaign id, `affected_kid`, `trigger`, `severity`, `deadline` | T1–T3 | ผู้ออกเอกสาร | +| (4) VC Token Status List ค่า status `0x03` = `rotated` (Thai-profile extension) | ระบุว่า VC ถูกแทนด้วยใบใหม่แล้ว | T1–T3 (หลังออกใบใหม่) | ผู้ออกเอกสาร | + +**[ข้อกำหนด]** กระเป๋าเอกสารดิจิทัลต้องตรวจข้อมูลกุญแจของ Issuer ใน Trusted List และตรวจช่วงเวลาต่อกุญแจเมื่อ Thai profile ประกาศฟิลด์ดังกล่าว หากไม่มีข้อมูลเพียงพอที่จะยืนยันว่ากุญแจมีผลในเวลาที่ VC ถูกลงลายมือชื่อ ต้องปฏิเสธการใช้ VC นั้น การมีชื่อกุญแจใน DID Document เพียงอย่างเดียวไม่ยืนยันสถานะการอนุญาต [research/42](../research/42-trusted-list-lote-jwt-format.md) [บทสรุปความสอดคล้องขั้นต่ำ](00-minimal-interoperability-reference.md) + +**[ข้อวิเคราะห์]** ช่องทาง (3) เป็นกลไกเฉพาะของ T1–T3 ที่ช่วยให้กระเป๋าเอกสารดิจิทัลรับรู้ล่วงหน้าก่อน deadline และกระตุ้นให้ผู้ใช้ออก VC ใหม่ กลไกนี้ใช้ dual-channel คือ push ผ่าน wallet provider (APN/FCM) สำหรับ T1 ที่ต้องการความเร็ว และ pull ผ่านการเรียก `GET /.well-known/reissue-campaigns/active` เป็นระยะตาม TTL เป็น safety net [research/07-1 §3] ทั้งนี้ Campaign Manifest ต้องลงลายมือชื่อด้วย **กุญแจใหม่** เพื่อกันผู้โจมตีที่ครอบครองกุญแจเดิมปลอม campaign ยกเลิกเหตุฉุกเฉิน [research/07-1 §4.3] + +#### 13.7.7.4 ผลกระทบต่อ VC เดิม และความจำเป็นในการออก VC ใหม่ + +**[ข้อเท็จจริง]** ผลกระทบต่อ VC เดิมที่ลงลายมือชื่อด้วยกุญแจรุ่นก่อนหน้า (`key-1`) จำแนกตาม trigger ดังนี้ [A-KD08 §8], [research/07], [research/07-1] + +| Trigger | VC เดิม (`key-1`) ตรวจสอบได้หรือไม่ | ค่า status ใน Status List | ต้องออก VC ใหม่หรือไม่ | +|---------|:----------------------------------:|:-----------------:|:------------------------:| +| **T0 — Scheduled Rotation** | ✅ ใช้ได้ตราบเท่าที่ `key-1` อยู่ใน `assertionMethod` และ VC ยังไม่หมดอายุ | คงค่า `0` (Valid) | ❓ ไม่บังคับ แต่แนะนำให้ reissue ตามรอบหมุนเวียนประจำปี เพื่อลดหน้าต่างความเสี่ยงจาก compromise [A-KD08 §10] | +| **T1 — Key Compromise** | ❌ ต้องปฏิเสธเมื่อยืนยันสถานะกุญแจเดิมจาก Thai profile ไม่ได้; JWT ตัวอย่างยังไม่มีช่วงเวลาต่อกุญแจ [research/42](../research/42-trusted-list-lote-jwt-format.md) | เปลี่ยนเป็น status `0x03` (`rotated` ตาม Thai-profile extension) ทันทีที่ออก VC ใหม่ | ✅ **บังคับ** ต้องออก VC ใหม่ผูกกับ `key-2` และ mark VC เดิมเป็น `rotated` ตามนโยบายกำกับดูแล | +| **T2 — Early Decommissioning** | ✅ ใช้ได้จนถึง `deadline` ที่กำหนด | คงค่า `0` (Valid) จนกว่าจะถึง `deadline` | ✅ **บังคับ** ต้องออก VC ใหม่ภายใน grace period (≥ 90 วัน) หากต้องการให้ VC ยังใช้งานได้หลัง `deadline` | +| **T3 — Coordinated Migration** | ✅ ใช้ได้จนถึง `deadline` ที่กำหนด | คงค่า `0` (Valid) จนกว่าจะถึง `deadline` | ✅ **แนะนำ** ให้ออก VC ใหม่ภายใน grace period (≥ 30 วัน) เพื่อให้ผู้ใช้พร้อมใช้อัลกอริทึมใหม่ก่อนกุญแจเดิมถูกถอน | + +**[ข้อวิเคราะห์]** หลักการตอบคำถาม "ต้องออก VC ใหม่หรือไม่" มีสามประเด็น + +1. **T0 Scheduled Rotation** — เป็นทางเลือก ไม่บังคับ เพราะ VC เดิมยังตรวจสอบได้ตราบเท่าที่กุญแจเดิมอยู่ใน `assertionMethod` และ VC ยังไม่หมดอายุ ผู้ออกเอกสาร **SHOULD** กำหนดนโยบายหมุนเวียนประจำปีควบคู่กับ reissue เพื่อลดหน้าต่างความเสี่ยง แต่ไม่จำเป็นต้องออก VC ใหม่ให้ผู้ถือทุกรายทันที [A-KD08 §10] +2. **T1 Key Compromise** — ผู้ออกเอกสาร **MUST** ออก VC ใหม่ให้ผู้ถือที่ได้รับผลกระทบ และ **MUST** mark VC เดิมเป็น status `0x03` (`rotated` ตาม Thai-profile extension) ใน Status List ของตน การตรวจ Trusted List ต้องปฏิเสธกุญแจที่ยืนยันสถานะหรือช่วงเวลาที่มีผลไม่ได้ โดย Thai profile ยังต้องกำหนดฟิลด์ช่วงเวลาต่อกุญแจตาม [research/42](../research/42-trusted-list-lote-jwt-format.md) ขั้นตอน mark VC เดิมเป็น `rotated` **MUST** สำเร็จก่อนลบ key handle เดิม [A-KD08 §3] +3. **T2/T3 Planned Scenarios** — เป็นข้อบังคับในเชิงเวลา ผู้ออกเอกสาร **MUST** แจ้ง campaign ล่วงหน้าตาม grace period ที่กำหนด (≥ 90 วัน สำหรับ T2, ≥ 30 วัน สำหรับ T3) กระเป๋าเอกสารดิจิทัล **MUST** แสดง notification ให้ผู้ใช้ทราบตาม milestone (30d / 7d / 1d / deadline) หากผู้ใช้ไม่ดำเนินการภายใน deadline กระเป๋าเอกสารดิจิทัล **MUST** บล็อกการนำเสนอ VC ที่ลงลายมือชื่อด้วยกุญแจรุ่นเก่าที่ถูกถอนออกจาก `assertionMethod` แล้ว + +**[ข้อวิเคราะห์]** กระเป๋าเอกสารดิจิทัล **MUST NOT** ลบ VC เดิมก่อนได้รับ VC ใหม่ที่ลงลายมือชื่อด้วยกุญแจรุ่นใหม่ เพราะหาก reissue ล้มเหลว (เช่น issuer down) ผู้ใช้จะไม่มีหลักฐานในกระเป๋าฯ เลย ทั้งนี้ กระเป๋าเอกสารดิจิทัล **SHOULD** เก็บ VC เดิมไว้ในสถานะ "superseded" เป็นเวลาอย่างน้อย 30 วันหลัง reissue สำเร็จ เพื่อใช้อ้างอิงย้อนหลังหากผู้ตรวจสอบเอกสารยังไม่ได้รับ Trusted List ฉบับใหม่ + +#### 13.7.7.5 แผนภาพลำดับการหมุนเวียนกุญแจ T1 (Key Compromise) + +**[ข้อวิเคราะห์]** แผนภาพนี้แสดงกระแสงานเต็มเมื่อกุญแจลงลายมือชื่อของผู้ออกเอกสารถูก compromise (T1) ครอบคลุม (1) การ push campaign จาก issuer ผ่าน wallet provider (2) การ pull campaign manifest โดย wallet (3) การ reissue ผ่าน OID4VCI (4) การ mark VC เดิมเป็น `rotated` และ (5) การอัปเดต Trusted List โดย สพธอ. + +```mermaid +sequenceDiagram + autonumber + participant EV as เหตุการณ์
กุญแจถูก compromise + participant ISS as ผู้ออกเอกสาร + participant VSL as VC Token Status List
ของผู้ออกเอกสาร + participant WP as Wallet Provider
(APN/FCM) + participant W as Wallet Unit + actor U as ผู้ใช้ + participant ETDA as สพธอ.
ผู้ดูแล Trusted List + participant TL as Trusted List
(ข้อมูลกุญแจตาม Thai profile) + + EV->>ISS: T1 กุญแจ key-1 ถูก compromise (SLA ≤ 15 นาที) + ISS->>ISS: mark key-1 = COMPROMISED
สร้าง Campaign Manifest JWS
(signed by key-2 ใหม่) + ISS->>WP: POST /notify/reissue-campaign
(Campaign Manifest JWS) + WP->>W: Push notification (silent) + Note over W: ถ้า push ล้มเหลว → poll
GET /.well-known/reissue-campaigns/active
ทุก 6 ชม. เป็น safety net + W->>ISS: GET /.well-known/reissue-campaigns/{id} + ISS-->>W: Campaign Manifest (JWS) + W->>W: verify JWS + match `affected_kid`
กับ VC ที่เก็บไว้ + W-->>U: 🔔 Modal blocking: "VC ต้องออกใหม่ด่วน" + U->>W: กด "Reissue Now" + W->>ISS: OID4VCI + reissue_context.campaign_id + ISS->>VSL: mark VC เดิม status = 0x03 (Thai-profile `rotated`, ต้องประกาศ `bits` ที่รองรับ) + ISS-->>W: VC ใหม่ (signed by key-2) + W->>W: เก็บ VC ใหม่
mark VC เดิม = SUPERSEDED + Note over ETDA,TL: คู่ขนาน: สพธอ. ปรับข้อมูลกุญแจและสถานะตาม Thai profile + ETDA->>TL: เผยแพร่ JWT ฉบับใหม่พร้อม LoTESequenceNumber ใหม่ + TL-->>W: Wallet ตรวจครั้งต่อไป: กุญแจเดิมไม่ผ่านเกณฑ์ → ปฏิเสธ +``` + +**[ข้อวิเคราะห์]** จุดสำคัญสี่ประการที่แผนภาพบังคับไว้ + +1. ผู้ออกเอกสาร **MUST** ลงลายมือชื่อ Campaign Manifest ด้วยกุญแจใหม่ (`key-2`) เพราะผู้โจมตีที่ครอบครอง `key-1` จะปลอม campaign "ยกเลิกฉุกเฉิน" ได้ [research/07-1 §4.3, S1] +2. กระเป๋าเอกสารดิจิทัล **MUST** block UI สำหรับ T1 จนกว่าผู้ใช้จะอนุมัติหรือปฏิเสธ เพราะ VC ที่ลงลายมือชื่อด้วยกุญแจที่ compromise มีความเสี่ยงสูงที่จะถูกปลอม +3. การ mark VC เดิมเป็น `rotated` ใน Status List **MUST** สำเร็จก่อนที่กระเป๋าเอกสารดิจิทัลจะลบ key handle เดิม [A-KD08 §3] +4. สพธอ. **MUST** เผยแพร่ Trusted List ฉบับใหม่ที่ระบุผลต่อกุญแจที่ถูก compromise ตาม Thai profile โดยต้องกำหนดฟิลด์สถานะและช่วงเวลาต่อกุญแจให้ตรวจได้ก่อนใช้งานจริง [research/42](../research/42-trusted-list-lote-jwt-format.md) + +#### 13.7.7.6 ตารางเปรียบเทียบ 4 รูปแบบการหมุนเวียนกุญแจ + +**[ข้อวิเคราะห์]** ตารางต่อไปนี้สรุปมิติสำคัญของการหมุนเวียนกุญแจทั้ง 4 รูปแบบ เพื่อให้ผู้พัฒนาเห็นความแตกต่างชัดเจน + +| มิติ | T0 Scheduled | T1 Compromise | T2 Early Decomm. | T3 Migration | +|------|:------------:|:-------------:|:----------------:|:-------------:| +| **Trigger** | นโยบายหมุนเวียนประจำปี | กุญแจรั่วไหล / ถูกขโมย | Compliance / ยุติใช้ | Algorithm upgrade | +| **SLA แจ้งเตือน** | ไม่ต้องแจ้ง | ≤ 15 นาที push / 6 ชม. pull | ≥ 90 วันล่วงหน้า | ≥ 30 วันล่วงหน้า | +| **Grace Period** | ไม่กำหนด | 0 (ทันที) | ≥ 90 วัน | ≥ 30 วัน | +| **Sign Campaign ด้วย key** | ไม่ต้อง sign | key ใหม่ (key-2) | key ใดก็ได้ | key ใดก็ได้ | +| **Push notification** | ไม่ต้อง | ✅ Required | ✅ ที่ 30/7/1 วัน | ✅ ที่ 14/3 วัน | +| **Pull endpoint** | ไม่ต้อง | ✅ Required | ✅ Required | ✅ Required | +| **VC เดิมใช้ได้ไหม** | ✅ จนกว่า VCs จะหมดอายุ | ❌ verify fail ทันที | ✅ จนถึง deadline | ✅ จนถึง deadline | +| **ต้อง reissue หรือไม่** | ❓ ตามนโยบาย | ✅ บังคับ | ✅ บังคับ (ก่อน deadline) | ✅ แนะนำ | +| **Mark VC เดิมเป็น `rotated`** | ไม่บังคับ | ✅ บังคับ (พร้อม reissue) | ✅ บังคับ (เมื่อ reissue) | ✅ แนะนำ (เมื่อ reissue) | +| **TL update ข้อมูลกุญแจตาม Thai profile** | ไม่บังคับ | ✅ ตามเป้าหมายฉุกเฉิน [A-TM §I3] | ✅ ตาม deadline | ✅ ตาม deadline | +| **Wallet block UI** | ไม่ | ✅ modal blocking | ไม่ (in-app banner) | ไม่ (in-app banner) | + +**[ข้อวิเคราะห์]** ข้อสังเกตที่ผู้พัฒนาต้องไม่สับสน มีดังนี้ + +1. **T0 transparent** ต่างจาก T1–T3 ตรงที่ไม่มี deadline และไม่มีกลไกแจ้ง wallet เนื่องจาก `key-1` ยังอยู่ใน `assertionMethod` และ VC ที่ลงด้วยกุญแจนี้ยังตรวจสอบได้ +2. **T1 มี SLA สั้นที่สุด** (15 นาที) และเป็น trigger เดียวที่ต้อง sign campaign ด้วยกุญแจใหม่ เพราะต้องกันผู้โจมตีปลอม campaign ยกเลิกเหตุฉุกเฉิน +3. **T2 มี grace period ยาวที่สุด** (≥ 90 วัน) เพราะไม่มีเหตุเร่งด่วนด้านความปลอดภัย แต่ต้องการเวลาให้ผู้ถือออก VC ใหม่อย่างทั่วถึง +4. **ทั้ง 4 รูปแบบ** ต้องมี audit log และประวัติ KSN เพื่อให้ตรวจสอบย้อนหลังได้ตาม [A-KD08 §8] + +**[ช่องว่าง G-04 — มีนัยด้านการปฏิบัติตามข้อกำหนด]** แม้เอกสารสถาปัตยกรรม [A-TM §I3] จะกำหนด SLA แพร่กระจายการเพิกถอนฉุกเฉินภายใน 1 ชั่วโมงสำหรับกรณี Issuer key compromise แต่กลไก dual-channel (push + pull) ที่บันทึกไว้ใน [research/07-1] อิงกับ push notification ผ่าน wallet provider ซึ่งไม่มีในเอกสารสถาปัตยกรรมระดับ spec ปัจจุบัน ETDA **MUST** กำหนดสัญญาการให้บริการ (SLA) ระหว่าง สพธอ. กับ wallet provider ว่าด้วยเวลาแจ้งเตือน push notification สำหรับ T1 ให้ชัดเจน และ **MUST** กำหนดให้ wallet unit ตรวจ pull endpoint ทุก 6 ชั่วโมงเป็น safety net ก่อนออกข้อกำหนดการตรวจประเมิน + +--- + +## 13.8 ช่องว่างที่เกี่ยวข้อง + +**[ช่องว่าง]** ช่องว่าง G-02, G-03 และ G-04 ที่พบในบทนี้มีรายละเอียดอยู่ในหัวข้อข้างต้น (§13.4, §13.5 และ §13.7.7 ตามลำดับ) รายการรวมของช่องว่างทั้งหมดเพื่อ escalate ต่อ ETDA Security Team อยู่ในบทที่ 14 §14.4 บทนี้จึงไม่ทำซ้ำตารางรวมดังกล่าว + +--- + +{/* METADATA (agent-only — not rendered to readers) +## 13.9 References + +### 13.9.1 แหล่งข้อมูลหลัก — เอกสารสถาปัตยกรรมในโครงการ + +วันที่เข้าถึงทุกไฟล์: **2026-08-07** โดยอ้างสถานะ repository ณ เวอร์ชันเอกสาร 1.0.0 (อัปเดตเพิ่ม research/07 และ research/07-1 สำหรับ §13.7.7) + +| รหัส | เอกสาร | เวอร์ชันที่ระบุในไฟล์ | +|------|--------|---------------------| +| A-ARCH | [../architecture/01-architecture.md](../architecture/01-architecture.md) — สถาปัตยกรรมความน่าเชื่อถือแบบ Trusted List | v2.3 | +| A-TM | [../architecture/phase2/trustlist/10-threat-model.md](../architecture/phase2/trustlist/10-threat-model.md) — Trust List Verification: Threat Model & Mitigations | Draft v1.0 | +| A-KD08 | [../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md](../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md) — Key Rotation & Lifecycle | Draft v1.0 | +| A-VRP | [../policy/01-verifier-registration-policy.md](../policy/01-verifier-registration-policy.md) — นโยบายการลงทะเบียนผู้ตรวจสอบ | Draft v1.0 | +| research/41 | [../research/41-trustlist-serving-filecdn-etag-binary.md](../research/41-trustlist-serving-filecdn-etag-binary.md) — Trustlist ผ่าน File/Edge CDN และ lazy revalidation | v1.10 | +| research/07 | [../research/07-issuer-key-rotation.md](../research/07-issuer-key-rotation.md) — Issuer Key Rotation (did:web) — 4 สถานะของกุญแจผู้ออก (Created/Active/Inactive/Decommissioned) และ 3 scenarios ที่สมมุติฐาน transparent พัง | v2.1 | +| research/07-1 | [../research/07-1-issuer-key-rotation-wallet-notification.md](../research/07-1-issuer-key-rotation-wallet-notification.md) — Issuer Key Rotation: Wallet Notification & Re-issue Campaign — Campaign Protocol, FSM M₁₃, API endpoints สำหรับ T1/T2/T3 | v1.0 | +| บทที่ 10 | [10-wallet-unit-attestation.md](10-wallet-unit-attestation.md) — Wallet Unit Attestation (WUA) — บทภายในเล่ม (ARF) แหล่งของ §13.7.5 (การเพิกถอน WIA/KA และ cascade) สืบย้อนไปยัง EUDI ARF 2.9.0 TS3 และ CIR 2024/2977 Art. 5(4)(b) | v1.6 | + +### 13.9.2 มาตรฐานภายนอกที่อ้างผ่านเอกสารข้างต้น + +เอกสารนี้ไม่อ้างมาตรฐานภายนอกโดยตรง รายการต่อไปนี้คือมาตรฐานที่ไฟล์สถาปัตยกรรมระบุไว้เป็นแหล่งข้อมูลของตน และปรากฏในเนื้อหาของบทนี้ + +| มาตรฐาน | ส่วนที่อ้าง | ไฟล์ที่อ้าง | +|---------|-----------|-----------| +| IETF Token Status List | `draft-ietf-oauth-status-list-18` (architecture baseline); current technical reference: `draft-ietf-oauth-status-list-21` [13] (`0x03` application-specific; Thai profile may define `rotated`) | A-ARCH, A-TM §10, A-KD08 §9 | +| OID4VP 1.0 Final | §5.10, §11 Security Considerations, §11.5, §11.6 | A-TM §10, §V1, §V4, §V5 | +| SD-JWT VC (draft-ietf-oauth-sd-jwt-vc) | §4.1 Key Binding JWT, §6 | A-TM §10, §W6 | +| CIR (EU) 2024/2979 | Wallet requirements (Art. 5, 6, 7 — รวมการหมุนเวียนกุญแจ) | A-KD08 §8 | +| CIR (EU) 2024/2977 | PID issuance (Art. 5(4)(b) — cascade revocation เมื่อ wallet unit ถูกยกเลิก) | A-KD08, บทที่ 10 §10.5.3 | +| W3C DID Core | Data Model (ข้อจำกัดเรื่องสถานะรายกุญแจ) | research/07 | + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 12 — Key Management และ Trusted List Deployment](12-key-management-and-trustlist-deployment.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 14 — ความมั่นคงปลอดภัย: Threat Model และ Conformance ➡️](14-security-threat-model-and-conformance.md) diff --git a/docs/thai-vc-arf/14-security-threat-model-and-conformance.md b/docs/thai-vc-arf/14-security-threat-model-and-conformance.md new file mode 100644 index 0000000..723ef42 --- /dev/null +++ b/docs/thai-vc-arf/14-security-threat-model-and-conformance.md @@ -0,0 +1,217 @@ +--- +description: "14. ความมั่นคงปลอดภัย: Threat Model และรายการตรวจ Conformance — Thai VC ARF 2.0 DRAFT 0" +--- + +# 14. ความมั่นคงปลอดภัย: Threat Model และรายการตรวจ Conformance + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📐 Specification — Draft สำหรับการทบทวนเชิงกำกับดูแล (S8, MASA-129 → MASA-150) +> **เวอร์ชันเอกสาร:** 1.0.2 (2026-08-06) — ตัดการอ้างถึง Phase 3 ในช่องว่าง G-05 (§14.2) เปลี่ยนเป็น "แนวทางในอนาคต" ตามมติให้ยกเลิกการแบ่งระยะ (MASA-157) +> **เวอร์ชันเอกสาร:** 1.0.1 (2026-08-06) — ตัดข้อมูล Access Certificate และกลไก `cnf.jwk` ใน Verifier Attestation ออกจากช่องว่าง G-07 (§14.4) และลบแหล่งอ้างอิง A-ACP ที่ไม่ถูกอ้างแล้ว ให้สรุป G-07 ตรงกับมติใน §12.2.1 (soft key / TEE-WSCD / HSM และประกาศ public key ผ่าน Trusted List หรือ `did:web`) (MASA-153) +> **เวอร์ชันเอกสาร:** 1.0.0 (2026-08-06) +> **แหล่งข้อมูล:** เอกสารสถาปัตยกรรมภายใต้ `th/architecture/` เท่านั้น — ดู §14.5 References +> **ขอบเขต:** เอกสารกำกับดูแลและข้อกำหนดสำหรับการตรวจประเมิน ห้ามตีความเป็นผลทดสอบ production +> **เอกสารที่เกี่ยวข้อง:** [11-cryptographic-suites.md](11-cryptographic-suites.md), [12-key-management-and-trustlist-deployment.md](12-key-management-and-trustlist-deployment.md), [13-vc-status-and-revocation.md](13-vc-status-and-revocation.md), [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + +บทที่ 14 รวม threat model, abuse case, รายการตรวจ conformance ที่ตรวจสอบได้ และสรุปช่องว่างที่ต้อง escalate ของทั้งสามเรื่องในบทที่ 11–13 คือ cryptographic suites, key management/Trusted List deployment และ VC status/revocation ไว้ในบทเดียว เพื่อให้ผู้ตรวจประเมินใช้เป็นกรอบเดียวกัน + +--- + +## 14.1 ขอบเขตและการจำแนกข้อความ + +หัวข้อนี้รวม threat model และรายการตรวจ conformance ของบทที่ 11–13 คือ (1) Cryptographic Suites (2) Key Management/Rotation และ Trusted List Deployment (3) VC Revocation ด้วย IETF Token Status List + +**กฎการอ้างอิงของหัวข้อนี้:** ข้อกำหนดทุกข้อ **MUST** สืบย้อนไปยังไฟล์ภายใต้ `th/architecture/` มาตรฐานภายนอกอ้างผ่านไฟล์สถาปัตยกรรมที่ระบุมาตรฐานนั้นไว้แล้ว หัวข้อนี้ **MUST NOT** สร้างข้อกำหนดใหม่ที่ไม่มีฐานในเอกสารสถาปัตยกรรม + +การจำแนกข้อความ: + +| ป้าย | ความหมาย | +|------|----------| +| **[ข้อเท็จจริง]** | ข้อความที่ปรากฏในเอกสารสถาปัตยกรรมโดยตรง พร้อมเลขอ้างอิง | +| **[ข้อวิเคราะห์]** | การประมวลข้อเท็จจริงหลายแหล่งเข้าเป็นข้อกำหนดเชิงกำกับดูแล | +| **[ช่องว่าง]** | ประเด็นที่เอกสารสถาปัตยกรรมยังขัดกันเองหรือยังไม่กำหนด ต้องมีมติก่อนบังคับใช้ | + +คำ **MUST / MUST NOT / SHOULD / MAY** ใช้ตามความหมายเชิงข้อกำหนด: ต้อง / ห้าม / ควร / อาจ + +--- + +## 14.2 Threat Model และ Abuse Case + +**[ข้อเท็จจริง]** เอกสารสถาปัตยกรรมใช้กรอบ **STRIDE ร่วมกับ VC Trust Triangle** ครอบคลุม 4 actor และกำหนดรหัสภัยคุกคามไว้ 32 รายการ [A-TM §1, §8.1] + +| ชุด | ขอบเขต | จำนวน | +|-----|--------|:-----:| +| W1–W8 | Wallet ปลอมหรือถูกฝัง malware | 8 | +| I1–I8 | Issuer ปลอมหรือถูก impersonate | 8 | +| V1–V9 | Verifier ปลอม MITM หรือขอข้อมูลเกิน | 9 | +| T1–T7 | โครงสร้างพื้นฐาน Trusted List ถูกโจมตี | 7 | + +**[ข้อเท็จจริง]** ข้อความสำคัญของ threat model คือ ทุก attack ต้องผ่านทุก checkpoint จึงจะถึงสถานะ `BREACH_RISK` ซึ่งเกิดได้เมื่อทุก layer fail พร้อมกันเท่านั้น [A-TM §2.0] + +### 14.2.1 Abuse Case ที่เกี่ยวกับ Crypto, Key และ Status โดยตรง + +**[ข้อวิเคราะห์]** ตารางนี้คัดรหัสภัยคุกคามที่เกี่ยวกับสามหัวข้อของเอกสารนี้ พร้อมมาตรการที่ตรวจสอบได้ ทุกแถวอ้างรหัสจาก [A-TM] + +| รหัส | Abuse case | ผลกระทบ | มาตรการที่ตรวจสอบได้ | +|------|-----------|---------|---------------------| +| W2 | Wallet binary ถูกแก้ให้ข้ามการตรวจลายมือชื่อ TL | ยอมรับ TL ปลอม | pin TL public key ใน binary, code signing, runtime integrity check และถ้า pubkey ไม่ตรงให้ app block ตัวเอง | +| W4 | Wallet ใช้ soft key ที่ export ได้ | กุญแจ holder ถูกขโมยไปสร้าง wallet ปลอม | Issuer ปฏิเสธกุญแจที่ไม่มี `key_storage_verified` และตรวจ WSCD attestation chain; หากจะใช้ข้อกำหนดฮาร์ดแวร์จาก Trusted List ต้องกำหนดฟิลด์และกฎใน Thai profile ก่อน [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| W5 | TL rollback หรือ downgrade | ใช้ TL เก่าที่องค์กรหรือบริการถูกเพิกถอนแล้วยังปรากฏว่า active | ตรวจ `LoTE.ListAndSchemeInformation.LoTESequenceNumber` ไม่ให้ย้อนกลับ และตรวจ `NextUpdate` ก่อนใช้ cache [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| W6 | Holder ส่ง VC และ key handle ให้ผู้อื่น | ใช้ VC ของบุคคลอื่น | ผูก `cnf` กับกุญแจใน WSCD ที่ส่งต่อไม่ได้, ตรวจ PoP ใน VP และ biometric gate ก่อนปล่อยกุญแจ | +| W7 | Cache poisoning บนเครื่องที่ root | ใช้ TL ที่ถูกแก้ในเครื่อง | re-sign cached TL ด้วยกุญแจจาก WSCD ก่อนเก็บ, encrypted storage และตรวจ HMAC ทุกครั้งที่ load | +| I3 | กุญแจ Issuer เก่าถูกขโมยแต่ยังถูก trust | ปลอม VC ด้วยกุญแจเก่า | Thai profile ต้องกำหนดสถานะและช่วงเวลาต่อกุญแจ; หากตรวจไม่ได้ให้ปฏิเสธ [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| I6 | Status List endpoint ถูก takeover | ส่งค่า active ให้ VC ที่ถูกเพิกถอน | Status List ต้องเป็น signed JWS; หากจะอ้าง URI จาก Trusted List ต้องกำหนดตำแหน่ง URI และกฎการผูกกับ Issuer ใน Thai profile ก่อน ห้ามเชื่อ URI จาก `status` claim เพียงลำพัง และต้องตรวจลายมือชื่อด้วยกุญแจที่อนุญาต [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| I7 | DID Document ถูกแก้ให้เปลี่ยน controller key | หลอกให้ trust กุญแจของผู้โจมตี | TL เป็นแหล่งข้อมูลอ้างอิงหลักของกุญแจ, monitor การเปลี่ยนกุญแจใน DID Document และเผยแพร่ TL ใหม่ภายใน 24 ชั่วโมง | +| I8 | Replay Issuer ที่ถูกเพิกถอนแล้ว | Wallet ที่ cache เก่ายังยอมรับ VC | Verifier ตรวจ Status List รายใบและสถานะ Issuer ใน Trusted List ที่ยังไม่เกิน `LoTE.ListAndSchemeInformation.NextUpdate` [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| V7 | ใช้ Verifier Attestation ที่หมดอายุจาก cache | Verifier ที่หมดสิทธิ์ยังขอข้อมูลได้ | เพดาน TTL ที่ override ไม่ได้, ห้าม cache attestation และตรวจ `iat` กับ `exp` โดย clock skew ไม่เกิน 60 วินาที | +| V8 | Verifier ถูก compromise หลัง onboard | รายการ Verifier ยังมีผลใช้ได้ | สพธอ. ต้องเผยแพร่การเปลี่ยนสถานะ Verifier เป็น `suspended` หรือ `withdrawn` ตามฟิลด์ที่ Thai profile กำหนด; Wallet ดึงไฟล์ Verifier Trusted List ฉบับเต็มเมื่อเกิดการตรวจสอบ และตรวจสถานะก่อนนำเสนอ [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| T1 | HSM ที่ลงลายมือชื่อ TL ถูก breach | ลงลายมือชื่อ TL ปลอมได้ | กุญแจอยู่ใน HSM ที่ export ไม่ได้, quorum M-of-N แบบ 3-of-5, emergency key แบบ air-gapped และ HSM กระจายตามภูมิศาสตร์ | +| T4 | Rollback หรือ freeze attack ด้วย TL เก่าที่ลายมือชื่อถูกต้อง | ไม่เห็นการเพิกถอนล่าสุด | ตรวจ `LoTESequenceNumber`, `ListIssueDateTime` และ `NextUpdate` ใน `LoTE.ListAndSchemeInformation` พร้อมตรวจ clock skew [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| T5 | เนื้อหา JWT ที่เผยแพร่กับข้อมูลต้นทางไม่ตรงกัน | consumer ตัดสิน trust จากข้อมูลผิด | ตรวจความสอดคล้องของ payload แบบ LoTE ใน JWT ทั้งสามรายการกับข้อมูลต้นทางก่อนลงลายมือชื่อ และใช้ CI gate ปฏิเสธเมื่อพบความคลาดเคลื่อน [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| T6 | ผู้ดูแล TL ภายในกระทำโดยทุจริต | แก้รายการหรือขอบเขตสิทธิ์ | Transparency Log แบบ append-only, 2-person rule ที่แยกหน้าที่ admin ไม่เท่ากับ approver และไม่เท่ากับ signer, public diff feed และบันทึก privileged session | + +**[ข้อเท็จจริง]** หลักการ defense-in-depth ที่บันทึกไว้ 4 ข้อคือ ทุกการตัดสินใจผ่านหลาย layer / Trusted List เป็นแหล่งข้อมูลอ้างอิงหลักของกุญแจและขอบเขตสิทธิ์ตาม Thai profile / มีการเพิกถอนกุญแจ Issuer แบบเรียลไทม์ และเพิกถอนผู้ตรวจสอบเอกสารผ่านการเปลี่ยนสถานะใน Trusted List / มี append-only log ที่ตรวจสอบได้จากภายนอก [A-TM §6] ทั้งนี้ [JWT ตัวอย่างแบบ LoTE](../research/42-trusted-list-lote-jwt-format.md) ยังไม่แสดงฟิลด์ขอบเขตสิทธิ์ครบถ้วน + +**[ช่องว่าง G-05]** threat model ระบุประเด็นที่ยังไม่ปิดไว้เอง 5 รายการ ได้แก่ การย้ายไป PQC ในปี 2028 เป็นต้นไป การ compromise ที่ผู้ผลิต WSCD ซึ่งอยู่นอกขอบเขต TL การทำ side-channel บนกุญแจ holder ซึ่งเป็นความรับผิดของผู้ผลิต การต้านการสอดส่องเป็นวงกว้างด้วย BBS+ ซึ่งเป็นแนวทางในอนาคต และ Bridge TL specification ที่กำหนดกรอบ Q4 2026 [A-TM §9] เอกสารนี้ไม่ขยายขอบเขตเกินรายการดังกล่าว + +--- + +## 14.3 Security Checklist ที่ตรวจสอบได้ + +**[ข้อวิเคราะห์]** รายการนี้ประกอบจาก pen-test scope checklist สำหรับ onboarding ผู้ผลิต Wallet [A-TM §8.2] ขอบเขต conformance ของ Issuer และ Verifier [A-TM §8.3] และรายการตรวจประเมินของ ETDA [A-ARCH §8] ทุกแถวผูกกับรหัสภัยคุกคามหรือรหัสข้อตรวจที่มีอยู่แล้ว + +### 14.3.1 Wallet + +| # | ข้อกำหนด | รหัสที่เกี่ยวข้อง | หลักฐานที่ต้องมี | เกณฑ์ผ่าน | +|---|---------|-----------------|-----------------|-----------| +| SC-01 | App attestation ทำงานจริง | W1 | ผล Play Integrity หรือ App Attest ตอน enrollment | enrollment จาก app ที่ไม่ผ่าน attestation ถูกปฏิเสธ | +| SC-02 | การตรวจลายมือชื่อ TL แก้ไม่ได้ | W2 | code review และ binary ที่ pin pubkey | TL ที่ลายมือชื่อผิดถูกปฏิเสธทุกกรณี | +| SC-03 | บังคับขอบเขตสิทธิ์ตาม Thai profile โดยข้ามไม่ได้ | W3, V3 | negative conformance test เมื่อ Thai profile กำหนดฟิลด์ขอบเขตสิทธิ์แล้ว | VC หรือคำขอที่อยู่นอก scope ถูกปฏิเสธ และผู้ใช้ override ไม่ได้ [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| SC-04 | กุญแจใน WSCD export ไม่ได้ | W4 | WSCD attestation chain | กุญแจที่ไม่มี `key_storage_verified` ถูกปฏิเสธ | +| SC-05 | บังคับ `LoTESequenceNumber` ไม่ย้อนกลับ | W5, T4 | fixture JWT ที่ `LoTESequenceNumber` ย้อนหลังหรือ `NextUpdate` หมดอายุ | JWT ที่ย้อนหลังหรือหมดอายุถูกปฏิเสธ [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| SC-06 | ตรวจ `cnf` key binding | W6, V9 | fixture ที่ `cnf` ไม่ตรง | VP ที่ `cnf` ไม่ตรงถูกปฏิเสธ | +| SC-07 | Cache integrity ของ TL | W7 | ผลตรวจ HMAC และการทดสอบแก้ cache บนเครื่อง root | cache ที่ถูกแก้ถูกตรวจจับและไม่ถูกใช้ | +| SC-08 | ป้องกัน overlay และ phishing UI | W8, V6 | ผลทดสอบ anti-overlay | prompt สำหรับยืนยันสำคัญมาจาก WSCD และ UI แสดงชื่อจาก TL เท่านั้น | +| SC-09 | บังคับ `client_id_scheme` อย่างเข้มงวด | V1, V4 | fixture ที่ SAN ไม่ตรงและ `response_uri` นอก SAN | ถูกปฏิเสธทั้งสองกรณี | +| SC-10 | ไม่ cache Verifier Attestation | V7 | code review และ fixture ที่หมดอายุ | revalidate ทุกครั้งที่นำเสนอ และ clock skew ที่ยอมรับไม่เกิน 60 วินาที | +| SC-11 | ตรวจสถานะ Verifier ใน Trusted List | V8 | fixture JWT แบบ LoTE ที่มีสถานะ `suspended` และ `withdrawn` ตามฟิลด์ที่ Thai profile กำหนด | ปฏิเสธ **ก่อน** แสดงหน้าขอความยินยอม; หากไม่มีฟิลด์สถานะที่ตรวจสอบได้ ให้ปฏิเสธ [research/42](../research/42-trusted-list-lote-jwt-format.md) | + +### 14.3.2 Issuer + +| # | ข้อกำหนด | รหัสที่เกี่ยวข้อง | หลักฐานที่ต้องมี | เกณฑ์ผ่าน | +|---|---------|-----------------|-----------------|-----------| +| SC-12 | กุญแจลงลายมือชื่ออยู่ใน HSM | A-ARCH §8 I1 | ใบรับรอง HSM | ระดับตรงตามข้อกำหนด และไม่มีทางส่งออกกุญแจแบบ plaintext | +| SC-13 | พิธีสร้างกุญแจมีพยาน | A-ARCH §8 I2 | รายงานพิธีและบันทึกวิดีโอ | ครบทั้งพยานและบันทึก | +| SC-14 | แผนหมุนเวียนกุญแจประจำปี | A-ARCH §8 I3 | เอกสารนโยบาย | ระบุรอบ ผู้รับผิดชอบ และขั้นตอน rollback | +| SC-15 | Status List endpoint ตรงตามรูปแบบ | A-ARCH §8 I7 | ผลทดสอบ endpoint | ส่งกลับ `application/statuslist+jwt` ที่ `typ` ถูกต้อง | +| SC-16 | SLA อัปเดต Status List | A-ARCH §8 I8 | เอกสาร SLA และ log การอัปเดต | ไม่เกิน 15 นาที และวัดจาก log ได้ | +| SC-17 | Status List JWT ครบองค์ประกอบ | A-ARCH §8 I9 | ตรวจ token | ลงลายมือชื่อด้วยกุญแจผู้ออก และมีทั้ง `exp` กับ `ttl` โดย `ttl` ≤ `exp` | +| SC-18 | Rotation endpoint ตรวจ PoP ด้วยกุญแจเดิม | A-KD08 §7 | fixture PoP ที่ลงลายมือชื่อด้วยกุญแจผิด | คำขอที่ PoP ไม่ถูกต้องถูกปฏิเสธ | +| SC-19 | mark VC เดิมเป็น `rotated` | A-KD08 §3, §8 | log การหมุนเวียนและสถานะใน list | VC เดิมเปลี่ยนสถานะสำเร็จก่อน Wallet ลบ key handle | +| SC-20 | ตรวจ WSCD attestation ก่อนออก VC | W4, A-KD06 | ผลตรวจ attestation chain | กุญแจที่เป็น software หรือมาจากเครื่อง root ถูกปฏิเสธ | + +### 14.3.3 Verifier + +| # | ข้อกำหนด | รหัสที่เกี่ยวข้อง | หลักฐานที่ต้องมี | เกณฑ์ผ่าน | +|---|---------|-----------------|-----------------|-----------| +| SC-21 | ตรวจการอนุญาตของ Issuer ผ่าน Trusted List | A-ARCH §8 V2 | code review | ตัดสิน trust จาก entry ใน Trusted List เท่านั้น ไม่ใช้ผล resolve DID เป็นหลักฐานการอนุญาต | +| SC-22 | ตรวจ KB-JWT | A-ARCH §8 V3 | ผลทดสอบ | KB-JWT ที่ผิดถูกปฏิเสธ | +| SC-23 | ตรวจ Status List JWT ครบสามขั้น | A-ARCH §8 V4 | fixture ที่ลายมือชื่อผิด, `typ` ผิด, `exp` หมด และ index เกินขอบเขต | ยอมรับเฉพาะ fixture ที่ถูกต้องครบทุกขั้น | +| SC-24 | ตรวจแหล่ง status URI ตาม Thai profile | I6 | code review และ fixture ที่ `status` claim ชี้ไป host อื่น เมื่อ profile กำหนด URI อ้างอิงแล้ว | ไม่เชื่อ URI ที่ credential กำหนดเองโดยไม่มีการตรวจผูกกับแหล่งที่อนุญาต [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| SC-25 | ตรวจลายมือชื่อก่อนคลาย ZLIB | I6 | code review และ fixture ที่ลายมือชื่อผิดแต่ payload ใหญ่ | ไม่คลายข้อมูลที่ยังไม่ผ่านการตรวจลายมือชื่อ | +| SC-26 | ตรวจ `iat` ของ VC ในช่วงอายุกุญแจ | I3 | fixture VC ที่ `iat` อยู่นอกช่วง | ถูกปฏิเสธ | +| SC-27 | ตรวจ `nonce` และ `aud` | V5 | fixture VP ที่ `aud` เป็นของ Verifier อื่น | ถูกปฏิเสธ และอายุ token ไม่เกิน 5 นาที | +| SC-28 | ทดสอบเจาะระบบ VP verification endpoint | A-ARCH §8 V9 | รายงาน pen test | ไม่มีช่องข้ามการตรวจลายมือชื่อหรือการตรวจสถานะ | + +### 14.3.4 โครงสร้างพื้นฐาน Trusted List (ETDA) + +| # | ข้อกำหนด | รหัสที่เกี่ยวข้อง | หลักฐานที่ต้องมี | เกณฑ์ผ่าน | +|---|---------|-----------------|-----------------|-----------| +| SC-29 | quorum M-of-N ในการลงลายมือชื่อ TL | T1 | บันทึกการลงลายมือชื่อ | การลงลายมือชื่อด้วยผู้ถือ share น้อยกว่า quorum ไม่สำเร็จ | +| SC-30 | 2-person rule และแยกหน้าที่ | I4, T6 | signed change log และบันทึก session | admin ไม่เท่ากับ approver และไม่เท่ากับ signer พร้อม audit record ที่ลงลายมือชื่อทุกการแก้ไข | +| SC-31 | Transparency Log แบบ append-only | I4, T4, T6 | public diff feed และผล query witness | ทุกการเผยแพร่ปรากฏใน log และตรวจสอบจากภายนอกได้ | +| SC-32 | ความสอดคล้องระหว่างข้อมูลต้นทางกับ JWT แบบ LoTE | T5 | ผล CI gate ของ JWT ทั้งสามรายการ | CI block การเผยแพร่เมื่อ payload ไม่ตรงกับข้อมูลต้นทาง [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| SC-33 | Multi-CDN และ DNS fallback | T2, T3 | ผลทดสอบ fault injection | ปิด primary แล้วยัง fetch TL ได้จาก endpoint สำรอง | +| SC-34 | ขั้นตอนเพิกถอนฉุกเฉิน | I3, T1 | บันทึกการซ้อม | การเพิกถอนแพร่กระจายภายในเป้าหมายที่กำหนด และมี emergency key แบบ air-gapped | + +> **ห้ามรายงานว่า "ผ่าน"** จนกว่าจะมี artifact จากการรันจริง ได้แก่ hash ของ fixture คำสั่งที่ใช้ timestamp สภาพแวดล้อม และผลลัพธ์ เอกสารฉบับนี้ยัง**ไม่มี**ผลการทดสอบใด ๆ แนบมา + +**[ข้อเท็จจริง]** ขอบเขต TTA Conformance v2.0 สาย Trusted List กำหนด negative test case เพิ่ม 32 รายการ แบ่งเป็น W-series 8 รายการ I-series 8 รายการ V-series 9 รายการ และ T-series 7 รายการ [A-TM §8.1] และทุก state ที่อยู่ในคอลัมน์ Critical States ของตาราง mapping **MUST** มีทั้ง unit test และ negative conformance test สำหรับรหัสภัยคุกคามที่เกี่ยวข้อง [A-TM §7] + +--- + +## 14.4 สรุปช่องว่างที่ต้อง Escalate + +**[ช่องว่าง]** ประเด็นทั้งหมดที่พบจากการตรวจความสอดคล้องภายใน `th/architecture/` เรียงตามความสำคัญ + +| รหัส | ประเด็น | นัยสำคัญ | ผู้ตัดสิน | อ้างอิงที่ขัดกัน | +|------|--------|---------|----------|-----------------| +| G-02 | เอกสารต้นทางบางแห่งประกาศ `bits: 1` แต่กำหนด status `SUSPENDED` (`0x02`) และ Thai-profile `rotated` (`0x03`) ซึ่งแทนด้วย 1 บิตไม่ได้ | interoperability — issuer ต้องใช้ `bits: 2` ขึ้นไปสำหรับ status เหล่านี้; `rotated` เป็นค่าเฉพาะ Thai profile ไม่ใช่ status ที่ IETF ลงทะเบียนหรือข้อกำหนดที่ยืนยันได้จาก CIR โดยตรง | ETDA Security Team | [A-ARCH §ลำดับที่ 3] กับ [A-KD08 §8, §9]; TSL draft-21 §§4.1, 7.1, 14.5.2 | +| G-03 | `ttl` 12 ชั่วโมงขัดกับ SLA อัปเดต 15 นาทีและเป้าหมายเพิกถอนฉุกเฉิน 1 ชั่วโมง | ความมั่นคงปลอดภัย เพราะ Verifier อาจไม่เห็นการเพิกถอนได้นานถึง 12 ชั่วโมง | ETDA Security Team | [A-ARCH §ลำดับที่ 3] กับ [A-ARCH §8 I8] และ [A-TM §I3] | +| G-06 | เอกสารสถาปัตยกรรมกำหนด algorithm profile แบบ mandatory และ optional ไว้เฉพาะกุญแจ Wallet Level 1–3 ยังไม่มี profile ที่บังคับสำหรับกุญแจลงลายมือชื่อ VC ของ Issuer และกุญแจ TL ของ ETDA มีเพียงค่าที่ปรากฏในตัวอย่าง | ต้องมี profile ที่บังคับได้ก่อนใช้เป็นเกณฑ์ตรวจประเมิน | ETDA Security Team | [A-KD01 §9] เทียบกับ [A-ARCH §ลำดับที่ 3] | +| G-01 | HSM ของ Issuer กำหนด FIPS 140-2 Level 3 ขณะที่ HSM ของ ETDA กำหนด FIPS 140-3 Level 3 ขึ้นไป | **มีมติแล้ว (ดู §12.2):** เกณฑ์ขั้นต่ำคือ FIPS 140-2 Level 3 [N1] และยอมรับ FIPS 140-3 Level 3 [N2] เป็นมาตรฐานสืบทอด เนื่องจาก NIST เปลี่ยนใบรับรอง FIPS 140-2 เป็น Historical วันที่ 21 กันยายน 2569 [N3] | ETDA Security Team | [A-ARCH §8 I1] กับ [A-TM §T1] | +| G-07 | ผู้ตรวจสอบเอกสารต้องมีกุญแจลงลายมือชื่อ Request Object และการบังคับใช้ HSM ทุกรายมีต้นทุนสูง | **มีมติแล้ว (ดู §12.2.1):** กำหนดทางเลือกการเก็บกุญแจ 3 แบบตามระดับความเชื่อมั่น คือ soft key (**MAY**) TEE หรือ WSCD (**ควร** เป็นอย่างน้อย) และ HSM (**MAY** ความเชื่อมั่นสูงสุดแต่ต้นทุนสูง) ทุกแบบต้องประกาศ public key ผ่าน Trusted List entry หรือ `did:web` ที่ `/.well-known/did.json` | ETDA Security Team | [A-HSM §2], [A-VRP] | +| G-05 | ประเด็นค้างที่ threat model ระบุไว้เอง 5 รายการ รวมถึงการย้ายไป PQC และ Bridge TL | ต้องมีเจ้าภาพและกรอบเวลา | ETDA Security Team | [A-TM §9] | + +**[ข้อวิเคราะห์]** การจัดทำเอกสารนี้**ไม่พบ** critical security finding ที่ต้องหยุดงาน ช่องว่างข้างต้นเป็นความไม่สอดคล้องของเอกสารที่ต้องมีมติ และยังไม่มีหลักฐานว่าเป็นช่องโหว่ที่ถูกใช้โจมตีได้ทันที หากการทดสอบใน staging พบการข้ามการตรวจลายมือชื่อ การข้ามการตรวจสถานะ หรือการข้าม gate ของ Trusted List **MUST** หยุดการนำขึ้นใช้งานและรายงานทันที + +**[ข้อเท็จจริง]** threat model กำหนดตัวเองเป็น living document ที่ต้องปรับทุกไตรมาสหรือเมื่อมี attack surface ใหม่ ผู้ดูแลคือ ETDA Security Team และต้องทบทวนทุกครั้งที่ TL ขึ้น minor version [A-TM §สถานะเอกสาร] + +--- + +{/* METADATA (agent-only — not rendered to readers) +## 14.5 References + +### 14.5.1 แหล่งข้อมูลหลัก — เอกสารสถาปัตยกรรมในโครงการ + +วันที่เข้าถึงทุกไฟล์: **2026-08-04** โดยอ้างสถานะ repository ณ เวอร์ชันเอกสาร 1.0.0 + +| รหัส | เอกสาร | เวอร์ชันที่ระบุในไฟล์ | +|------|--------|---------------------| +| A-ARCH | [../architecture/01-architecture.md](../architecture/01-architecture.md) — สถาปัตยกรรมความน่าเชื่อถือแบบ Trusted List | v2.3 | +| A-TM | [../architecture/phase2/trustlist/10-threat-model.md](../architecture/phase2/trustlist/10-threat-model.md) — Trust List Verification: Threat Model & Mitigations | Draft v1.0 | +| A-KD01 | [../architecture/phase2/key-derivation/01-key-derivation-architecture.md](../architecture/phase2/key-derivation/01-key-derivation-architecture.md) — Key Derivation Architecture | Draft v1.0 | +| A-KD08 | [../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md](../architecture/phase2/key-derivation/08-key-rotation-lifecycle.md) — Key Rotation & Lifecycle | Draft v1.0 | +| A-KD06 | [../architecture/phase2/key-derivation/06-wscd-key-attestation.md](../architecture/phase2/key-derivation/06-wscd-key-attestation.md) — WSCD Key Attestation | Draft v1.0 | +| A-VRP | [../policy/01-verifier-registration-policy.md](../policy/01-verifier-registration-policy.md) — นโยบายการลงทะเบียนผู้ตรวจสอบ | Draft v1.0 | +| A-HSM | [../policy/07-hsm-and-schema-governance-policy.md](../policy/07-hsm-and-schema-governance-policy.md) — นโยบาย HSM Service Model และ Schema Governance | Draft v1.0 | + +### 14.5.2 มาตรฐานภายนอกที่อ้างผ่านเอกสารข้างต้น + +เอกสารนี้ไม่อ้างมาตรฐานภายนอกโดยตรง รายการต่อไปนี้คือมาตรฐานที่ไฟล์สถาปัตยกรรมระบุไว้เป็นแหล่งข้อมูลของตน + +| มาตรฐาน | ส่วนที่อ้าง | ไฟล์ที่อ้าง | +|---------|-----------|-----------| +| IETF Token Status List | Architecture baseline: `draft-ietf-oauth-status-list-18`; current technical reference: `draft-ietf-oauth-status-list-21` [13] (Internet-Draft) | A-ARCH, A-TM §10 | +| OID4VP 1.0 Final | §5.10, §11 Security Considerations, §11.5, §11.6 | A-TM §10, §V1, §V4, §V5 | +| OID4VCI 1.0 Final | §11 | A-TM §10 | +| SD-JWT VC (draft-ietf-oauth-sd-jwt-vc) | §4.1 Key Binding JWT, §6 | A-TM §10, §W6 | +| EU eIDAS ARF | §2.5, §6 WSCD, §6.6.3.8, Annex 2 Threat Model | A-TM §10, A-KD08, A-KD06 | +| CIR 2024/2979 | ข้อกำหนดการหมุนเวียนกุญแจ | A-KD08 | +| NIST SP 800-63 | IAL และ AAL | A-TM §10 | +| RFC 4033 | DNSSEC | A-TM §I1 | +| RFC 9162 | Certificate Transparency v2 | A-TM §I4, §10 | +| RFC 5891 และ Unicode TR 39 | IDN และการตรวจ homograph | A-TM §I2 | +| OWASP STRIDE | กรอบการทำ threat model | A-TM §1, §10 | +| Apple App Attest และ Android Key Attestation | กลไก attestation ระดับ platform | A-TM §W1, A-KD06 | + +### 14.5.3 แหล่งอ้างอิง NIST สำหรับข้อกำหนด HSM + +ระดับการรับรอง HSM ในหัวข้อนี้อ้างอิงมาตรฐานของ National Institute of Standards and Technology (NIST) โดยตรง เนื่องจากเอกสารสถาปัตยกรรม [A-ARCH §8 I1] และ [A-TM §T1] ระบุระดับ FIPS ไว้แล้ว รายการนี้จึงระบุแหล่งที่มาที่ตรวจสอบได้ของมาตรฐานดังกล่าว + +- [N1] NIST, "FIPS PUB 140-2 — Security Requirements for Cryptographic Modules", พฤษภาคม ค.ศ. 2001 (ปรับปรุงตาม Change Notice ธันวาคม ค.ศ. 2002) — https://csrc.nist.gov/pubs/fips/140-2/upd2/final +- [N2] NIST, "FIPS PUB 140-3 — Security Requirements for Cryptographic Modules", มีนาคม ค.ศ. 2019 — https://csrc.nist.gov/pubs/fips/140-3/final +- [N3] NIST, "Cryptographic Module Validation Program (CMVP) — FIPS 140-2 Transition" — ใบรับรองตาม FIPS 140-2 เปลี่ยนสถานะเป็น Historical ตั้งแต่วันที่ 21 กันยายน ค.ศ. 2026 — https://csrc.nist.gov/projects/cryptographic-module-validation-program + +--- + +**สถานะเอกสาร:** Living document — ทบทวนพร้อม [A-TM] ทุกไตรมาส หรือเมื่อ Trusted List ขึ้น minor version +**ผู้ดูแล:** ETDA Security Team + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 13 — VC Status และ Revocation](13-vc-status-and-revocation.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 15 — ภาคผนวก ➡️](15-appendix.md) diff --git a/docs/thai-vc-arf/15-appendix.md b/docs/thai-vc-arf/15-appendix.md new file mode 100644 index 0000000..daa6b13 --- /dev/null +++ b/docs/thai-vc-arf/15-appendix.md @@ -0,0 +1,220 @@ +--- +description: "15. ภาคผนวก — Thai VC ARF 2.0 DRAFT 0" +--- + +# 15. ภาคผนวก + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารแปลงจาก PDF ต้นฉบับของ สพธอ. (Thai VC ARF v1.1, มกราคม 2568) พร้อมภาคผนวก ข ที่บันทึกการเปลี่ยนแปลงเป็นเวอร์ชัน 2.0 +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + +ภาคผนวกนี้ประกอบด้วยสองส่วน ได้แก่ ส่วน ก ว่าด้วยการทำงานร่วมกัน (interoperability) ระหว่างผู้ออกเอกสาร (issuer) ผู้ถือเอกสาร (holder) และผู้ตรวจสอบเอกสาร (verifier) และส่วน ข ว่าด้วยบันทึกการเปลี่ยนแปลงจากเวอร์ชัน 1.1 เป็นเวอร์ชัน 2.0 + +สถานะปัจจุบันของรูปแบบเอกสารให้ยึด [บทที่ 5](05-standards-and-compliance.md): ระบบยังรองรับ W3C VCDM v1.1 สำหรับเอกสารเดิม และเพิ่ม SD-JWT VC (`dc+sd-jwt`) ใน ARF ฉบับนี้ ข้อความในส่วน ข บันทึกมติและการแก้ไขในอดีตตามลำดับเวลา + +## ก. การทำงานร่วมกัน (Interoperability) ระหว่าง Issuer, Holder และ Verifier + +{/* pdf page 53 */} + +![ภาพรวมของการศึกษา Digital Document](images/p53-0.png) + +**รูปที่ 28 ภาพรวมของการศึกษา Digital Document** + +การออกแบบแนวทางการ Interoperability ระหว่าง Issuer และ Wallet Holder และ Verifier ในโครงการ แสดงได้ดังรูปที่ 28 โดยมีขั้นตอน ดังนี้ + +(1) กำหนด Registry สำหรับการทดสอบขึ้นมา 1 Registry เพื่อให้ Issuer ทำการ Register + +(2) กำหนด Issuer เพื่อใช้ในการทดสอบ โดย Issuer จะทำการสร้าง DID ไว้ เพื่อใช้สำหรับส่งข้อมูล และทำการ Register กับ Registry ที่ได้ทำการจัดเตรียมไว้ + +(3) กำหนด Holder เพื่อใช้ในการ Request ข้อมูลกับทาง Issuer และรับข้อมูล VC ที่จะได้จาก Issuer รวมถึงส่งข้อมูลในรูปแบบ VP ให้กับทาง Verifier + +(4) กำหนด Verifier เพื่อใช้ในการรับข้อมูล VP จาก Holder พร้อมทั้งต้องตรวจสอบความถูกต้องของข้อมูล ตลอดจนการรับ-ส่งข้อมูลกับทาง DIF Universal Resolver + +(5) สร้าง http get เพื่อใช้ในการเข้าถึง Registry พร้อมกับส่งข้อมูลให้กับทาง DIF Universal Resolver + +{/* pdf page 54 */} + +--- + +{/* METADATA (agent-only — not rendered to readers) +## ข. บันทึกการเปลี่ยนแปลงจากเวอร์ชัน 1.1 เป็นเวอร์ชัน 2.0 + +> **ฐานเปรียบเทียบ:** Thai VC ARF เวอร์ชัน 1.1 กับเนื้อหาที่กำหนดสำหรับเวอร์ชัน 2.0 — จัดทำ 4 สิงหาคม 2569 + +### ข.1 วัตถุประสงค์ + +บันทึกนี้สรุปการเพิ่มและการเปลี่ยนแปลงสาระสำคัญใน Thai VC ARF เวอร์ชัน 2.0 เพื่อให้ผู้อ่านประเมินผลกระทบต่อการออก การเก็บ และการแสดง Verifiable Credential (VC) ได้โดยไม่ต้องเปรียบเทียบเอกสารทั้งสองฉบับทีละหน้า + +คำว่า **ต้อง (MUST)** **ควร (SHOULD)** และ **อาจ (MAY)** ในบันทึกนี้ใช้ตาม RFC 2119 และ RFC 8174 [S01] [S02] + +### ข.2 สรุปการเปลี่ยนแปลง + +| รหัส | สถานะ | รายการเปลี่ยนแปลง | ผลต่อผู้ดำเนินการ | +|---|---|---|---| +| CHG-01 | เพิ่ม | เพิ่ม Trust Model 3 (Trusted List + DID) เป็นกลไกความน่าเชื่อถือสำหรับ Issuer, Verifier และ Wallet Provider | ผู้เข้าร่วม MUST ตรวจลายมือชื่อ สถานะ บทบาท ช่วงเวลาที่มีผล และสิทธิ์ที่เกี่ยวข้องจาก ETDA Signed Trusted List ก่อนเชื่อถือเอนทิตี | +| CHG-02 | เพิ่ม | เพิ่ม §7 Interoperability อธิบายการทำงานร่วมกันข้าม ecosystem และข้อจำกัดในการเชื่อมโยงกับ EU | ผู้ให้บริการ SHOULD รองรับมาตรฐานเปิดและกำหนด trust mapping ก่อนแลกเปลี่ยนข้อมูลข้าม ecosystem | +| CHG-03 | เพิ่ม | เพิ่ม sequence diagrams สำหรับ OID4VCI และ OID4VP พร้อม trust checkpoints | Implementer MUST ตรวจ trust ก่อนเปิดเผยข้อมูลหรือยอมรับ VC; diagram เป็นคำอธิบายประกอบและไม่แทนข้อกำหนดของมาตรฐานต้นทาง | +| CHG-04 | เปลี่ยน | เปลี่ยนคำและแนวคิดจาก Wallet Trust Evidence (WTE) เป็น Wallet Unit Attestation (WUA) ตาม EUDI ARF 2.9.0 | เอกสารและ implementation ใหม่ MUST ใช้ WUA; การอ้าง WTE เดิม SHOULD ระบุว่าเป็นคำจาก baseline เก่า | +| CHG-05 | เพิ่ม | เพิ่มข้อกำหนด Cryptographic Suites | Profile MUST ระบุ algorithm, key type, curve, key identifier (`kid`) และเงื่อนไขยกเลิกใช้อย่างชัดเจน; ห้ามอนุมาน interoperability จากชื่อ algorithm เพียงอย่างเดียว | +| CHG-06 | เพิ่ม | เพิ่ม Key Management และ Key Rotation | ผู้ควบคุมกุญแจ MUST กำหนด lifecycle, cryptoperiod, overlap, emergency rotation, audit trail และการป้องกัน private key | +| CHG-07 | เพิ่ม | เพิ่ม VC Revocation และ status checking โดยอ้าง IETF Token Status List | Verifier MUST ตรวจ credential status ตาม policy และ MUST กำหนดพฤติกรรมเมื่อข้อมูลสถานะหมดอายุหรือเข้าถึงไม่ได้ | +| CHG-08 | เพิ่ม | เพิ่มบทนิยามใหม่ โดยคงความหมายของนิยามเดิมในเวอร์ชัน 1.1 | ผู้จัดทำ profile SHOULD ใช้คำตามบทนิยามเดียวกันเพื่อลดความคลาดเคลื่อนระหว่างเอกสารและระบบ | +| CHG-09 | เพิ่ม | เพิ่มหลักการเผยแพร่ Trusted List แบบ signed object ผ่าน CDN และ local cache | Consumer MUST ตรวจลายมือชื่อ Trusted List และ `LoTE.ListAndSchemeInformation.NextUpdate` ก่อนใช้ cache [research/42](../research/42-trusted-list-lote-jwt-format.md) | +| CHG-10 | เพิ่ม | เพิ่มข้อกำหนดด้าน privacy, selective disclosure และ verifier authorization | Wallet MUST แสดง purpose และ claims ที่ร้องขอแก่ผู้ใช้ก่อนให้ความยินยอม และ MUST ไม่ส่งข้อมูลแก่ Verifier ที่ไม่ผ่าน trust policy | +| CHG-11 | เพิ่มหมายเหตุ | เพิ่มหมายเหตุปรับปรุงมาตรฐานใน [§5](05-standards-and-compliance.md) ให้ชี้ไปยัง SD-JWT VC (`dc+sd-jwt`), OID4VCI 1.0 Final และ OID4VP 1.0 Final (พร้อม DCQL) โดยไม่แก้ถ้อยคำตัวอย่างต้นฉบับ v1.1 | ผู้อ่านที่เปิด §5 โดยตรง MUST เห็นหมายเหตุระบุมาตรฐานปัจจุบันและลิงก์ไปยัง §8/§8.1/§8.2 ก่อนนำตัวอย่างในรายงานต้นฉบับไปใช้งานจริง | + +### ข.3 รายละเอียดตามหมวด + +#### ข.3.1 Trust Model 3 และ Trusted List + +เวอร์ชัน 2.0 เพิ่ม Trust Model 3 ซึ่งใช้ DID หรือ key reference ร่วมกับ ETDA Signed Trusted List แทนการใช้สายใบรับรอง X.509 เป็น trust mechanism หลัก รายละเอียดโครงสร้าง การลงทะเบียน การ cache และจุดตรวจอยู่ใน [§9 Trust Model 3](09-trust-model-3.md) + +ข้อกำหนดสำหรับผู้ใช้ Trusted List มีอย่างน้อยดังนี้ + +1. MUST ตรวจลายมือชื่อของ Trusted List และระบุตัว Trusted List signer ที่เชื่อถือได้ +2. MUST ตรวจสถานะระดับองค์กรและระดับ service +3. MUST ตรวจ `LoTESequenceNumber`, `ListIssueDateTime` และ `NextUpdate` ใน `LoTE.ListAndSchemeInformation` พร้อมตรวจช่วงเวลาบริการตาม Thai profile ที่ประกาศใช้ [research/42](../research/42-trusted-list-lote-jwt-format.md) +4. MUST ตรวจ role และ `allowed credential types` ก่อนเชื่อถือการออก VC +5. MUST ปฏิเสธข้อมูลเมื่อ trust check ไม่ผ่าน ก่อนเรียก network service อื่นโดยไม่จำเป็น + +#### ข.3.2 Interoperability + +เวอร์ชัน 2.0 เพิ่มการวิเคราะห์ผลของ Trusted List ต่อ interoperability ในประเทศและข้ามพรมแดนตาม [§7](07-cross-ecosystem-interoperability.md) การใช้ OID4VCI, OID4VP, SD-JWT VC และ DID ร่วมกันช่วยให้แลกเปลี่ยนข้อความมาตรฐานได้ แต่ **มิได้ทำให้เกิดการยอมรับความน่าเชื่อถือข้ามเขตอำนาจโดยอัตโนมัติ** + +ผู้ดำเนินการข้าม ecosystem MUST กำหนด trust mapping, governance, liability, status semantics และ assurance level ให้ตรงกันก่อนเปิดใช้งาน ส่วนการเชื่อมโยง EU MAY ใช้ bridge หรือ certificate wrapping ตาม profile ที่ได้รับอนุมัติ แต่ยังไม่ถือว่า Trust Model 3 ได้รับการยอมรับข้ามพรมแดนโดยปริยาย + +#### ข.3.3 OID4VCI และ OID4VP + +เวอร์ชัน 2.0 เพิ่ม sequence diagrams สำหรับ issuance และ presentation เพื่อแสดงจุดที่ระบบต้องตรวจ Trusted List, WUA, issuer key, verifier authorization และ credential status + +- OID4VCI flow MUST ตรวจ Issuer และ Wallet Provider ตาม trust policy ก่อนออกหรือบันทึก VC +- OID4VP flow MUST ตรวจ Verifier ก่อนเปิดเผยข้อมูล และ Verifier MUST ตรวจ Issuer, credential status และ holder binding ก่อนยอมรับผล +- ทุก flow MUST มี timeout, error path และพฤติกรรมที่กำหนดไว้เมื่อข้อมูล trust หรือ status ไม่พร้อมใช้งาน + +#### ข.3.4 WTE เปลี่ยนเป็น WUA + +คำว่า Wallet Trust Evidence (WTE) ใน baseline เดิมถูกแทนด้วย Wallet Unit Attestation (WUA) ตาม EUDI ARF 2.9.0 [S07] WUA ใช้ยืนยันคุณลักษณะและสถานะที่เกี่ยวข้องกับ Wallet Unit ตาม profile ที่กำหนด การเปลี่ยนชื่อไม่อนุญาตให้ระบบข้ามการตรวจ issuer, validity, key binding หรือ status ของ WUA + +ข้อความใหม่ MUST ใช้คำว่า WUA การเก็บคำว่า WTE ไว้ MAY ทำได้เฉพาะในเชิงประวัติหรือการอ้างเอกสารเวอร์ชัน 1.1 โดยต้องระบุว่าเป็นคำที่เลิกใช้ในเวอร์ชัน 2.0 + +#### ข.3.5 Security + +เวอร์ชัน 2.0 เพิ่มหมวด security ตามข้อเสนอจากการรับฟังความเห็นอุตสาหกรรม [S10] + +- Cryptographic profile MUST ระบุชุดอัลกอริทึมที่อนุญาตและวิธี algorithm negotiation/downgrade prevention +- Private signing key MUST ได้รับการป้องกันตามระดับความเสี่ยง และการใช้กุญแจ MUST มี audit trail ที่ตรวจสอบย้อนหลังได้ +- Key rotation MUST รองรับ `kid`, overlap และ emergency revocation โดยไม่ทำให้การตรวจ VC ที่ออกก่อนหน้าผิดพลาดเกิน policy +- Status mechanism MUST ป้องกัน replay ของข้อมูลสถานะเก่า และ Verifier MUST ตรวจ freshness ก่อนตัดสินใจ +- การใช้ local cache MAY ทำได้ภายในอายุที่กำหนดเท่านั้น; policy MUST ระบุ fail-open หรือ fail-closed แยกตามระดับความเสี่ยง + +#### ข.3.6 หมายเหตุปรับปรุงมาตรฐานใน §5 (MASA-169) + +[§5](05-standards-and-compliance.md) เดิมคงถ้อยคำและตัวอย่างจากรายงานต้นฉบับ v1.1 ไว้ทั้งหมด (JSON-LD VC/VP, OID4VC draft 13, OID4VP draft 20) โดยยังไม่มีหมายเหตุชี้ว่ามาตรฐานที่ประกาศใช้จริงในเวอร์ชัน 2.0 เปลี่ยนไปแล้ว (ต่างจาก [§8](08-implementation-guidelines.md) ที่ปรับตามงาน MASA-158 ไปก่อนหน้า) งาน MASA-169 แก้ช่องว่างนี้โดยเพิ่มหมายเหตุกำกับในบทดังกล่าว (ไม่แก้ถ้อยคำตัวอย่างต้นฉบับ ตามหลักการเดียวกับที่ใช้ในบทที่ 1–8 อื่น ๆ) ระบุว่าเวอร์ชัน 2.0 ใช้: + +- **SD-JWT VC (`dc+sd-jwt`)** [12] แทนเค้าร่าง JSON-LD — เพิ่มเป็น §5.1.1 และ §5.2.1 +- **OID4VCI 1.0 Final** [10] แทน OID4VC draft 13 — เพิ่มหมายเหตุใน §5.3 +- **OID4VP 1.0 Final พร้อม DCQL** [11] แทน OID4VP draft 20 — เพิ่มหมายเหตุใน §5.4 + +ทุกหมายเหตุชี้กลับไปยัง [§8](08-implementation-guidelines.md), [§8.1](08.1-issuance-full-flow-detail.md) และ [§8.2](08.2-oid4vp-full-flow-detail.md) ซึ่งเป็นแหล่งรายละเอียดทางเทคนิคระดับ field-by-field ของเวอร์ชัน 2.0 อยู่แล้ว จึงไม่มีการเขียนตัวอย่างซ้ำใน §5 + +> **หมายเหตุ:** แนวทางแบบ "เพิ่มหมายเหตุกำกับ" ในข้อ ข.3.6 ถูกแทนที่ด้วยแนวทาง "แทนที่เนื้อหาทั้งบท" ตามข้อ ข.3.7 ด้านล่าง (MASA-170) รายละเอียดในข้อนี้คงไว้เป็นบันทึกประวัติการเปลี่ยนแปลงตามกฎ Version History Protection ส่วนสถานะปัจจุบันให้ยึด [§5](05-standards-and-compliance.md) + +#### ข.3.7 แทนที่เนื้อหา §5 ทั้งบทด้วยมาตรฐานเวอร์ชัน 2.0 (MASA-170) + +หลังจากงาน MASA-169 เพิ่มหมายเหตุกำกับตามข้อ ข.3.6 คณะกรรมการทบทวนเห็นว่าแนวทาง "คงถ้อยคำเดิมพร้อมหมายเหตุ" ทำให้ผู้อ่านต้องเทียบสองมาตรฐานพร้อมกันในบทเดียว (SD-JWT VC กับ JSON-LD, OID4VCI 1.0 Final กับ OID4VC draft 13, OID4VP 1.0 Final กับ OID4VP draft 20) ทั้งที่ระบบใช้งานจริงเพียงชุดเดียว จึงมีมติให้ **แทนที่เนื้อหาทั้งบทแทนการเพิ่มหมายเหตุ** งาน MASA-170 จึงเขียน [§5](05-standards-and-compliance.md) ใหม่ทั้งหมดตามหลักการนี้: + +- **ไม่คงตัวอย่างหรือถ้อยคำจากรายงานต้นฉบับเวอร์ชัน 1.1** — เค้าร่างเอกสาร JSON-LD ในตัวอย่างที่ 1–2 (§5.1–§5.2 เดิม) และการอ้างอิง OID4VC draft 13 / OID4VP draft 20 (§5.3–§5.4 เดิม) ถูกลบออกทั้งหมด แทนที่ด้วยตัวอย่างและคำอธิบายของมาตรฐานที่ประกาศใช้จริงโดยตรง +- **§5.1–§5.2** เขียนใหม่ให้ใช้ **SD-JWT VC (`dc+sd-jwt`)** [12] เป็นเค้าร่างเดียว อธิบายโครงสร้าง Issuer-signed JWT / Disclosures / Key Binding JWT (KB-JWT) พร้อมตัวอย่าง JSON ที่ตัดตรงจากมาตรฐาน แทนที่ตัวอย่าง JSON-LD เดิม และปรับขั้นตอนตรวจสอบ VP ให้ตรงกับกลไก selective disclosure ของ SD-JWT +- **§5.3** เขียนใหม่ให้ใช้ **OID4VCI 1.0 Final** [10] โดยตรง (ลบข้อความ "draft Version 13" ทั้งหมด) และปรับตัวอย่าง Metadata/Credential Request ให้ใช้ `format: "dc+sd-jwt"`, `credential_configuration_id` และโครงสร้าง `claims` ตามที่ [§8.1](08.1-issuance-full-flow-detail.md) ใช้งานจริง +- **§5.4** เขียนใหม่ให้ใช้ **OID4VP 1.0 Final พร้อม DCQL** [11] โดยตรง (ลบข้อความ "draft 20" และ Presentation Exchange ทั้งหมด) และปรับตัวอย่าง Authorization Request/Response ให้ใช้ `dcql_query` แทน `presentation_definition`/`presentation_definition_uri` ตามที่ [§8.2](08.2-oid4vp-full-flow-detail.md) ใช้งานจริง +- **เหตุผลที่เปลี่ยน** ระบุไว้ในกล่องเหตุผลที่ต้นบท [§5](05-standards-and-compliance.md): SD-JWT VC รองรับ selective disclosure ระดับ field โดยไม่ต้องสังเคราะห์ VP ทั้งฉบับ และเป็นรูปแบบเดียวที่ OID4VCI/OID4VP ฉบับทางการรองรับโดยตรง ส่วน OID4VCI/OID4VP 1.0 Final เป็นฉบับทางการที่แทนที่ฉบับร่างที่ใช้ในรายงานต้นฉบับแล้ว +- **ไม่กระทบ** [§8](08-implementation-guidelines.md), [§8.1](08.1-issuance-full-flow-detail.md), [§8.2](08.2-oid4vp-full-flow-detail.md) และ [§11](11-cryptographic-suites.md) ซึ่งใช้มาตรฐานชุดนี้อยู่แล้วตั้งแต่ก่อนงาน MASA-158 — งานนี้ทำให้ [§5](05-standards-and-compliance.md) สอดคล้องกับบทเหล่านั้น + +#### ข.3.8 แก้ไขข้อความอ้างอิง W3C VC Data Model v1.1 ที่ตกค้าง (MASA-170) + +หลังจากงาน MASA-170 แทนที่เนื้อหา [§5](05-standards-and-compliance.md) ทั้งบทตามข้อ ข.3.7 คณะกรรมการทบทวนตรวจพบว่ายังมีประโยคตกค้างจากรายงานต้นฉบับ 5 จุดที่ระบุว่ารายงานฉบับนี้ "อ้างอิงโครงสร้างตาม W3C VC Data Model v1.1" หรือ "ตามมาตรฐานของ World Wide Web Consortium (W3C)" ซึ่งขัดแย้งกับเนื้อหาส่วนอื่นของบทเดียวกันที่ระบุว่าเค้าร่างเอกสารรับรองเปลี่ยนเป็น SD-JWT VC แล้ว จึงแก้ไขให้สอดคล้องกันทั้งบท ดังนี้: + +- ย่อหน้าเปิดบท (ก่อน §5.0): ตัดข้อความ "จะอ้างอิงรูปแบบเอกสารรับรองตามโครงสร้าง W3C VC Data Model v1.1 [5]" ออก ระบุแทนว่ารายงานฉบับนี้**ไม่**อ้างอิงโครงสร้างดังกล่าวอีกต่อไป +- §5.1 และ §5.2 (โครงสร้าง VC และ VP): แก้ประโยค "อ้างอิงโครงสร้างตาม World Wide Web Consortium (W3C) [5]" เป็นระบุว่าทั้ง 3 ส่วนบันทึกตามเค้าร่าง IETF SD-JWT VC (`dc+sd-jwt`) [12] ในการปรับปรุงรอบนั้น ส่วน W3C VC Data Model v1.1 [5] เป็นโครงสร้างในรายงานต้นฉบับ +- §5.1 ข้อเปรียบเทียบ claim `vct`: ระบุชัดว่า `type` ตามโครงสร้าง W3C VC Data Model v1.1 เป็นคุณสมบัติที่รายงานต้นฉบับเวอร์ชัน 1.1 เคยใช้ และไม่ใช้แล้วในเวอร์ชัน 2.0 +- §5.3 (OID4VCI): ตัดวลี "รองรับมาตรฐานเอกสารรับรองหลากหลายรูปแบบ รวมถึง W3C Verifiable Credentials" ระบุแทนว่ารายงานฉบับนี้เลือกใช้เฉพาะ SD-JWT VC เพียงรูปแบบเดียว +- กล่องเหตุผลที่ต้นบท: เพิ่มประโยคยืนยันชัดเจนว่าไม่อ้างอิง W3C VC Data Model v1.1 อีกต่อไป + +การแก้ไขนี้ไม่เปลี่ยนแปลงเนื้อหาทางเทคนิคที่ประกาศใช้จริง (ยังเป็น SD-JWT VC, OID4VCI 1.0 Final, OID4VP 1.0 Final พร้อม DCQL ตามข้อ ข.3.7 เดิม) เป็นเพียงการแก้ถ้อยคำอ้างอิงที่ไม่สอดคล้องกันภายในบทเดียวกันให้ถูกต้องตรงกันทั้งหมด + +### ข.4 Traceability + +> **อัปเดต 4 สิงหาคม 2569 (รอบตรวจ QA gate ครั้งที่ 2):** S2–S8 ทั้งหมดถูกรวมเข้า canonical tree `th/thai-vc-arf/` + `en/thai-vc-arf/` แล้วผ่านคอมมิต integration (`docs(arf-v2.0): integrate S0,S1,S3,S4,S5,S6,S9`) และคอมมิต fix ตามหลัง (S6/S8 gap recovery, cross-language link fix) ตารางด้านล่างแสดงสถานะและ path ของ repository ณ เวลาที่ตรวจ + +| รหัส | หัวข้อในเวอร์ชัน 2.0 | แหล่งภายใน | แหล่งมาตรฐานหลัก | สถานะการรวม ณ วันที่จัดทำ | +|---|---|---|---|---| +| CHG-01 | Trust Model 3 | [§9](09-trust-model-3.md) | W3C DID Core [S05] | รวมแล้ว (S2, MASA-123) | +| CHG-02 | Interoperability | [§7](07-cross-ecosystem-interoperability.md) | OID4VCI [S03], OID4VP [S04] | รวมแล้ว (S4, MASA-125) | +| CHG-03 | OID4VCI/OID4VP sequences | [§8](08-implementation-guidelines.md) | OID4VCI [S03], OID4VP [S04] | รวมแล้ว (S5 รายละเอียดและ S7 ภาพรวมวงจรชีวิต end-to-end รวมอยู่ที่ §8, MASA-126/128) | +| CHG-04 | WUA | [§10](10-wallet-unit-attestation.md) | EUDI ARF 2.9.0 [S07] | รวมแล้ว (S6, MASA-127); ย้ายจากคำอธิบายใน §9.2 มาเป็นบทเฉพาะ §10 | +| CHG-05 | Cryptographic Suites | [§11](11-cryptographic-suites.md) | JOSE/JWS และ profile ที่อนุมัติ [S08] | รวมแล้ว (S8, MASA-129 → MASA-135 → แยกบทที่ MASA-150) | +| CHG-06 | Key Management/Rotation | [§12](12-key-management-and-trustlist-deployment.md) | NIST SP 800-57 Part 1 Rev.5 [S09] | รวมแล้ว (S8, MASA-129 → MASA-135 → แยกบทที่ MASA-150) | +| CHG-07 | VC Revocation | [§13](13-vc-status-and-revocation.md) | IETF Token Status List [S06] | รวมแล้ว (S8, MASA-129 → MASA-135 → แยกบทที่ MASA-150) | +| CHG-08 | Definitions | [§2](02-definitions.md) | คำศัพท์จาก [S03]–[S07] | รวมแล้ว (S3, MASA-124); นิยามเดิมคงอยู่ เพิ่มข้อ 2.23–2.42 | +| CHG-09 | CDN/cache | [§9.3 และ §9.9](09-trust-model-3.md) | Signed-object security model [S08] | รวมแล้ว | +| CHG-10 | Privacy/authorization | [§8](08-implementation-guidelines.md) | OID4VP [S04] | รวมแล้ว; ผ่าน QA gate อัตโนมัติ (traceability/RFC2119/mermaid/link/README/KNOWLEDGE.yaml) — ดู ข.4.1 | +| CHG-11 | แทนที่เนื้อหา §5 ทั้งบทด้วยมาตรฐานเวอร์ชัน 2.0 | [§5](05-standards-and-compliance.md) | OID4VCI 1.0 Final [10], OID4VP 1.0 Final [11], SD-JWT VC [12] | รวมแล้ว (MASA-169 เพิ่มหมายเหตุ → MASA-170 แทนที่เต็มรูปแบบ → MASA-170 แก้ข้อความ W3C VC Data Model v1.1 ตกค้าง) — ดู ข.3.6, ข.3.7, ข.3.8 | + +#### ข.4.1 QA gate ล่าสุด + +- รัน `scripts/qa/run_all.sh` วันที่ 4 สิงหาคม 2569 เวลา 08:51 UTC — ผลลัพธ์ ✅ PASS (Critical 0, Major 0, Minor 7, Suggestion 0) +- ข้อค้นพบระดับ Minor ที่เหลือเป็นสำนวนภาษาไทยในเนื้อหาฐานเวอร์ชัน 1.1 (§5 และ §6) ซึ่งคงไว้ตรงตามต้นฉบับ PDF เพื่อรักษาความตรง 1:1 กับต้นฉบับ จึงไม่ปรับถ้อยคำในส่วนนี้ ไม่กระทบความถูกต้องเชิงเทคนิค +- `link_check`, `mermaid_syntax_check`, `traceability_check`, `readme_sync_check`, `knowledge_yaml_sync_check` ผ่านทั้งหมดโดยไม่มีข้อค้นพบ + +> **ข้อเท็จจริง:** ตารางนี้ระบุสถานะไฟล์ใน repository ณ วันที่จัดทำ การรับรอง conformance ของระบบจริงต้องตรวจจากผลทดสอบที่เกี่ยวข้อง + +### ข.5 การจัดประเภทข้อความ + +#### ข้อเท็จจริง + +- OID4VCI 1.0 และ OID4VP 1.0 เป็น Final Specification ของ OpenID Foundation [S03] [S04] +- W3C DID Core 1.0 เป็น W3C Recommendation ลงวันที่ 19 กรกฎาคม 2022 [S05] +- EUDI ARF มีเอกสารเวอร์ชัน 2.9.0 ที่ใช้คำว่า Wallet Unit Attestation [S07] +- IETF Token Status List ยังเป็น Internet-Draft ณ วันที่เข้าถึง จึงอาจเปลี่ยนแปลงก่อนเป็น RFC [S06] + +#### ข้อวิเคราะห์ + +- Trust Model 3 ลดการพึ่ง CA สำหรับ entity trust ภายในประเทศ แต่ยังต้องมี trust anchor, governance, audit และ revocation ที่ตรวจสอบได้ +- การรองรับ protocol เดียวกันไม่เพียงพอต่อ cross-border trust หาก assurance, liability และ trusted-list semantics ไม่ตรงกัน +- อายุ cache ที่ยาวขึ้นเพิ่ม availability แต่ทำให้ช่วงเวลาที่ระบบอาจไม่เห็นการเพิกถอนยาวขึ้น + +#### สมมติฐานและช่องว่าง + +- สมมติให้ สพธอ. เป็นผู้กำกับดูแลและลงลายมือชื่อ Trusted List; รูปแบบอำนาจตามกฎหมายและ operating model ต้องได้รับมติแยกต่างหาก +- ค่า TTL, cryptoperiod, overlap, fail-open/fail-closed และ assurance mapping ยังต้องได้รับอนุมัติจากผู้มีอำนาจกำกับ +- เนื้อหาจาก S2–S8 รวมเข้า canonical tree ครบแล้ว ณ วันที่ตรวจปรับปรุงนี้ (4 สิงหาคม 2569); ตาราง traceability ใน §ข.4 ปรับให้ตรงกับสถานะปัจจุบัน อย่างไรก็ตาม MASA-131 (QA Gate ก่อนเผยแพร่จริง) และ MASA-132 (Publish/build Docusaurus + เปิด MR) ยังอยู่ระหว่างดำเนินการ — เอกสารนี้จึงยังเป็นสถานะ **Draft for approval** จนกว่าทั้งสอง milestone จะปิด + +### ข.6 แนวทางย้ายจากเวอร์ชัน 1.1 + +1. ผู้ดูแลเอกสาร MUST เปลี่ยนคำ WTE เป็น WUA ในข้อกำหนดที่มีผลบังคับ และคง WTE ไว้เฉพาะข้อความเชิงประวัติ +2. ผู้ให้บริการ MUST จัดทำรายการ trust checkpoints ของ OID4VCI และ OID4VP ให้สอดคล้องกับ §9 +3. ผู้ให้บริการ MUST เพิ่ม key lifecycle และ credential status policy ก่อนประกาศ conformance กับเวอร์ชัน 2.0 +4. ผู้ให้บริการ SHOULD ตรวจ interoperability กับ profile คู่เชื่อมต่อโดยใช้ test evidence ที่ตรวจสอบย้อนกลับได้ +5. ผู้ดูแลเอกสาร MUST ปิดรายการ “รอรวม” ใน §ข.4 และ QA ลิงก์ Mermaid และ reference ก่อนเผยแพร่ + +### ข.7 แหล่งอ้างอิง + +- [S01] IETF, **RFC 2119 — Key words for use in RFCs to Indicate Requirement Levels**, March 1997, https://www.rfc-editor.org/rfc/rfc2119 — เข้าถึง 4 สิงหาคม 2569 +- [S02] IETF, **RFC 8174 — Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words**, May 2017, https://www.rfc-editor.org/rfc/rfc8174 — เข้าถึง 4 สิงหาคม 2569 +- [S03] OpenID Foundation, **OpenID for Verifiable Credential Issuance 1.0**, Final, 16 September 2025, https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html — เข้าถึง 4 สิงหาคม 2569 +- [S04] OpenID Foundation, **OpenID for Verifiable Presentations 1.0**, Final, 9 July 2025, https://openid.net/specs/openid-4-verifiable-presentations-1_0.html — เข้าถึง 4 สิงหาคม 2569 +- [S05] W3C, **Decentralized Identifiers (DIDs) v1.0**, W3C Recommendation, 19 July 2022, https://www.w3.org/TR/did-core/ — เข้าถึง 4 สิงหาคม 2569 +- [S06] IETF OAuth Working Group, **Token Status List**, Internet-Draft (current draft at URL), https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ — เข้าถึง 4 สิงหาคม 2569 +- [S07] European Commission, **European Digital Identity Wallet Architecture and Reference Framework**, version 2.9.0, https://eudi.dev/2.9.0/main/ — เข้าถึง 4 สิงหาคม 2569 +- [S08] IETF, **RFC 7515 — JSON Web Signature (JWS)**, May 2015, https://www.rfc-editor.org/rfc/rfc7515 — เข้าถึง 4 สิงหาคม 2569 +- [S09] NIST, **SP 800-57 Part 1 Rev.5 — Recommendation for Key Management: Part 1 — General**, May 2020, https://csrc.nist.gov/pubs/sp/800/57/pt1/r5/final — เข้าถึง 4 สิงหาคม 2569 +- [S10] คณะทำงาน VC, **Trusted List 3 Models Industry Survey**, repository revision ณ 4 สิงหาคม 2569 — [research/37-trustlist-3models-industry-survey.md](../research/37-trustlist-3models-industry-survey.md) — เข้าถึง 4 สิงหาคม 2569 (ยืนยันแล้วว่าไฟล์อยู่ใน branch `docs/arf-v2.0` / `origin/main` ปัจจุบัน) + +--- +*/} + +**การนำทาง:** [⬅️ บทที่ 14 — ความมั่นคงปลอดภัย: Threat Model และ Conformance](14-security-threat-model-and-conformance.md) · [⬆️ สารบัญ ARF](README.md) · [บทที่ 16 — บรรณานุกรม ➡️](16-bibliography.md) diff --git a/docs/thai-vc-arf/16-bibliography.md b/docs/thai-vc-arf/16-bibliography.md new file mode 100644 index 0000000..5ae865a --- /dev/null +++ b/docs/thai-vc-arf/16-bibliography.md @@ -0,0 +1,55 @@ +--- +description: "16. บรรณานุกรม — Thai VC ARF 2.0 DRAFT 0" +--- + +# 16. บรรณานุกรม + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — เอกสารแปลงจาก PDF ต้นฉบับของ สพธอ. (Thai VC ARF v1.1, มกราคม 2568) และเพิ่มรายการอ้างอิงที่ใช้ในบทที่ 8 (Implementation Guidelines) ฉบับปรับปรุง MASA-126 +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย +> **เอกสารที่เกี่ยวข้อง:** [ดัชนีเอกสาร ARF](README.md) +*/} + +--- + +บทนี้รวบรวมรายการอ้างอิงทั้งหมดที่ใช้ในรายงานฉบับนี้ ประกอบด้วยแหล่งอ้างอิงจากเอกสารต้นฉบับเวอร์ชัน 1.1 และแหล่งอ้างอิงเพิ่มเติมที่ใช้ในบทที่ 8 ว่าด้วย Implementation Guidelines + +{/* METADATA (agent-only — not rendered to readers) +> **หมายเหตุขอบเขตของบทนี้:** บทที่ 16 เป็นบรรณานุกรมรวมหลัก (master bibliography) แบบ superset ของทั้งรายงาน โดยรวบรวมแหล่งอ้างอิงที่รายงานฉบับนี้ใช้ทั้งเล่มไว้ในที่เดียว บทที่แปลงจาก PDF ต้นฉบับเวอร์ชัน 1.1 คือบทที่ 1 ถึงบทที่ 8 อ้างอิงรายการในบทนี้ด้วยเลขรวม (global) ในรูปแบบ `[n]` ตามลำดับที่ 1 ถึง 22 ส่วนบทเพิ่มเติมฉบับปรับปรุง (v2.0) ได้แก่ บทที่ 6.5, 7, 9 ถึง 15 มีรายการอ้างอิงเฉพาะบทของตนเองที่ใช้ prefix กำกับ เช่น `[D*]` (บทที่ 2), `[G*]` (บทที่ 6.5), `[R*]` (บทที่ 7), `[T*]` (บทที่ 9), `[S*]` (บทที่ 15) และ `[REF-*]` (บทที่ 8) เพื่อมิให้เลขซ้ำชนกับเลขรวมในบทนี้ ดังนั้น รายการในบทนี้ที่ไม่มีเลข `[n]` แบบเปล่ากำกับ เป็นแหล่งอ้างอิงหลักที่ตั้งใจคงไว้สำหรับทั้งรายงาน ทั้งนี้ การตรวจความสอดคล้องสองทางของการอ้างอิงตาม AGENTS.md ให้ดำเนินการภายในขอบเขตของแต่ละรายการอ้างอิง (บทนี้สำหรับเลขรวม และรายการเฉพาะบทสำหรับเลขที่มี prefix) +*/} + +## A. แหล่งอ้างอิงจาก PDF ต้นฉบับ v1.1 (มกราคม 2568) + +- [1] European Commission, "The European Digital Identity Wallet Architecture and Reference Framework Version 1.0.0", January 2023. +- [2] ข้อเสนอแนะมาตรฐานด้านเทคโนโลยีสารสนเทศและการสื่อสารที่จำเป็นต่อธุรกรรมทางอิเล็กทรอนิกส์ ว่าด้วยโครงสร้างข้อมูลของเอกสารรับรองและเอกสารสำแดง เลขที่ ขมธอ. 24-2563, เวอร์ชัน 1.0. +- [3] ข้อเสนอแนะมาตรฐานด้านเทคโนโลยีสารสนเทศและการสื่อสารที่จำเป็นต่อธุรกรรมทางอิเล็กทรอนิกส์ ว่าด้วยแนวทางการลงลายมือชื่ออิเล็กทรอนิกส์ เลขที่ ขมธอ. 23-2563, เวอร์ชัน 1.0. +- [4] W3C, "Decentralized Identifiers (DIDs) v1.0", 19 July 2022. +- [5] W3C, "Verifiable Credentials Data Model v1.1", 03 March 2022. +- [6] ข้อเสนอแนะมาตรฐานด้านเทคโนโลยีสารสนเทศและการสื่อสารที่จำเป็นต่อธุรกรรมทางอิเล็กทรอนิกส์ ว่าด้วยการพิสูจน์และยืนยันตัวตนทางดิจิทัล – ข้อกำหนดของการยืนยันตัวตน เลขที่ ขมธอ. 20-2566, เวอร์ชัน 3.0. +- [7] ข้อเสนอแนะมาตรฐานด้านเทคโนโลยีสารสนเทศและการสื่อสารที่จำเป็นต่อธุรกรรมทางอิเล็กทรอนิกส์ ว่าด้วยการพิสูจน์และยืนยันตัวตนทางดิจิทัล – ข้อกำหนดของการพิสูจน์ตัวตน เลขที่ ขมธอ. 19-2566, เวอร์ชัน 3.0. +- [8] OpenID Foundation, "OpenID for Verifiable Presentations", 30 December 2022. +- [9] OpenID Foundation, "OpenID for Verifiable Credential Issuance", 30 December 2022. + +## B. แหล่งอ้างอิงเพิ่มเติม (MASA-126) + +- [10] OpenID Foundation, "OpenID for Verifiable Credential Issuance 1.0 Final", 17 September 2025. — https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html +- [11] OpenID Foundation, "OpenID for Verifiable Presentations 1.0 Final", 15 July 2025. — https://openid.net/specs/openid-4-verifiable-presentations-1_0.html +- [12] IETF, “SD-JWT-based Verifiable Digital Credentials (SD-JWT VC),” draft-ietf-oauth-sd-jwt-vc-19, 31 August 2026 (Internet-Draft; submitted to IESG, Waiting for AD Go-Ahead as accessed 1 October 2026). — https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/ +- [13] IETF, “Token Status List (TSL),” draft-ietf-oauth-status-list-21, 21 June 2026 (Internet-Draft; RFC Editor status: Awaiting First editor as accessed 1 October 2026). — https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/ +- [14] European Commission, "EUDI Architecture and Reference Framework v2.9.0", 2025. — https://eudi.dev/2.9.0/main/ +- [15] IETF RFC 8032, "Edwards-Curve Digital Signature Algorithm (EdDSA)", January 2017. +- [16] Decentralized Identity Foundation, "Universal Resolver", 2024. — https://github.com/decentralized-identity/universal-resolver +- [17] IETF RFC 9449, "OAuth 2.0 Demonstrating Proof-of-Possession (DPoP)", September 2023. +- [18] IETF RFC 7636, "Proof Key for Code Exchange (PKCE)", September 2015. +- [19] (EU) CIR 2024/2979, "Implementing Regulation on Wallet Unit Attestation", 2024. +- [20] Thailand, พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562, ราชกิจจานุเบกษา, 27 พฤษภาคม 2562. +- [21] Thailand, พ.ร.บ. ว่าด้วยธุรกรรมทางอิเล็กทรอนิกส์ พ.ศ. 2544, ราชกิจจานุเบกษา, 4 ธันวาคม 2544 (แก้ไขเพิ่มเติม พ.ศ. 2551 และ 2562). +- [22] สพธอ., เอกสารร่าง "trust-flow-minimal-explained.md" และ "trust-framework-template.md" ภายใต้โครงการ Trusted List public doc (MASA-126), 2569. + +> **หมายเหตุ:** การอ้างอิงแบบ `[n]` ในบทที่ 8 ใช้ prefix `REF-` เพื่อแยกระหว่างรายการบรรณานุกรมของ PDF ต้นฉบับ (เช่น `[1]`, `[8]`) และแหล่งอ้างอิงเพิ่มเติม (เช่น `REF-OID4VCI` → `[10]`, `REF-ARF` → `[14]`) ผู้อ่านสามารถค้นหาเลข `[n]` ในรายการข้างต้นเพื่อตรวจสอบการอ้างอิงสองทางตาม AGENTS.md §Output & Writing + +{/* pdf page 55 */} + +--- + +**การนำทาง:** [⬅️ บทที่ 15 — ภาคผนวก](15-appendix.md) · [⬆️ สารบัญ ARF](README.md) diff --git a/docs/thai-vc-arf/README.md b/docs/thai-vc-arf/README.md new file mode 100644 index 0000000..52c836d --- /dev/null +++ b/docs/thai-vc-arf/README.md @@ -0,0 +1,63 @@ +--- +description: "Thai VC ARF — 2.0 DRAFT 0 — Thai VC ARF 2.0 DRAFT 0" +--- + +# Thai VC ARF — กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัล (เวอร์ชัน 1.1 + ส่วนขยาย 2.1) + +{/* METADATA (agent-only — not rendered to readers) +> **สถานะ:** 📄 Reference — บทที่ 1–8 แปลงจาก PDF ต้นฉบับ Thai VC ARF v1.1 ของ สพธอ. (มกราคม 2568) และเพิ่มส่วนขยายเวอร์ชัน 2.1 (บทที่ 6.5, 8.1, 8.2, 9–16) +> **แหล่งข้อมูล:** [Thai-VC-ARF-v1-1.pdf](https://www.etda.or.th/getattachment/Our-Service/Digital-Trusted-services-Infrastructure/VC-and-Digital-Document-Wallet/Information/รายงานทางเทคนค-Thai-VC-ARF-v1-1.pdf) — รายงานทางเทคนิค: THAI VC ARCHITECTURE AND REFERENCE FRAMEWORK เวอร์ชัน 1.1 (มกราคม 2568) +> **เอกสารที่เกี่ยวข้อง:** [research/34 — Trusted List domestic + cross-border](../research/34-trustlist-domestic-and-crossborder.md) · [governance/VCGF](../governance/README.md) +*/} + +--- + +## เอกสารนี้คืออะไร + +นี่คือ **รายงานทางเทคนิค Thai VC ARF** ของ สพธอ. ในรูปแบบ Markdown เพื่อใช้บนเว็บ (Docusaurus) +เอกสารต้นฉบับวางกรอบการทำงานร่วมกัน (interoperability) ของเอกสารรับรองดิจิทัล (VC) และเอกสารสำแดงดิจิทัล (VP) สำหรับประเทศไทย + +บทที่ 1–8 มีฐานจากต้นฉบับเวอร์ชัน 1.1 แต่มีการปรับปรุงมาตรฐานและแนวทางปฏิบัติในบางบท; บทที่ 6.5 และบทที่ 9–14 เป็นส่วนขยายหลักเวอร์ชัน 2.0 (บทที่ 6.5 เป็นบทเกริ่นนำเรื่องกลไกการสร้างความน่าเชื่อถือ แทรกก่อนบทที่ 7) ส่วนบทที่ 8.1, 8.2 และ 12.1 เพิ่มเติมในเวอร์ชัน 2.1 และบทที่ 8 มี revision ย่อยตาม metadata ของบท แต่ละบทอยู่คนละไฟล์เพื่อให้อ่านง่ายบนเว็บ + +{/* METADATA (agent-only — not rendered to readers) +## หมายเหตุการแปลงและการปรับปรุง + +- เนื้อความไทยและศัพท์เทคนิคภาษาอังกฤษในบทที่ 1–8 เก็บไว้ตรงตามต้นฉบับ ไม่ได้เขียนใหม่ (บทที่ 5 มีข้อยกเว้นเป็นหมายเหตุกำกับเพิ่มเติม — ดูรายการถัดไป) +- **บทที่ 5 ปรับปรุงมาตรฐานในรอบ v2.0 (MASA-170)** — ระบุการรองรับ W3C VCDM v1.1 สำหรับเอกสารเดิม และ SD-JWT VC (`dc+sd-jwt`) สำหรับโปรไฟล์เอกสารใหม่ พร้อม OID4VCI 1.0 Final และ OID4VP 1.0 Final (DCQL); รายละเอียดทางเทคนิคอยู่ที่บทที่ 8/8.1/8.2 +- รูปทั้ง 28 รูปถูกดึงออกมาเป็นไฟล์ภาพและฝังไว้ในตำแหน่งเดิม (ดูโฟลเดอร์ `images/`) +- คอมเมนต์ `` บอกเลขหน้าของ PDF ต้นฉบับไว้เพื่ออ้างอิง +- **บทที่ 6.5 (กลไกการสร้างความน่าเชื่อถือ — Trusted List และแบบจำลองความน่าเชื่อถือ 3 รูปแบบ) และบทที่ 9–14 (Trust Model 3, WUA, Cryptographic Suites, Key Management/Trusted List Deployment, VC Status/Revocation และ Threat Model/Conformance) เป็นส่วนขยายเวอร์ชัน 2.0** ที่คณะทำงานเพิ่ม ไม่ได้อยู่ใน PDF ต้นฉบับ +- **บทที่ 8.1 (OID4VCI Full Flow) และ 8.2 (OID4VP Full Flow) เป็นส่วนขยายเวอร์ชัน 2.1** ที่เพิ่มเติมภายใต้ MASA-158 — รวมแผนภาพฉบับสมบูรณ์ รายละเอียด field-by-field และนโยบาย DPoP จาก trust-guide เข้าเป็นภาคผนวกของบทที่ 8 +- **บันทึกการเปลี่ยนแปลงจากเวอร์ชัน 1.1 เป็น 2.0** อยู่ใน [ภาคผนวก ข](15-appendix.md) +- **[บทที่ 0](00-minimal-interoperability-reference.md) เป็นบทสรุปหนึ่งหน้าที่จัดทำเพิ่มเติม** เพื่อให้ระบบของคู่ภาคีใช้ตรวจสอบความสอดคล้องกัน (interoperability) โดยรายละเอียดโครงสร้าง Trusted List ให้ยึด [research/42](../research/42-trusted-list-lote-jwt-format.md) และบทที่ 0 เป็นแหล่งข้อมูลอ้างอิงหลัก +*/} + +## สารบัญ + +| บท | หัวข้อ | ไฟล์ | +|---|--------|------| +| 0 | **ชุดมาตรฐานและเทคโนโลยีของเอกสารรับรองดิจิทัลของประเทศไทย — สรุปหนึ่งหน้า** (บทสรุปสำหรับระบบของคู่ภาคี) | [00-minimal-interoperability-reference.md](00-minimal-interoperability-reference.md) | +| 1 | ขอบข่าย | [01-scope.md](01-scope.md) | +| 2 | บทนิยาม | [02-definitions.md](02-definitions.md) | +| 3 | ภาพรวมการใช้งาน | [03-usage-overview.md](03-usage-overview.md) | +| 4 | แนวคิดและองค์ประกอบที่ส่งเสริมการทำงานร่วมกัน | [04-interoperability-concepts.md](04-interoperability-concepts.md) | +| 5 | มาตรฐานและข้อปฏิบัติสำหรับ VC และ VP | [05-standards-and-compliance.md](05-standards-and-compliance.md) | +| 6 | Decentralized Identifiers Methodologies | [06-did-methodologies.md](06-did-methodologies.md) | +| 6.5 | **กลไกการสร้างความน่าเชื่อถือ (Trust-Building Mechanism) — Trusted List และแบบจำลองความน่าเชื่อถือ 3 รูปแบบ** (ส่วนขยาย v2.0) | [06.5-trust-building-mechanism.md](06.5-trust-building-mechanism.md) | +| 7 | การทำงานร่วมกันข้ามระบบนิเวศและผลของ Trusted List | [07-cross-ecosystem-interoperability.md](07-cross-ecosystem-interoperability.md) | +| 8 | Implementation Guidelines (OID4VCI / OID4VP) | [08-implementation-guidelines.md](08-implementation-guidelines.md) | +| 8.1 | **ภาคผนวกบทที่ 8 — Full Flow OID4VCI ทางเทคนิคโดยละเอียด** (ส่วนขยาย v2.1 — MASA-158, STEP 1.5 → 2.5) | [08.1-issuance-full-flow-detail.md](08.1-issuance-full-flow-detail.md) | +| 8.2 | **ภาคผนวกบทที่ 8 — Full Flow OID4VP ทางเทคนิคโดยละเอียด** (ส่วนขยาย v2.1 — MASA-158) | [08.2-oid4vp-full-flow-detail.md](08.2-oid4vp-full-flow-detail.md) | +| 9 | **Trust Model 3 — Trusted List + DID** (ส่วนขยาย v2.0) | [09-trust-model-3.md](09-trust-model-3.md) | +| 10 | **Wallet Unit Attestation (WUA)** (ส่วนขยาย v2.0) | [10-wallet-unit-attestation.md](10-wallet-unit-attestation.md) | +| 11 | **Cryptographic Suites** (ส่วนขยาย v2.0) | [11-cryptographic-suites.md](11-cryptographic-suites.md) | +| 12 | **Key Management, Key Rotation และ Trusted List Deployment** (ส่วนขยาย v2.0) | [12-key-management-and-trustlist-deployment.md](12-key-management-and-trustlist-deployment.md) | +| 12.1 | **ข้อกำหนดการเผยแพร่ Trusted List — JWT แบบ LoTE จำนวน 3 ไฟล์ จุดเผยแพร่ โครงสร้างข้อมูล และตัวอย่าง** (ส่วนขยาย v2.1) | [12.1-trust-list-publication-profile.md](12.1-trust-list-publication-profile.md) | +| 13 | **VC Status และ Revocation** (ส่วนขยาย v2.0) | [13-vc-status-and-revocation.md](13-vc-status-and-revocation.md) | +| 14 | **ความมั่นคงปลอดภัย: Threat Model และรายการตรวจ Conformance** (ส่วนขยาย v2.0) | [14-security-threat-model-and-conformance.md](14-security-threat-model-and-conformance.md) | +| 15 | ภาคผนวก (ก. การทำงานร่วมกัน · ข. บันทึกการเปลี่ยนแปลง v1.1 → v2.0) | [15-appendix.md](15-appendix.md) | +| 16 | บรรณานุกรม | [16-bibliography.md](16-bibliography.md) | + +--- + +> 📌 กลับไปที่ [th/](../README.md) | [README.md หลัก](../../README.md) diff --git a/docs/thai-vc-arf/_category_.json b/docs/thai-vc-arf/_category_.json new file mode 100644 index 0000000..8e0328c --- /dev/null +++ b/docs/thai-vc-arf/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Thai VC ARF — 2.0 DRAFT 0", + "position": 0, + "link": { + "type": "generated-index", + "slug": "/", + "title": "Thai VC ARF — 2.0 DRAFT 0", + "description": "กรอบแนวทางการทำงานร่วมกันของเอกสารรับรองดิจิทัลสำหรับประเทศไทย ฉบับร่าง 2.0 DRAFT 0" + } +} diff --git a/docs/thai-vc-arf/images/p10-0.png b/docs/thai-vc-arf/images/p10-0.png new file mode 100644 index 0000000..168fc55 Binary files /dev/null and b/docs/thai-vc-arf/images/p10-0.png differ diff --git a/docs/thai-vc-arf/images/p12-0.png b/docs/thai-vc-arf/images/p12-0.png new file mode 100644 index 0000000..a665a81 Binary files /dev/null and b/docs/thai-vc-arf/images/p12-0.png differ diff --git a/docs/thai-vc-arf/images/p15-0.png b/docs/thai-vc-arf/images/p15-0.png new file mode 100644 index 0000000..095b796 Binary files /dev/null and b/docs/thai-vc-arf/images/p15-0.png differ diff --git a/docs/thai-vc-arf/images/p15-1.png b/docs/thai-vc-arf/images/p15-1.png new file mode 100644 index 0000000..9c16e5d Binary files /dev/null and b/docs/thai-vc-arf/images/p15-1.png differ diff --git a/docs/thai-vc-arf/images/p16-0.png b/docs/thai-vc-arf/images/p16-0.png new file mode 100644 index 0000000..52953ea Binary files /dev/null and b/docs/thai-vc-arf/images/p16-0.png differ diff --git a/docs/thai-vc-arf/images/p19-0.png b/docs/thai-vc-arf/images/p19-0.png new file mode 100644 index 0000000..90b5ed6 Binary files /dev/null and b/docs/thai-vc-arf/images/p19-0.png differ diff --git a/docs/thai-vc-arf/images/p22-0.png b/docs/thai-vc-arf/images/p22-0.png new file mode 100644 index 0000000..d226999 Binary files /dev/null and b/docs/thai-vc-arf/images/p22-0.png differ diff --git a/docs/thai-vc-arf/images/p22-1.png b/docs/thai-vc-arf/images/p22-1.png new file mode 100644 index 0000000..7e689f5 Binary files /dev/null and b/docs/thai-vc-arf/images/p22-1.png differ diff --git a/docs/thai-vc-arf/images/p29-0.png b/docs/thai-vc-arf/images/p29-0.png new file mode 100644 index 0000000..6fbcb47 Binary files /dev/null and b/docs/thai-vc-arf/images/p29-0.png differ diff --git a/docs/thai-vc-arf/images/p31-0.png b/docs/thai-vc-arf/images/p31-0.png new file mode 100644 index 0000000..dd421f0 Binary files /dev/null and b/docs/thai-vc-arf/images/p31-0.png differ diff --git a/docs/thai-vc-arf/images/p34-0.png b/docs/thai-vc-arf/images/p34-0.png new file mode 100644 index 0000000..490882e Binary files /dev/null and b/docs/thai-vc-arf/images/p34-0.png differ diff --git a/docs/thai-vc-arf/images/p35-0.png b/docs/thai-vc-arf/images/p35-0.png new file mode 100644 index 0000000..49c64ba Binary files /dev/null and b/docs/thai-vc-arf/images/p35-0.png differ diff --git a/docs/thai-vc-arf/images/p36-0.png b/docs/thai-vc-arf/images/p36-0.png new file mode 100644 index 0000000..7567d86 Binary files /dev/null and b/docs/thai-vc-arf/images/p36-0.png differ diff --git a/docs/thai-vc-arf/images/p38-0.png b/docs/thai-vc-arf/images/p38-0.png new file mode 100644 index 0000000..4f14203 Binary files /dev/null and b/docs/thai-vc-arf/images/p38-0.png differ diff --git a/docs/thai-vc-arf/images/p39-0.png b/docs/thai-vc-arf/images/p39-0.png new file mode 100644 index 0000000..4f18de6 Binary files /dev/null and b/docs/thai-vc-arf/images/p39-0.png differ diff --git a/docs/thai-vc-arf/images/p41-0.png b/docs/thai-vc-arf/images/p41-0.png new file mode 100644 index 0000000..67fb24d Binary files /dev/null and b/docs/thai-vc-arf/images/p41-0.png differ diff --git a/docs/thai-vc-arf/images/p44-0.png b/docs/thai-vc-arf/images/p44-0.png new file mode 100644 index 0000000..3e67427 Binary files /dev/null and b/docs/thai-vc-arf/images/p44-0.png differ diff --git a/docs/thai-vc-arf/images/p44-1.png b/docs/thai-vc-arf/images/p44-1.png new file mode 100644 index 0000000..8f73d34 Binary files /dev/null and b/docs/thai-vc-arf/images/p44-1.png differ diff --git a/docs/thai-vc-arf/images/p45-0.png b/docs/thai-vc-arf/images/p45-0.png new file mode 100644 index 0000000..3a6d9f3 Binary files /dev/null and b/docs/thai-vc-arf/images/p45-0.png differ diff --git a/docs/thai-vc-arf/images/p45-1.png b/docs/thai-vc-arf/images/p45-1.png new file mode 100644 index 0000000..3fe7c63 Binary files /dev/null and b/docs/thai-vc-arf/images/p45-1.png differ diff --git a/docs/thai-vc-arf/images/p46-0.png b/docs/thai-vc-arf/images/p46-0.png new file mode 100644 index 0000000..1a3d050 Binary files /dev/null and b/docs/thai-vc-arf/images/p46-0.png differ diff --git a/docs/thai-vc-arf/images/p47-0.png b/docs/thai-vc-arf/images/p47-0.png new file mode 100644 index 0000000..a2f927b Binary files /dev/null and b/docs/thai-vc-arf/images/p47-0.png differ diff --git a/docs/thai-vc-arf/images/p47-1.png b/docs/thai-vc-arf/images/p47-1.png new file mode 100644 index 0000000..c15fd36 Binary files /dev/null and b/docs/thai-vc-arf/images/p47-1.png differ diff --git a/docs/thai-vc-arf/images/p48-0.png b/docs/thai-vc-arf/images/p48-0.png new file mode 100644 index 0000000..4129b6a Binary files /dev/null and b/docs/thai-vc-arf/images/p48-0.png differ diff --git a/docs/thai-vc-arf/images/p48-1.png b/docs/thai-vc-arf/images/p48-1.png new file mode 100644 index 0000000..b660e03 Binary files /dev/null and b/docs/thai-vc-arf/images/p48-1.png differ diff --git a/docs/thai-vc-arf/images/p49-0.png b/docs/thai-vc-arf/images/p49-0.png new file mode 100644 index 0000000..1e1eeac Binary files /dev/null and b/docs/thai-vc-arf/images/p49-0.png differ diff --git a/docs/thai-vc-arf/images/p53-0.png b/docs/thai-vc-arf/images/p53-0.png new file mode 100644 index 0000000..0e5281c Binary files /dev/null and b/docs/thai-vc-arf/images/p53-0.png differ diff --git a/docs/thai-vc-arf/images/p9-0.png b/docs/thai-vc-arf/images/p9-0.png new file mode 100644 index 0000000..bc44666 Binary files /dev/null and b/docs/thai-vc-arf/images/p9-0.png differ diff --git a/docs/trust-guide/01-concepts-and-roles/01-glossary.md b/docs/trust-guide/01-concepts-and-roles/01-glossary.md deleted file mode 100644 index f3eb759..0000000 --- a/docs/trust-guide/01-concepts-and-roles/01-glossary.md +++ /dev/null @@ -1,33 +0,0 @@ -# 1.1 คำศัพท์และนิยาม - -## 1.1 คำศัพท์และนิยาม - -ก่อนอ่านเนื้อหาทางเทคนิคในบทถัดไป ควรทำความรู้จักคำศัพท์หลักที่ใช้ตลอดทั้งคู่มือนี้ก่อน ตารางด้านล่างรวมคำศัพท์ที่พบบ่อยที่สุด เรียงตามลำดับที่ผู้อ่านจะเจอในเนื้อหา - -| คำศัพท์ | ความหมาย | -|---------|----------| -| **EUDI** | กระเป๋าเอกสารดิจิทัลของสหภาพยุโรป (European Digital Identity) ใช้เก็บและแสดงเอกสารดิจิทัล | -| **EUDI ARF** | เอกสารกรอบสถาปัตยกรรมของ EUDI (Architecture and Reference Framework) บอกว่าระบบต้องทำงานอย่างไร | -| **OID4VCI** | protocol สำหรับ "ออก" credential จาก Issuer ไปยัง Wallet (OpenID for Verifiable Credential Issuance) | -| **OID4VP** | protocol สำหรับ "แสดง" credential จาก Wallet ไปยัง Verifier (OpenID for Verifiable Presentations) | -| **Issuer** | ผู้ออก credential เซ็นและส่งให้ Holder — **หน่วยงานรัฐและองค์กรเอกชนที่ผ่านเกณฑ์การตรวจสอบของ ETDA สมัครเป็น Issuer ได้ทั้งคู่** เช่น หน่วยงานรัฐที่ออกบัตรประชาชนดิจิทัล หรือธนาคารเอกชนที่ออก credential ยืนยันบัญชี (ดูตัวอย่าง BBL ใน `05-trust-infrastructure/02-tl-json-examples.md`) — ดูขั้นตอนสมัครที่ [§ 7.5](../07-governance-and-conformance/01-conformance-governance-versioning.md#75-ขั้นตอนสมัครเป็น-issuer) | -| **Holder** | เจ้าของ credential เก็บไว้ใน Wallet และเป็นคนเลือกแสดง | -| **Wallet** | แอปที่ Holder ใช้เก็บ credential และแสดงให้ Verifier | -| **Verifier** | ผู้ตรวจ credential หรือเรียกว่า Relying Party คนที่ขอดูและเชื่อข้อมูล | -| **Credential / VC** | เอกสารดิจิทัลที่ Issuer เซ็น (Verifiable Credential) พิสูจน์ได้ว่าจริงและไม่ถูกแก้ | -| **Status List** | รายการสถานะของ credential บอกว่ายังใช้ได้ ถูกเพิกถอน หรือถูกระงับ (Token Status List, `statuslist+jwt`) | -| **Schema** | แบบโครงสร้างของ credential บอกว่ามี field อะไรบ้างและความหมายคืออะไร | -| **Metadata** | ข้อมูลกำกับ เช่น Issuer เป็นใคร, credential แบบไหน, ใช้ key อะไรเซ็น | -| **Trustlist** | รายชื่อผู้เข้าร่วมที่ได้รับอนุมัติ เซ็นโดย Governance Authority ใช้บอกว่าใครเชื่อถือได้ | -| **Gateway** | ตัวกลางที่เชื่อมระบบหรือช่วยตรวจ trust ก่อนส่งต่อ request | -| **ETDA** | สพธอ. (Electronic Transactions Development Agency) หน่วยงานกำกับดูแลของไทย วางกฎและอนุมัติผู้เข้าร่วม | -| **Governance Authority** | ผู้วางกฎของ trust framework อนุมัติและถอนสิทธิ์ผู้เข้าร่วม | -| **SD-JWT VC** | รูปแบบ credential ที่เปิดเผยข้อมูลแบบเลือกได้ (selective disclosure) แสดงเฉพาะ field ที่จำเป็น (dc+sd-jwt) | -| **DID** | ตัวระบุแบบกระจายศูนย์ (Decentralized Identifier) ใช้ชี้ตัวตนและหา key สำหรับตรวจลายเซ็น | -| **PKI** | ระบบจัดการ key และใบรับรอง (Public Key Infrastructure) ใช้ออกและตรวจ key | -| **LoA** | ระดับความเชื่อมั่น (Level of Assurance) บอกว่าพิสูจน์ตัวตนเข้มแค่ไหน | -| **TTL** | อายุของข้อมูลใน cache (Time-to-Live) บอกว่าเก็บใช้ได้นานแค่ไหนก่อนต้องดึงใหม่ เช่น Trustlist ที่ดึงมามี TTL 24 ชม. เมื่อหมดอายุต้องดึงฉบับใหม่จาก ETDA | -| **aal** (Authenticator Assurance Level) | ระดับความเข้มของการยืนยันตัวตนผู้ใช้ปลายทาง (ไม่ใช่ระดับความน่าเชื่อถือของ credential แบบ LoA) ใช้ในขั้นตอนตรวจ trust เพื่อยืนยันว่าผู้ใช้ authen ตัวเองเข้มแค่ไหนก่อนดำเนินการ เช่น `"aal": "high"` หมายถึงยืนยันตัวตนแบบ multi-factor ค่าที่พบในเอกสารนี้มี `high` และ `low` **ความสัมพันธ์กับ LoA:** LoA บอกความน่าเชื่อถือของ *credential ที่ออก* ส่วน aal บอกความเข้มของการยืนยันตัวตน *ผู้ใช้ ณ ขณะทำธุรกรรม* — LoA สูง (เช่น 3-4) มักกำหนดให้ต้องมี aal สูง (`high`) ควบคู่กัน แต่ทั้งสองเป็นคนละมิติกัน | - -เมื่อคุ้นกับคำศัพท์ข้างต้นแล้ว บทถัดไป ([1.2 บทบาทและผู้เกี่ยวข้อง](02-roles-and-trust-model.md)) จะอธิบายว่าแต่ละฝ่าย (Issuer, Holder, Wallet, Verifier, ETDA) ใช้คำเหล่านี้ทำหน้าที่อะไรบ้าง - diff --git a/docs/trust-guide/01-concepts-and-roles/02-roles-and-trust-model.md b/docs/trust-guide/01-concepts-and-roles/02-roles-and-trust-model.md deleted file mode 100644 index 9398a42..0000000 --- a/docs/trust-guide/01-concepts-and-roles/02-roles-and-trust-model.md +++ /dev/null @@ -1,154 +0,0 @@ -# 1.2 บทบาทผู้เกี่ยวข้อง และ Trust Model - -ต่อจาก [1.1 คำศัพท์และนิยาม](01-glossary.md) บทนี้อธิบายว่าใครมีบทบาทอะไรในระบบ และแต่ละฝ่ายเชื่อใจกันได้อย่างไร (Trust Model) ซึ่งเป็นหลักการพื้นฐานที่ทุกบทถัดไปอ้างอิงถึง - -## 1.2 บทบาทและผู้เกี่ยวข้อง - -| บทบาท | หน้าที่ | -|-------|--------| -| ETDA(Governance Authority, Trust Service Provider) | วางกฎ อนุมัติผู้เข้าร่วม ดูแลบริการกลาง เช่น list, PKI| -| Issuer | สร้างและเซ็น credential | -| Holder | เก็บและแสดง credential | -| Verifier | ตรวจและเชื่อ credential | - -## 1.3 Trust Model - -Trust Model บอกว่าใครเชื่อใคร และเชื่อเพราะอะไร ในระบบนี้ทุกคนเชื่อจุดเดียวกันคือ ETDA แล้วส่งต่อความเชื่อผ่าน Trustlist - -- **Root of trust:** ETDA คือจุดที่ทุกคนเชื่อ เพราะ ETDA เป็นคนดูแล Trustlist ทุกคนในระบบใช้ Trustlist ของ ETDA ตัดสินว่าใครเชื่อถือได้ -- **Trust chain:** ETDA Registry publish Trustlist ออกมา แต่ละ entity ดึง Trustlist ผ่าน ETDA Gateway มาเก็บเป็น local cache แล้วใช้เช็คว่า entity อื่นอยู่ใน Trustlist หรือไม่ ความเชื่อจึงส่งต่อจาก ETDA → Trustlist → ผู้เข้าร่วมทุกคน -- **การสร้าง trust:** ผู้เข้าร่วมสมัครเข้ามาที่ ETDA จากนั้น ETDA จะตรวจสอบและอนุมัติให้เข้าไปอยู่ใน Trustlist เมื่ออยู่ใน Trustlist แล้ว คนอื่นจึงเชื่อได้ -- **การถอน trust:** ผู้ที่ต้องการเพิกถอน ทำเรื่องมาที่ ETDA จากนั้น ETDA จะตรวจสอบและเพิกถอนออกจาก Trustlist เมื่อออกจาก Trustlist แล้ว คนอื่นจะไม่เชื่ออีกต่อไป -- **การเข้าถึง Trustlist:** ผู้เข้าร่วมดึง Trustlist ผ่าน ETDA API Gateway โดย ETDA จะให้ 1 API key ต่อ 1 Service ของแต่ละ Business - -**จุดที่ผู้เข้าร่วมเข้าสู่ระบบ:** ทุก entity (Issuer, Verifier, Wallet Provider) ต้องสมัครและผ่านการตรวจสอบจาก ETDA ก่อนเข้าสู่ระบบ เมื่อผ่านแล้วจึงถูกเพิ่มเข้า Trustlist และได้ API key ไว้ดึง Trustlist มาทำ local cache - -#### ภาพ 4a — ETDA Registration & Trust List Publishing - -แสดงวิธีที่ entity สมัครเข้าระบบ ผ่านการตรวจสอบ แล้ว ETDA publish Trust List — ไล่ทีละเส้น: - -- **เส้นที่ ①:** Issuer / Verifier / Wallet Provider → ยื่นสมัครเข้าใช้งานบริการ ETDA Trust List ที่ ETDA Registry -- **เส้นที่ ②:** ETDA Registry → Audit — ส่งคำขอไปตรวจสอบ entity ตามข้อกำหนด (เอกสาร, ระบบ, ความปลอดภัย) -- **เส้นที่ ③:** Audit ผ่าน → เพิ่ม entity เข้า Registry + ออก API Key ให้ (ไม่ผ่าน → แจ้งผล ปฏิเสธ) -- **เส้นที่ ④:** ETDA Registry → Publish Trust List (รายชื่อ entity ที่อนุมัติแล้ว พร้อมลายเซ็น ETDA) - -```mermaid -%%{init: {"theme":"redux","flowchart":{"defaultRenderer":"elk","htmlLabels":true,"curve":"linear"}}}%% -flowchart TB - subgraph ETDA_BOX["ETDA"] - direction TB - TL["🛡️ Trust List"] - REG["📋 ETDA Registry"] - AUD{"🔍 Audit -ตรวจตามข้อกำหนด"} - REG -->|"② ส่งคำขอไปตรวจสอบ"| AUD - AUD -->|"③ ผ่าน → เพิ่มเข้า Registry -+ ออก API Key"| REG - AUD -->|"❌ ไม่ผ่าน → ปฏิเสธ"| REJ["แจ้งผล ไม่เข้า Trust List"] - REG -->|"④ Publish Trust List -(เซ็นโดย ETDA)"| TL - end - - J((" ")) - - ISS["🏛️ Issuer"] --> J - VER["🔍 Verifier"] --> J - WP["📱 Wallet Provider"] --> J - - J -->|"① สมัครเข้าใช้งานบริการ -ETDA Trust List"| REG - - style ETDA_BOX fill:#C8E6C9,stroke:#2E7D32 - style TL fill:#BBDEFB,stroke:#1565C0 - style REG fill:#BBDEFB,stroke:#1565C0 - style AUD fill:#FFF3E0,stroke:#E65100 - style REJ fill:#FFCDD2,stroke:#C62828 -``` - -#### ภาพ 4b — Trust List Distribution (การกระจาย Trust List) - -แสดงวิธีที่แต่ละ entity ดึง Trust List ผ่าน ETDA Gateway แล้วเก็บ local cache ไว้ตรวจสอบ - -- **เส้นที่ ①:** Trust List → ETDA Gateway — Publish Trust List ให้ ETDA Gateway พร้อมให้บริการ -- **เส้นที่ ②:** Entity → ETDA Gateway — แต่ละ entity ส่ง request ขอ Trust List (แนบ API Key) -- **เส้นที่ ③:** ETDA Gateway → Entity — ETDA Gateway ส่ง Trust List กลับ -- **เส้นที่ ④:** Entity → Cache — entity เก็บ Trust List ไว้ใน local cache เพื่อใช้ตรวจสอบ - -```mermaid -%%{init: {"theme":"redux","flowchart":{"defaultRenderer":"elk","htmlLabels":true,"curve":"linear"}}}%% -flowchart TB - TL["🛡️ Trust List"] - GW["🔀 ETDA Gateway"] - - TL -->|"① Publish Trust List"| GW - - subgraph RP_ISS["Issuer"] - ISS["🏛️ Issuer"] - C_I[("Trust List Cache")] - end - - subgraph RP_VER["Verifier"] - VER["🔍 Verifier"] - C_V[("Trust List Cache")] - end - - subgraph RP_HOL["Holder"] - HOL["📱 Wallet"] - C_H[("Trust List Cache")] - end - - ISS -->|"② ขอ Trust List + API Key"| GW - VER -->|"② ขอ Trust List + API Key"| GW - HOL -->|"② ขอ Trust List + API Key"| GW - - GW -->|"③ ส่ง Trust List กลับ"| ISS - GW -->|"③ ส่ง Trust List กลับ"| VER - GW -->|"③ ส่ง Trust List กลับ"| HOL - - ISS -->|"④ เก็บ local cache"| C_I - VER -->|"④ เก็บ local cache"| C_V - HOL -->|"④ เก็บ local cache"| C_H - - style TL fill:#C8E6C9,stroke:#2E7D32 - style GW fill:#F8BBD0,stroke:#AD1457 - style C_I fill:#C8E6C9,stroke:#2E7D32 - style C_V fill:#C8E6C9,stroke:#2E7D32 - style C_H fill:#C8E6C9,stroke:#2E7D32 - style RP_ISS fill:#FFF8E1,stroke:#F9A825 - style RP_VER fill:#FFF8E1,stroke:#F9A825 - style RP_HOL fill:#FFF8E1,stroke:#F9A825 -``` - -**Pull บ่อยแค่ไหน + Audit เมื่อไหร่:** - -| การทำงาน | ความถี่ | รายละเอียด | -|----------|---------|-----------| -| **Pull Trust List (ปกติ)** | ตามอายุ cache (TTL — Time-to-Live) — **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ — ตัวอย่างที่ปรากฏในบทที่ 3 เช่น "24 ชั่วโมง" เป็นเพียงค่าสมมติสำหรับสาธิต ไม่ใช่ spec จริง; ค่าจริงจะประกาศโดย ETDA ในเวอร์ชันถัดไป]** | entity ดึงผ่าน ETDA Gateway (แนบ API Key) แล้วเก็บ local cache — เมื่อ cache หมดอายุจึงดึงใหม่ | -| **Pull Trust List (เร่งด่วน)** | ทันทีที่ ETDA แจ้งเปลี่ยนแปลง เช่น มี revocation | ดึงใหม่ก่อน cache หมดอายุ เพื่อไม่เชื่อ entity ที่เพิ่งถูกถอน | -| **Pull ตอน cache ใช้ไม่ได้** | ก่อนตัดสินใจ trust ถ้า cache หมดอายุ/เสียหาย | ห้ามใช้ cache ที่หมดอายุตัดสินใจเรื่อง trust | -| **Audit (ขาเข้า)** | ครั้งเดียว ก่อนเข้า Trustlist | ETDA ตรวจสอบ entity ก่อนอนุมัติเข้า Trust List (ภาพ 4a เส้น ② Registry → Audit) | -| **Audit (ระหว่างใช้)** | ตามรอบที่ ETDA กำหนด [ยังไม่ได้กำหนดค่า] | ETDA ตรวจซ้ำว่า entity ยังปฏิบัติตามข้อกำหนด | -| **Revocation** | เมื่อ audit ไม่ผ่าน หรือ entity ขอถอนตัว | ETDA ถอนออกจาก Trust List → รอบ pull ถัดไป ทุก entity จะไม่เชื่อถืออีก | - -#### ภาพ 4c — Trust List ใช้ตรวจอะไร (Usage Scenarios) - -แสดงว่าแต่ละ entity นำ Trust List ที่ cache ไว้ ไปใช้ตรวจคู่สื่อสารอย่างไร — แบ่งเป็น 2 flow รวม **จุดตรวจที่ 1–6** เรียงตามลำดับเวลา ส่วนผังการตรวจแบบละเอียด (step-by-step พร้อม diagram เต็ม) อยู่ในบทที่อธิบาย flow นั้น ๆ โดยตรง เพื่อไม่ให้ diagram ซ้ำกันหลายที่ — ตารางด้านล่างเป็นดัชนีสรุปจุดตรวจทั้งหมด พร้อมลิงก์ไปดูรายละเอียด: - -- **Flow ออก VC (OID4VCI) — จุดตรวจที่ 1–3:** จุดตรวจที่ 1 (Wallet ตรวจ Issuer ก่อนขอบัตร) ดูที่ [3.2.2 Early Trust Check — Issuer](../03-issuance-flow/02-full-flow/03-setup-and-credential-offer.md) (STEP 1.5); จุดตรวจที่ 2–3 (Issuer ตรวจ Wallet Provider, Wallet ตรวจ Issuer ก่อนบันทึก) ดูผังละเอียดที่ [3.2.5 Trustlist Verification](../03-issuance-flow/02-full-flow/06-verification-details.md) (STEP 5.5, 6.5) -- **Flow แสดง VP (OID4VP) — จุดตรวจที่ 4–6:** ดูภาพรวมที่ [4.1.1 SETUP, สแกน QR, ตรวจ Verifier](../04-presentation-flow/01-overview/02-setup-and-request.md) และรายละเอียดทางเทคนิคทุกขั้นตอนที่ [4.2.1 Full Flow เทคนิคโดยละเอียด](../04-presentation-flow/02-full-flow/02b-full-flow-technical-detail.md) (จุดตรวจสอบที่ 1–4 ในเอกสารนั้น ครอบคลุม Verifier / Wallet Provider / Issuer / สถานะเอกสาร) - -**สรุป: Trust List ถูกใช้ทุกครั้งที่ entity ต้องตัดสินใจเรื่อง trust** - -| จุดตรวจที่ | Flow | Step ใน flow เต็ม | ใครเช็ค | เช็คอะไร | ถ้าไม่ผ่าน | -|:---------:|------|:-----------------:|---------|---------|-----------| -| **1** | ออก VC | 3.1 (§ 3) | Wallet | Issuer ใน Credential Offer อยู่ใน Trust List? | หยุด ไม่ส่ง Transaction Code | -| **2** | ออก VC | 5.5 (§ 3) | Issuer | Wallet Provider อยู่ใน Trust List? | ไม่ออก VC | -| **3** | ออก VC | 6.5 (§ 3) | Wallet | Issuer อยู่ใน Trust List? | ทิ้ง VC ไม่บันทึก | -| **4** | แสดง VP | 2.6 (§ 4) | Wallet | Verifier อยู่ใน Trust List? | ไม่ส่งข้อมูลให้ (จุดที่สำคัญที่สุดฝั่ง Wallet) | -| **5** | แสดง VP | 4.1 (§ 4) | Verifier | Wallet Provider อยู่ใน Trust List? (บังคับเฉพาะ Wallet ของรัฐ) | ปฏิเสธ | -| **6** | แสดง VP | 4.2 (§ 4) | Verifier | Issuer ของ VC อยู่ใน Trust List + status + สิทธิ์ออกบัตร? | ไม่เชื่อถือข้อมูล | - -> **หมายเหตุ:** หลักการตรวจสอบพื้นฐาน 4 ขั้น (Core Security Rules) ที่ทุก flow ใช้ร่วมกัน อยู่ที่หัวข้อ "หลักการตรวจสอบร่วม (Common Verification Pattern)" ในบท [1.4 หลักการตรวจสอบ — ใครตรวจใคร](./03-common-verification-pattern.md) - -จบเนื้อหาบทที่ 1 (คำศัพท์และบทบาท) — บทถัดไปคือ [บทที่ 2 สถาปัตยกรรม](../02-architecture/01-overview.md) อธิบายว่าส่วนประกอบต่าง ๆ (Gateway, Trustlist, Resolver) เชื่อมต่อกันอย่างไรในทางเทคนิค - diff --git a/docs/trust-guide/01-concepts-and-roles/03-common-verification-pattern.md b/docs/trust-guide/01-concepts-and-roles/03-common-verification-pattern.md deleted file mode 100644 index 9066c84..0000000 --- a/docs/trust-guide/01-concepts-and-roles/03-common-verification-pattern.md +++ /dev/null @@ -1,17 +0,0 @@ -# 1.4 หลักการตรวจสอบ — ใครตรวจใคร - -## 1.4 หลักการตรวจสอบ (Common Verification Pattern) - -ในระบบนี้ ทุกฝ่ายตรวจสอบความน่าเชื่อถือด้วยวิธีเดียวกัน คือ **ดูรายชื่อที่เชื่อถือได้ (TrustList)** ที่ ETDA เป็นผู้ดูแล แต่ว่า "ตรวจใคร" นั้นต่างกันไปตามหน้าที่ของแต่ละฝ่าย - -ตารางด้านล่างสรุปว่าใครต้องตรวจใครบ้าง ถ้าอยากรู้รายละเอียดเต็ม เช่น field หรือ error code ให้ตามลิงก์ในตารางไปอ่านที่บทที่เกี่ยวข้อง - -### ใครตรวจใคร - -| ผู้ตรวจ | ตรวจใคร | ดูรายละเอียดเต็มที่ | -|---------|---------|----------------------| -| Wallet | Issuer (ตอนรับบัตร) และ Verifier (ตอนถูกขอดูบัตร) | [3.2.5 การตรวจสอบ Trustlist](../03-issuance-flow/02-full-flow/06-verification-details.md), [4 Full Flow — จุดตรวจที่ 1](../04-presentation-flow/02-full-flow/02b-full-flow-technical-detail.md) | -| Issuer | Wallet Provider (ก่อนออกบัตร) | [3.2.5 การตรวจสอบ Trustlist](../03-issuance-flow/02-full-flow/06-verification-details.md) | -| Verifier | Wallet Provider ก่อน แล้วตามด้วย Issuer (ตอนรับบัตรที่แสดงมา) | [4 Full Flow — จุดตรวจที่ 2–4](../04-presentation-flow/02-full-flow/02b-full-flow-technical-detail.md) | - -> หน้านี้เก็บแค่ภาพรวมว่า "ใครตรวจใคร" เพื่อไม่ให้เนื้อหาซ้ำกับบทอื่น ส่วนวิธีตรวจแบบละเอียดทีละขั้นอยู่ในบท 3 (การออกเอกสาร) และบท 4 (การแสดงเอกสาร) diff --git a/docs/trust-guide/01-concepts-and-roles/_category_.json b/docs/trust-guide/01-concepts-and-roles/_category_.json deleted file mode 100644 index 45b5d9f..0000000 --- a/docs/trust-guide/01-concepts-and-roles/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "1. คำศัพท์และบทบาท", - "position": 1 -} diff --git a/docs/trust-guide/02-architecture/01-overview.md b/docs/trust-guide/02-architecture/01-overview.md deleted file mode 100644 index 7016134..0000000 --- a/docs/trust-guide/02-architecture/01-overview.md +++ /dev/null @@ -1,126 +0,0 @@ -# 2.1 สถาปัตยกรรม (Architecture) — ภาพรวม - -ต่อจากบทที่ 1 ([คำศัพท์และบทบาท](../01-concepts-and-roles/01-glossary.md)) ที่อธิบายว่าใครเป็นใครและเชื่อกันอย่างไร บทนี้ลงรายละเอียดทางเทคนิคว่าส่วนประกอบต่าง ๆ ของระบบ (Gateway, Trustlist, Resolver) เชื่อมต่อกันอย่างไร - -## 2.1 บทนำและขอบเขต - -- **จุดประสงค์:** วางกรอบความเชื่อถือสำหรับการทำ VC เพื่อป้องกันไม่ให้ผู้ไม่น่าเชื่อถือเข้ามาในระบบ VC ของประเทศไทย -- **ขอบเขต:** ครอบคลุมกฎที่ Entity ทุกฝ่ายต้องปฏิบัติตาม เพื่อเข้าร่วมกรอบความน่าเชื่อถือและดูแลความปลอดภัยทางเทคโนโลยี ไม่ครอบคลุมวิธีใช้งานหรือกฎหมายที่เกี่ยวข้อง -- **ใครต้องทำตาม:** Issuer, Wallet, Verifier, ETDA -- **คำบังคับ:** ใช้ MUST / SHOULD / MAY (RFC 2119) - - -## 2.2 สถาปัตยกรรม (Architecture) - -ส่วนนี้เล่าว่าระบบมีอะไรบ้าง และแต่ละส่วนคุยกันยังไง แบบเข้าใจง่าย - -### ภาพรวมของระบบ - -ในระบบนี้มีของหลัก ๆ ไม่กี่อย่าง: - -- **Entity** = ผู้เข้าร่วม เช่น Issuer, Verifier, Wallet (คนที่มาขอใช้บริการ) -- **API Gateway** = ประตูทางเข้าของ ETDA ทุกคนต้องคุยผ่านประตูนี้ (`trust.etda.or.th`) -- **TrustList** = บัญชีรายชื่อคนที่เชื่อถือได้ โดย ETDA เป็นคนเซ็นรับรอง -- **Resolver** = ตัวช่วยไปตามหาข้อมูล DID ให้ -- **well-known** = ไฟล์สาธารณะที่แต่ละฝ่ายเปิดไว้ ข้างในมีกุญแจสาธารณะ (public key) - -รูปข้างล่างเป็นภาพรวม กล่องสีคือส่วนที่คนใช้งานคุยด้วยบ่อยที่สุด (Entity กับ Gateway) ส่วนที่เหลือเป็นของหลังบ้านของ ETDA - -```mermaid -%%{init: {"theme":"base","flowchart":{"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - E["Entity
Issuer / Verifier / Wallet"]:::hl - subgraph ETDA["ETDA"] - direction TB - GW["API Gateway
trust.etda.or.th"]:::hl - TL["TrustList"] - RES["Resolver"] - end - WK["well-known
ของแต่ละฝ่าย และ ของ ETDA"] - - E -->|"ขอ TrustList / ขอ resolve DID"| GW - GW --> TL - GW --> RES - RES -->|"ไปดึง DID Document"| WK - E -.->|"เปิดไฟล์ did:web ให้คนอื่นดึง"| WK - - classDef hl fill:#fff4e0,stroke:#cc7700,stroke-width:2px,color:#000; -``` - -ระบบทำงานหลัก ๆ อยู่ 2 เรื่อง ถ้าดูรูปบนแล้วยังงง ให้ดูทีละเรื่องข้างล่างนี้ - -### 2.2.1 ขอ TrustList แล้วตรวจว่าจริงไหม - -Entity ขอรายชื่อที่เชื่อถือได้ (TrustList) ผ่าน Gateway จากนั้นเอากุญแจสาธารณะของ ETDA มาตรวจลายเซ็น ถ้าลายเซ็นถูก แปลว่ารายชื่อนี้ของจริง เชื่อได้ - -ไล่ทีละขั้น: - -1. ขอกุญแจสาธารณะของ ETDA ก่อน (จากไฟล์ trust-anchor) -2. ขอ TrustList โดยแนบ API key -3. Gateway ส่ง TrustList พร้อมลายเซ็น (Detached JWS) กลับมา -4. Entity ตรวจลายเซ็นด้วยกุญแจของ ETDA -5. ถ้าถูก เก็บ TrustList ไว้ใช้ (cache) พร้อมจำวันหมดอายุ - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"}}}%% -sequenceDiagram - autonumber - participant E as 👥 Entity - participant GW as 🛡️ ETDA Gateway - E->>GW: ขอกุญแจสาธารณะ ETDA
GET /.well-known/trust-anchor - GW-->>E: ส่ง ETDA Public Key (JWK) - E->>GW: ขอ TrustList แนบ API key
GET /trustlist - GW-->>E: ส่ง TrustList พร้อมลายเซ็น (Detached JWS) - E->>E: ตรวจลายเซ็นด้วยกุญแจ ETDA - Note over E: ถ้าลายเซ็นถูก คือเชื่อ TrustList ได้
เก็บ cache ไว้ใช้ จนกว่าจะหมดอายุ -``` - -### 2.2.2 ขอ Resolver ช่วยหา DID - -บางที Entity มีแค่ชื่อ DID ของอีกฝ่าย แต่ยังไม่มีกุญแจ จึงขอให้ Gateway ช่วยไปตามหา DID Document (ที่มีกุญแจสาธารณะอยู่ข้างใน) ให้ - -ไล่ทีละขั้น: - -1. Entity ส่งชื่อ DID ไปที่ Gateway (เช่น `did:ndid:...`) -2. Gateway ส่งต่อให้ Resolver -3. Resolver ไปดึง DID Document จากไฟล์ well-known ของฝ่ายนั้น -4. ส่ง DID Document กลับมาที่ Entity -5. Entity เอากุญแจใน DID Document ไปตรวจ VC หรือลายเซ็นต่อ - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"}}}%% -sequenceDiagram - autonumber - participant E as 👥 Entity (คนถาม) - participant GW as 🛡️ ETDA Gateway - participant R as 🔎 Resolver - participant WK as 📄 well-known ของอีกฝ่าย - E->>GW: ขอ resolve DID
GET /1.0/identifiers/⟨did⟩ - GW->>R: ส่ง DID ให้ Resolver - R->>WK: ไปดึง DID Document - WK-->>R: ส่ง DID Document กลับ - R-->>GW: ส่งต่อ DID Document - GW-->>E: ส่ง DID Document (มีกุญแจสาธารณะ) - Note over E: เอากุญแจไปตรวจ VC หรือลายเซ็นต่อได้
ถ้าเป็น did:web จะดึงเองก็ได้ ไม่ต้องพึ่ง Gateway -``` - -### 2.2.3 ถ้าอยากใช้ DID แบบพิเศษ (Custom DID) - -ปกติ Resolver ส่วนกลางของ ETDA รู้จัก DID แบบมาตรฐานอยู่แล้ว แต่บาง Entity อยากใช้ DID แบบของตัวเอง (เรียกว่า **Custom DID Method**) ที่ Resolver กลางยังไม่รู้จัก - -แบบนี้ก็ทำได้ แต่ต้องคุยกับ ETDA ก่อน แล้วเลือกวิธีเชื่อมต่อ 1 ใน 2 แบบ: - -- **แบบที่ 1:** Entity ส่ง "ตัวช่วยอ่าน DID" (DID Driver) ให้ ETDA เอาไปติดตั้งใน Resolver กลาง แล้ว Resolver กลางก็อ่าน Custom DID ได้เอง -- **แบบที่ 2:** Entity เปิด Resolver ของตัวเอง แล้วให้ Gateway ของ ETDA ยิงคำขอเข้ามา (Entity ต้องอนุญาต IP ของ Gateway ก่อน หรือที่เรียกว่า Whitelist) - -รายละเอียดครบ ๆ ทั้งขั้นตอนการลงทะเบียนและรูปประกอบ ดูได้ที่ [§ 6.1.1.1 การใช้งาน Custom DID](../06-security-and-privacy/01-verifier-confidence-and-registration.md#6111-การใช้งาน-custom-did) - -**สรุปส่วนประกอบและเส้นทางข้อมูล:** - -- **ส่วนประกอบ:** Gateway, TrustList, Resolver, Entity, well-known (DID) -- **เส้นทางข้อมูล:** Entity ⇄ Gateway ⇄ TrustList และ Entity ⇄ Gateway ⇄ Resolver ⇄ well-known - ---- - -จบเนื้อหาบทที่ 2 (สถาปัตยกรรม) — บทถัดไปคือ [บทที่ 3 กระบวนการออกเอกสาร (OID4VCI)](../03-issuance-flow/01-minimal-flow.md) อธิบาย flow การออก VC แบบเต็มทุกขั้นตอน เริ่มจาก Minimal Flow ที่เข้าใจง่ายก่อน - diff --git a/docs/trust-guide/02-architecture/_category_.json b/docs/trust-guide/02-architecture/_category_.json deleted file mode 100644 index a6c1708..0000000 --- a/docs/trust-guide/02-architecture/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "2. สถาปัตยกรรม", - "position": 2 -} diff --git a/docs/trust-guide/03-issuance-flow/01-minimal-flow.md b/docs/trust-guide/03-issuance-flow/01-minimal-flow.md deleted file mode 100644 index 2679e55..0000000 --- a/docs/trust-guide/03-issuance-flow/01-minimal-flow.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -sidebar_position: 1 ---- - -# 3.1 กระบวนการออกเอกสาร (OID4VCI) — ภาพรวมและ Minimal Flow - -บทนี้อธิบายกระบวนการออกเอกสาร (Verifiable Credential) ตามมาตรฐาน OID4VCI ตั้งแต่ **Minimal Flow** ที่เข้าใจง่าย (§ 3.1) ไปจนถึง **Full Flow** ฉบับเทคนิคเต็มรูปแบบ (§ 3.2) โดยใช้สถานการณ์ตัวอย่างเดียวกันตลอดทั้งบท - -> **Protocol:** OID4VCI **1.0 Final** (openid-4-verifiable-credential-issuance-1_0) -> **VC Format:** IETF SD-JWT VC (dc+sd-jwt) + VC-JOSE-COSE (`typ: dc+sd-jwt`) -> **Status:** IETF Token Status List (`statuslist+jwt`) -> **Flow:** Pre-Authorized Code Flow (สำหรับ VC ทุกประเภท — ไม่ใช้ Authorization Code Flow) -> **Token Binding:** DPoP (RFC 9449) — บังคับสำหรับ **LoA ≥ 3**, แนะนำสำหรับ LoA 2 (ดู [§ 3.2.6.1](02-full-flow/07-data-flow-and-policy.md#32611-loa-แต่ละระดับหมายถึงอะไร)) - -**สถานการณ์:** ประชาชนขอรับ **บัตรประชาชนดิจิทัล** จากกรมการปกครอง (DOPA) เก็บไว้ในแอป Wallet บนมือถือ - -> **หมายเหตุ:** ในตัวอย่างนี้ กรมการปกครอง (DOPA) ทำหน้าที่เป็นทั้ง Credential Issuer และ Authorization Server ในตัวเดียวกัน จึงไม่มีขั้นตอน AS Discovery แยกต่างหาก (Issuer ที่แยก Authorization Server ออกจาก Credential Issuer จะมีขั้นตอน AS Discovery เพิ่มเติมตามมาตรฐาน OID4VCI ซึ่งเอกสารฉบับนี้ยังไม่ครอบคลุมรายละเอียด) - ---- - -## 3.1 Minimal Flow — ภาพรวมและจุดที่ Trust Model คุม - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -sequenceDiagram - actor User as 👤 ประชาชน - participant W as 📱 Wallet - participant WP as 🏢 Wallet Provider - participant DOPA as 🏛️ กรมการปกครอง (DOPA) - participant ETDA as 🛡️ ETDA Trustlist - - rect rgb(210, 240, 210) - note over W,ETDA: 🟢 [ETDA คุม] SETUP — ดึง Trustlist (ต้องแนบ API Key) - W->>ETDA: ขอรายชื่อผู้ที่ผ่านการรับรอง + API Key - ETDA-->>W: Trustlist (WP, Issuer ที่อนุมัติแล้ว) - DOPA->>ETDA: ขอรายชื่อผู้ที่ผ่านการรับรอง + API Key - ETDA-->>DOPA: Trustlist - end - - rect rgb(220, 240, 255) - note over User,DOPA: 🔵 [OID4VCI] STEP 1 — รับคำเชิญ - User->>DOPA: เข้าระบบ + กด ขอบัตรประชาชนดิจิทัล - DOPA-->>User: QR Code + SMS OTP - User->>W: สแกน QR - end - - rect rgb(255, 237, 200) - note over W,ETDA: 🟠 [ETDA Trust Model] STEP 1.5 — Early Trust Check: Issuer - W->>W: ตรวจ credential_issuer จาก QR ใน Trustlist — active? - W->>W: ตรวจ credential_type ใน QR ได้รับอนุมัติจาก ETDA? - end - - rect rgb(220, 240, 255) - note over User,W: 🔵 [OID4VCI] STEP 2–3 — ยืนยันตัวตน - User->>W: กรอก OTP - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 4 — Token Request - W->>DOPA: ส่ง OTP + รหัสลับจาก QR - DOPA-->>W: access_token (บัตรผ่านชั่วคราว) - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 5 — ขอบัตรประชาชนดิจิทัล - W->>DOPA: ส่งคำขอบัตร + แนบ Wallet Attestation - end - - rect rgb(255, 237, 200) - note over DOPA,WP: 🟠 [ETDA Trust Model] STEP 5.5 — ตรวจ Wallet Provider - DOPA-->>DOPA: ตรวจว่า Wallet Provider นี้อยู่ใน Trustlist - DOPA->>WP: ตรวจว่า Wallet นี้อยู่ใน Wallet Provider - WP-->>DOPA: ยืนยัน Wallet อยู่ใน Wallet Provider - DOPA-->>DOPA: ตรวจว่า Wallet ไม่ถูกแก้ไข - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 6 — ออกบัตรประชาชนดิจิทัล - DOPA-->>W: บัตรประชาชนดิจิทัล (SD-JWT VC) - end - - rect rgb(255, 237, 200) - note over W,ETDA: 🟠 [ETDA Trust Model] STEP 6.5 — ตรวจ Issuer - W->>W: ตรวจว่า DOPA อยู่ใน Trustlist ไหม? - W->>W: ตรวจลายเซ็นดิจิทัลของบัตร ✅ - end - - W-->>User: ✅ บันทึกบัตรประชาชนดิจิทัลสำเร็จ! -``` - -**สีกรอบในผังหมายถึงอะไร:** - -| สี | หมายถึง | ใครกำหนด | -|----|---------|----------| -| 🟢 สีเขียว | ขั้นตอนที่ ETDA ดูแลโดยตรง (Trustlist) | ETDA | -| 🔵 สีน้ำเงิน | ขั้นตอนตามมาตรฐาน OID4VCI สากล | OpenID Foundation | -| 🟠 สีส้ม | จุดตรวจสอบ trust ว่าใครน่าเชื่อถือ | Trust Framework (ETDA) | -| ⚪ สีเทา | ขั้นตอนเตรียมการของ Wallet Provider | Wallet Provider | - -**Trustlist Verification — จุดตรวจที่ ETDA คุม (2 จุดใน flow):** - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart TD - classDef store fill:#e2e3e5,stroke:#6c757d,color:#343a40 - classDef check fill:#fff3cd,stroke:#ffc107,color:#856404 - classDef pass fill:#d4edda,stroke:#28a745,color:#155724 - classDef fail fill:#f8d7da,stroke:#721c24,color:#721c24 - classDef src fill:#cce5ff,stroke:#0056b3,color:#003d80 - - ETDA[("🛡️ ETDA Trustlist")]:::src - TL_W[("📱 Wallet Local Trustlist\n• Issuer DID")]:::store - TL_D[("🏛️ DOPA Local Trustlist\n• WP ID → endpoint")]:::store - ETDA -->|"SETUP ดึงก่อนใช้งาน (API Key required) "| TL_W & TL_D - - subgraph step55["🟠 STEP 5.5 — DOPA ตรวจ Wallet Provider (ก่อนออกบัตร)"] - WP_ID["① ดึง Wallet Provider ID(iss) \nจาก wallet_attestation"]:::check - WP_EP{"② ตรวจ Wallet Provider\nอยู่ใน Trustlistไหม"}:::check - WP_PK["③ ดึง Public Key\nของ Wallet Provider"]:::check - WP_ST["④ ตรวจสถานะ Wallet\nยังใช้งานได้อยู่ไหม"]:::check - WP_VF{"⑤ ตรวจ Wallet_attestation\nใน Wallet"}:::check - WP_CT["⑥ ตรวจประเภทบัตร\nว่าได้รับอนุมัติไว้ไหม?"]:::check - OK1["✅ ออกบัตรได้"]:::pass - FL1["❌ ปฏิเสธ"]:::fail - FL2["❌ ประเภทบัตรไม่ได้รับอนุมัติ"]:::fail - TL_D -.->|"ดึงข้อมูล"| WP_EP - WP_ID --> WP_EP --> WP_PK & WP_ST --> WP_VF - WP_EP -->|"ไม่ได้รับอนุมัติ"| FL2 - WP_VF -->|"ลายเซ็นถูกต้อง"| WP_CT - WP_VF -->|"ลายเซ็นผิด"| FL1 - WP_CT -->|"ได้รับอนุมัติ"| OK1 - WP_CT -->|"ไม่ได้รับอนุมัติ"| FL2 - end - - subgraph step65["🟠 STEP 6.5 — Wallet ตรวจ Issuer (หลังรับบัตร)"] - IS_DID["① แกะ Issuer DID\nจาก SD-JWT VC"]:::check - IS_TL["② ตรวจ DID\nใน Local Trustlist"]:::check - IS_PK["③ Resolve DID\n→ Public Key"]:::check - IS_VF{"④ Verify VC signature"}:::check - OK2["✅ บันทึกบัตร"]:::pass - FL3["❌ ทิ้งบัตร"]:::fail - TL_W -.->|"ดึงข้อมูล"| IS_TL - IS_DID --> IS_TL --> IS_PK --> IS_VF - IS_VF -->|"ลายเซ็นถูกต้อง"| OK2 - IS_VF -->|"ลายเซ็นผิด"| FL3 - end -``` - ---- - -### 3.1.1 คำอธิบายแต่ละขั้นตอน (สำหรับผู้อ่านทั่วไป) - -**ภาพรวม:** เปรียบได้กับการไปทำบัตรประชาชนที่อำเภอ แต่ทำทั้งหมดผ่านมือถือ โดยมี ETDA รับประกันว่าทุกคนในระบบ (กรมการปกครอง, แอป Wallet) ผ่านการตรวจสอบมาแล้ว - -**🟢 ก่อนเริ่ม — SETUP (ETDA คุม)** - -Wallet และ DOPA ดึง ETDA Public Key จาก `/.well-known/trust-anchor` แล้วดึง Trustlist (พร้อม API Key) มาเก็บไว้ใน Local Storage ใช้สำหรับ verify ทุกขั้นตอนที่ต้องตรวจ trust - -**🔵 STEP 1 — รับคำเชิญ (OID4VCI)** - -เราเข้าเว็บไซต์บริการประชาชนของกรมการปกครอง แล้วกดขอบัตรประชาชนดิจิทัล ระบบจะแสดง QR Code บนหน้าจอ และส่ง OTP มาทาง SMS เราเปิดแอป Wallet สแกน QR Code นั้น แค่นี้แอปก็รู้แล้วว่าจะไปขอบัตรจากที่ไหน - -**🔵 STEP 2 — ดูข้อมูลผู้ออกบัตร (OID4VCI)** - -แอป Wallet ดึงข้อมูลเบื้องต้นจากกรมการปกครองมาก่อน เช่น บัตรแบบไหนออกได้ ใช้รูปแบบข้อมูลอะไร และต้องส่งคำขอไปที่ endpoint ไหน เปรียบเหมือนอ่านป้ายประกาศหน้าเคาน์เตอร์ก่อนกรอกแบบฟอร์ม - -**[7.1] Scope Validation** — แอปตรวจว่า `credential_configurations_supported` มีประเภทบัตรที่ต้องการหรือไม่ ถ้าไม่พบ → หยุดทันที ไม่ส่งคำขอต่อ - -**🔵 STEP 3 — ยืนยันตัวตนด้วย OTP (OID4VCI)** - -แอปถามว่า "กรุณากรอก OTP ที่ได้รับทาง SMS" เราพิมพ์รหัส OTP เข้าไป นี่คือการยืนยันว่าเราเป็นเจ้าของเบอร์โทรนั้นจริง - -**🔵 STEP 4 — แลกบัตรผ่านชั่วคราว (OID4VCI)** - -แอปนำ OTP และรหัสลับจาก QR ไปยื่นให้กรมการปกครอง แล้วได้ "access_token" กลับมา เปรียบเหมือนบัตรคิวที่ใช้ได้แค่ 5 นาที สำหรับขั้นตอนถัดไป - -**🔵 STEP 5 — ขอรับบัตรประชาชนดิจิทัล (OID4VCI)** - -แอปสร้าง key คู่ (กุญแจส่วนตัว + กุญแจสาธารณะ) บนมือถือของเรา แล้วส่งคำขอบัตรพร้อมแนบ "Wallet Attestation" (หลักฐานว่าแอปนี้ผ่านการรับรองแล้ว) ไปให้กรมการปกครอง - -**🟠 STEP 5.5 — กรมการปกครองตรวจ Wallet Provider (ETDA Trust Model)** - -DOPA ตรวจ 6 ขั้นตอน: (1) แกะ WP ID จาก `wallet_attestation` → (2) หา WP endpoint จาก Trustlist → (3) ดึง WP Public Key และสถานะ Wallet → (4) ตรวจลายเซ็น WA → (5) ตรวจ aal → (6) ตรวจประเภทบัตรด้วยตัวเอง - -ถ้าขั้นตอนใดล้มเหลว → ปฏิเสธทันที ไม่ออกบัตร - -**🔵 STEP 6 — ออกบัตรประชาชนดิจิทัล (OID4VCI + IETF SD-JWT VC (dc+sd-jwt))** - -กรมการปกครองสร้าง SD-JWT VC ตามมาตรฐาน IETF SD-JWT VC (dc+sd-jwt) มีโครงสร้าง 3 ชั้น: - -- **JOSE Header:** `typ: dc+sd-jwt`, `alg: ES256`, `kid: https://issuer.dopa.go.th#key-2025-1` -- **JWT Claims:** `iss`, `sub`, `jti`, `nbf`, `exp`, `cnf.jwk`, `_sd_alg: sha-256`, `status.status_list` (IETF Token Status List) -- **VC Body (IETF SD-JWT VC (dc+sd-jwt)):** `@context`, `type: [VerifiableCredential, ThaiNationalIDCredential]`, `issuer: {id, name}`, `validFrom`, `validUntil`, `credentialSubject: {id, ชื่อ, เลขบัตร, วันเกิด}`, `credentialSchema`, `_sd: [...]` - -**🟠 STEP 6.5 — Wallet ตรวจ Issuer + VC (IETF SD-JWT VC (dc+sd-jwt))** - -เมื่อได้รับบัตร Wallet ตรวจ 3 กลุ่มก่อนบันทึก: -1. **ตัวตนผู้ออกบัตร** — เช็คว่าผู้ออกบัตรอยู่ใน Trustlist หรือไม่ -2. **ลายเซ็น + อายุ** — เช็คว่าลายเซ็นบนบัตรถูกต้องและยังไม่หมดอายุ -3. **สถานะบัตร** — เช็คกับ statuslist ว่าบัตรยังไม่ถูกยกเลิก - -ถ้าขั้นใดไม่ผ่าน → Wallet จะทิ้งบัตรและไม่บันทึกลงในแอป - -**🔵 STEP 7 — แจ้งว่ารับบัตรแล้ว (OID4VCI, Optional)** - -แอปแจ้งกลับไปยังกรมการปกครองว่าได้รับบัตรเรียบร้อยแล้ว เป็นขั้นตอนเสริมเพื่อให้ระบบของกรมการปกครองรู้ว่าออกบัตรสำเร็จ - ---- - diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/02b-full-flow-technical-detail.md b/docs/trust-guide/03-issuance-flow/02-full-flow/02b-full-flow-technical-detail.md deleted file mode 100644 index f8ea592..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/02b-full-flow-technical-detail.md +++ /dev/null @@ -1,276 +0,0 @@ ---- -sidebar_position: 2 ---- - -# 3.2.1 Full Flow เทคนิคโดยละเอียด — OID4VCI (การออก VC) - -## ภาพรวมขั้นตอนทั้งหมด (STEP 1–7) - -บทที่ [3.1 Minimal Flow](../01-minimal-flow.md) แสดงภาพรวมแบบง่ายของการออก VC ผ่าน OID4VCI พร้อมจุดที่ Trust Model (ETDA) เข้าควบคุม หน้านี้ (3.2) จะขยายภาพให้เห็น **ทุก STEP ตั้งแต่ SETUP จนถึง STEP 7 (Notification)** อย่างละเอียดขึ้น รวมผู้เกี่ยวข้องทั้งหมด (ประชาชน, Wallet, Wallet Provider, ETDA Trustlist, DOPA, DOPA Status List) และกรณีพิเศษที่ Minimal Flow ยังไม่ครอบคลุม เช่น: - -- **DPoP (Demonstrating Proof of Possession)** สำหรับระดับความเชื่อมั่น LoA 3 ขึ้นไป — ผูก access_token กับกุญแจบนเครื่อง ป้องกันการขโมย token ไปใช้งานที่อื่น -- **Immediate vs. Deferred Issuance** — กรณีบัตรออกได้ทันที เทียบกับกรณีที่ต้องรอ (`transaction_id`) แล้วค่อยมาขอรับภายหลัง -- **Trustlist Freshness Check** — จุดตรวจ TTL ของ Trustlist ก่อนแต่ละจุดตรวจ trust (ก่อน STEP 5.5 และก่อน STEP 6.5) -- **โครงสร้าง SD-JWT VC แบบ 3 ชั้น** (JOSE Header / JWT Claims / VC Body ตาม IETF SD-JWT VC `dc+sd-jwt`) - -ลำดับขั้นตอนทั้งหมดของ flow นี้ ประกอบด้วย: - -| ช่วง STEP | ชื่อขั้นตอน | รายละเอียดเพิ่มเติม | -|-----------|-------------|----------------------| -| SETUP | ดึง ETDA Public Key + Trustlist, Wallet Attestation | [3.2.2](03-setup-and-credential-offer.md) | -| STEP 1–3 | Credential Offer, Early Trust Check, Discovery, Authentication | [3.2.2](03-setup-and-credential-offer.md) | -| STEP 4–5.5 | Token Request, Credential Request, Trust Check: Wallet Provider | [3.2.3](04-token-and-credential-request.md) | -| STEP 6–7 | Issue VC, Trust Check: Issuer, Notification | [3.2.4](05-issue-and-notify.md) | -| — | รายละเอียดการตรวจสอบ Trustlist ทุกจุด (L1–L7) | [3.2.5](06-verification-details.md) | -| — | Data Flow และ LoA/DPoP Policy | [3.2.6](07-data-flow-and-policy.md) | - -## รายละเอียดทางเทคนิคระดับ field-by-field - -> เนื้อหาส่วนนี้เป็นรายละเอียดทางเทคนิคระดับ field-by-field ของขั้นตอนการออก VC (OID4VCI) ทั้งหมด ครบทุก STEP ตั้งแต่ SETUP จนถึง Notification รวมถึง DPoP (LoA 3+), Deferred Issuance และจุดตรวจสอบ Trust ทุกจุด เหมาะสำหรับผู้พัฒนาและสถาปนิกระบบที่ต้องการรายละเอียดครบทุกขั้นตอน -> -> คลิกที่หัวข้อด้านล่างเพื่อดูเนื้อหาแบบเต็ม (ซ่อนไว้โดยดีฟอลต์เพื่อไม่ให้หน้าเอกสารยาวเกินไป) - -
-📖 คลิกเพื่อดู Full Flow เทคนิคทั้งหมด (Sequence Diagram + คำอธิบายทุกขั้นตอน) - -## Sequence Diagram ฉบับเต็ม - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -sequenceDiagram - actor EU as 👤 ประชาชน - participant W as 📱 Wallet - participant WP as 🏢 Wallet Provider - participant ETDA as 🛡️ ETDA Trustlist - participant DOPA as 🏛️ กรมการปกครอง (DOPA) - participant SL as 📋 DOPA Status List - - rect rgb(210, 240, 210) - note over W,DOPA: 🟢 [ETDA คุม] SETUP — ดึง ETDA Public Key + Trustlist - W->>ETDA: GET https://trust.etda.or.th/.well-known/trust-anchor - ETDA-->>W: ETDA Public Key (JWK) - W->>W: เก็บ ETDA Public Key ไว้ใน Local Storage - DOPA->>ETDA: GET https://trust.etda.or.th/.well-known/trust-anchor - ETDA-->>DOPA: ETDA Public Key (JWK) - DOPA->>DOPA: เก็บ ETDA Public Key ไว้ใน Local Storage - W->>ETDA: GET /trustlist + Authorization: Bearer *** - ETDA-->>W: Trustlist JSON + Detached JWS Signature - W->>W: Verify Detached JWS ด้วย ETDA Public Key - W->>W: เก็บ Trustlist + บันทึก TTL (exp claim) - DOPA->>ETDA: GET /trustlist + Authorization: Bearer *** - ETDA-->>DOPA: Trustlist JSON + Detached JWS Signature - DOPA->>DOPA: Verify Detached JWS ด้วย ETDA Public Key - DOPA->>DOPA: เก็บ Trustlist + บันทึก TTL (exp claim) - end - - rect rgb(240, 240, 240) - note over W,WP: ⚙️ [Wallet Provider ดูแล] SETUP — Wallet Attestation - WP->>WP: ลงทะเบียน Wallet instance + เก็บข้อมูลไว้ใน Registry - WP-->>W: Wallet Attestation JWT (ลงนามโดย Wallet Provider) - W->>W: เก็บ Wallet Attestation ไว้ใน Local Storage - end - - rect rgb(220, 240, 255) - note over EU,DOPA: 🔵 [OID4VCI] STEP 1 — Credential Offer "รับคำเชิญ" - EU->>DOPA: [1] เข้าสู่ระบบบริการประชาชน + กด "ขอบัตรประชาชนดิจิทัล" - DOPA->>DOPA: [2] สร้าง pre_authorized_code + tx_code (OTP) - DOPA-->>EU: [3] QR Code บนหน้าจอ - DOPA-->>EU: [4] SMS OTP 512847 - EU->>W: [5] สแกน QR Code - end - - rect rgb(255, 237, 200) - note over W,ETDA: 🟠 [ETDA Trust Model] STEP 1.5 — Early Trust Check: Issuer - W->>W: [5.5] ตรวจ credential_issuer จาก QR ใน Local Trustlist - W->>W: [5.6] ตรวจ status = active + valid_from ≤ now ≤ valid_until - W->>W: [5.7] ตรวจ credential_configuration_id ∈ TL.allowed_credential_types - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 2 — Discovery "ดูข้อมูลผู้ออกบัตร" - W->>DOPA: [6] GET /.well-known/openid-credential-issuer - DOPA-->>W: [7] Credential Issuer Metadata (format, claims, token_endpoint) - W->>W: [7.1] Scope Validation — ตรวจ credential_configurations_supported ว่ามีประเภทบัตรที่ต้องการ ถ้าไม่พบ → หยุด - end - - rect rgb(220, 240, 255) - note over EU,W: 🔵 [OID4VCI] STEP 3 — Authentication "ยืนยันตัวตนด้วย OTP" - W->>EU: [8] กรุณากรอกรหัส OTP ที่ได้รับทาง SMS - EU->>W: [9] กรอก OTP: 512847 - end - - rect rgb(235, 230, 255) - note over W,DOPA: 🔐 DPoP PRE-STEP 4 — สร้าง DPoP Key Pair (LoA 3+) - W->>W: [9.1] สร้าง DPoP key pair + เก็บ private key ใน Trusted Execution Environment (TEE) - W->>W: สร้าง DPoP Proof JWT สำหรับ /token — type=dpop, method=POST, jti=unique - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 4 — Token Request "แลกบัตรผ่าน" - W->>DOPA: [10] POST /token + pre_authorized_code + tx_code + DPoP-Proof header (LoA 3+) - DOPA->>DOPA: [11] Validate: pre_auth_code, OTP, single-use, DPoP Proof signature (LoA 3+) - DOPA->>DOPA: ผูก access_token กับ DPoP public key - DOPA-->>W: [12] access_token (DPoP-bound) + c_nonce (expires_in: 300s) - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 5 — Credential Request "ขอรับบัตรประชาชนดิจิทัล" - W->>W: [13] สร้าง Key Pair + ลงนาม JWT Proof ด้วย Private Key + c_nonce - W->>W: [13.1] สร้าง DPoP Proof ใหม่สำหรับ /credential — htm:POST, jti:new-unique (LoA 3+) - W->>DOPA: [14] POST /credential + Authorization: DPoP *** DPoP-Proof + credential_configuration_id + proof.jwt + wallet_attestation - end - - rect rgb(245, 245, 220) - note over DOPA,WP: ⚙️ Trustlist Freshness Check (ก่อน STEP 5.5) - alt Trustlist ยังไม่หมดอายุ (now < exp) - DOPA->>DOPA: ใช้ Local Trustlist เดิม - else Trustlist หมดอายุ - DOPA->>ETDA: GET /trustlist + Authorization: Bearer *** - ETDA-->>DOPA: Trustlist JSON + Detached JWS Signature - DOPA->>DOPA: Verify + Update Local Trustlist + reset TTL - end - end - - rect rgb(255, 237, 200) - note over DOPA,WP: 🟠 [ETDA Trust Model] STEP 5.5 — Trust Check: Wallet Provider - DOPA->>DOPA: [15] แกะ wallet_attestation ได้ Wallet Provider ID - DOPA->>DOPA: [16] ค้นหา WP ใน Local Trustlist ได้ WP endpoint - DOPA->>WP: [17] GET WP_endpoint/.well-known/provider-info - WP-->>DOPA: [18] Wallet Provider Public Key - DOPA->>WP: [19] GET /wallet-registry?wallet_id=... - WP-->>DOPA: [20] Wallet info + status (registered, valid) - DOPA->>DOPA: [21] Verify wallet_attestation signature ด้วย WP Public Key - DOPA->>DOPA: [21.1] Self-check — ตรวจ credential_configuration_id ที่ขอมาอยู่ใน allowed_credential_types ของ DOPA ไหม - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 6 — Issue VC "ออกบัตรประชาชนดิจิทัล" - DOPA->>DOPA: [22] Validate Token + Key Proof + สร้าง SD-JWT VC - note over DOPA: JOSE — typ:dc+sd-jwt, alg:ES256, kid:issuer.dopa.go.th#key-2025-1 - note over DOPA: JWT — iss, sub, jti, nbf, exp, cnf.jwk, _sd_alg:sha-256, status.status_list - note over DOPA: VC body — context, type, issuer, validFrom, validUntil, credentialSubject, credentialSchema, _sd - alt Immediate Issuance - DOPA-->>W: [23] credential: SD-JWT VC (บัตรประชาชนดิจิทัล) - else Deferred Issuance - DOPA-->>W: [23] transaction_id: txn-dopa-456 - W->>DOPA: [24] POST /deferred_credential + transaction_id - DOPA-->>W: [25] credential: SD-JWT VC (บัตรประชาชนดิจิทัล) - end - end - - rect rgb(245, 245, 220) - note over W,DOPA: ⚙️ Trustlist Freshness Check (ก่อน STEP 6.5) - alt Trustlist ยังไม่หมดอายุ (now < exp) - W->>W: ใช้ Local Trustlist เดิม - else Trustlist หมดอายุ - W->>ETDA: GET /trustlist + Authorization: Bearer *** - ETDA-->>W: Trustlist JSON + Detached JWS Signature - W->>W: Verify + Update Local Trustlist + reset TTL - end - end - - rect rgb(255, 237, 200) - note over W,DOPA: 🟠 [ETDA Trust Model] STEP 6.5 — Trust Check: Issuer - W->>W: [26] แกะ iss, jti, sub จาก JWT payload - W->>W: [27] Lookup iss ใน Local Trustlist → พบ status = active - W->>W: [27.1] ตรวจ iss == issuer.id และ jti == id - W->>W: [27.2] ตรวจ context มี https://www.w3.org/ns/credentials/v2 - W->>W: [27.3] ตรวจ type มี VerifiableCredential และ type ที่ 2 อยู่ใน allowed_credential_types - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [W3C VC-JOSE-COSE] Resolve Issuer Public Key ผ่าน JWKS - W->>DOPA: [28] GET https://issuer.dopa.go.th/.well-known/jwt-vc-issuer - DOPA-->>W: [29] JWKS — รายการ key พร้อม kid, alg, x, y - W->>W: [29.1] kid matching: หา key ใน JWKS ที่ตรงกับ JOSE header, ดึง publicKeyJwk - end - - rect rgb(255, 237, 200) - note over W,DOPA: 🟠 [ETDA Trust Model] STEP 6.5 ต่อ — Verify + Validity + IETF Status - W->>W: [30] Verify SD-JWT signature ด้วย publicKeyJwk (kid matched) - W->>W: [30.1] ตรวจ nbf ตรงกับ validFrom (Unix vs ISO 8601) และ sub ตรงกับ credentialSubject.id - W->>W: [30.2] ตรวจ validFrom น้อยกว่าเท่ากับ now น้อยกว่าเท่ากับ validUntil - W->>SL: [30.3] GET status.status_list.uri (Accept: application/statuslist+jwt) - SL-->>W: [30.4] statuslist+jwt - W->>W: [30.5] Verify statuslist+jwt: iss, typ=statuslist+jwt, sub ตรงกับ uri - W->>W: [30.6] ตรวจ iat น้อยกว่าเท่ากับ now น้อยกว่าเท่ากับ exp ของ statuslist+jwt - W->>W: [30.7] Decode lst: base64url แล้ว zlib แล้วดึง bit ที่ index = 0 (valid) - end - - rect rgb(220, 240, 255) - note over W,DOPA: 🔵 [OID4VCI] STEP 7 — Notification "แจ้งว่ารับแล้ว" (Optional) - W->>DOPA: [31] POST /notification credential_id + event=credential_accepted - DOPA-->>W: [32] 204 No Content - W-->>EU: [33] บันทึกบัตรประชาชนดิจิทัลสำเร็จ - end -``` - -## คำอธิบายทุกขั้นตอน (ทุก step ≤ 2 บรรทัด) - -**🟢 SETUP — ดึง ETDA Public Key** -GET `/.well-known/trust-anchor` ได้ ETDA Public Key ทั้ง Wallet และ DOPA เก็บไว้ใช้ verify ลายเซ็น -เป็นจุดเริ่มของ trust chain ทั้งหมด — ETDA อัปเดต key ได้โดยไม่ต้อง redeploy app - -**🟢 SETUP — ดึง Trustlist** -GET /trustlist (ต้องแนบ API Key) ได้รายชื่อ WP/Issuer ที่ ETDA อนุมัติ เก็บพร้อม TTL -ทั้ง Wallet และ DOPA ต่างเก็บ Trustlist ไว้ใช้ตรวจ trust ในขั้นตอนถัดไป - -**⚙️ SETUP — Wallet Attestation** -Wallet Provider ออก Wallet Attestation JWT ให้ Wallet เก็บไว้ล่วงหน้า -ใช้พิสูจน์ว่าแอปนี้ผ่านการรับรองจาก ETDA เมื่อส่งคำขอบัตรใน STEP 5 - -**🔵 [1–5] STEP 1 — Credential Offer** -ผู้ใช้กดขอบัตร → DOPA สร้าง QR Code + ส่ง OTP ทาง SMS (pre_authorized_code + tx_code) -ผู้ใช้สแกน QR ด้วย Wallet — แอปรู้ว่าต้องไปขอบัตรที่ไหน และประเภทไหน - -**🔵 [6–7.1] STEP 2 — Discovery + Scope Validation** -Wallet ดึง Issuer Metadata → รู้ token_endpoint, credential_endpoint, credential type ที่รองรับ -[7.1] ตรวจว่า credential type ที่ต้องการมีใน Metadata — ถ้าไม่พบหยุดทันที ไม่ดำเนินการต่อ - -**🔵 [8–9] STEP 3 — Authentication** -Wallet ถามผู้ใช้ขอ OTP ที่ได้รับทาง SMS -ผู้ใช้กรอก OTP → พิสูจน์ว่าเป็นเจ้าของเบอร์โทรที่ผูกกับบัญชี DOPA - -**🔐 [9.1] PRE-STEP 4 — สร้าง DPoP Key Pair (LoA 3+)** -Wallet สร้าง DPoP key pair: private key เก็บใน Trusted Execution Environment (TEE), public key ฝังใน DPoP Proof JWT -DPoP ผูก access_token กับ key บนเครื่อง — ขโมย token ไปใช้จากเครื่องอื่นไม่ได้ - -**🔵 [10–12] STEP 4 — Token Request** -POST /token ส่ง pre_authorized_code + OTP + DPoP Proof (LoA 3+) → DOPA validate ทั้งหมด -ได้ access_token (DPoP-bound สำหรับ LoA 3+) + c_nonce อายุ 5 นาที - -**🔵 [13–14] STEP 5 — Credential Request** -[13] Wallet สร้าง Holder Key + JWT Proof ลงนามด้วย Private Key + c_nonce (ป้องกัน replay) -[13.1] สร้าง DPoP Proof ใหม่สำหรับ /credential (LoA 3+) → POST /credential ส่ง access_token + DPoP Proof + credential_configuration_id + proof.jwt + wallet_attestation - -**⚙️ Trustlist Freshness Check (ก่อน STEP 5.5)** -DOPA ตรวจ TTL ของ Trustlist — ถ้าหมดอายุดึงใหม่จาก ETDA ก่อนดำเนินการต่อ - -**🟠 [15–21.1] STEP 5.5 — Trust Check: Wallet Provider** -DOPA แกะ WP ID → Lookup Trustlist → GET WP Public Key + Wallet status → Verify WA signature -ตรวจ aal + ตรวจว่า DOPA อนุมัติออก credential type ที่ขอ — fail ขั้นใด ปฏิเสธทันที - -**🔵 [22–25] STEP 6 — Issue VC** -DOPA validate token + key proof → สร้าง SD-JWT VC ครบ 3 ชั้น (JOSE header / JWT claims / VC body IETF SD-JWT VC (dc+sd-jwt)) -ส่ง credential ทันที (Immediate) หรือส่ง transaction_id รอออก (Deferred) - -**⚙️ Trustlist Freshness Check (ก่อน STEP 6.5)** -Wallet ตรวจ TTL ของ Trustlist — ถ้าหมดอายุดึงใหม่จาก ETDA ก่อนดำเนินการต่อ - -**🟠 [26–27.3] STEP 6.5 ตอน 1 — ตรวจ Identity + VC Structure** -แกะ iss/jti/sub → Lookup Trustlist → ตรวจ iss == issuer.id และ jti == VC id -ตรวจ @context (IETF SD-JWT VC (dc+sd-jwt)) + type (VerifiableCredential + อนุมัติจาก ETDA) - -**🟠 [28–29.1] STEP 6.5 ตอน 2 — Resolve Issuer Key** -GET JWKS จาก DOPA `.well-known/jwt-vc-issuer` → รับ key list -kid matching: หา public key ที่ตรงกับ kid ใน JOSE header → ได้ publicKeyJwk - -**🟠 [30–30.7] STEP 6.5 ตอน 3 — Verify + Validity + Status** -Verify SD-JWT signature + ตรวจ validFrom ≤ now ≤ validUntil + sub == credentialSubject.id -ดึง statuslist+jwt → verify signature → decode bit ที่ index → bit = 0 (บัตรไม่ถูกยกเลิก) ✅ - -**🔵 [31–33] STEP 7 — Notification (Optional)** -POST /notification แจ้ง DOPA ว่ารับบัตรแล้ว — เสริมสำหรับ audit log ของ DOPA -Wallet บันทึกบัตรประชาชนดิจิทัลสำเร็จ ✅ - -
diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/03-setup-and-credential-offer.md b/docs/trust-guide/03-issuance-flow/02-full-flow/03-setup-and-credential-offer.md deleted file mode 100644 index 7527efb..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/03-setup-and-credential-offer.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -sidebar_position: 3 ---- - -# 3.2.2 ขั้นตอนที่ 1 — SETUP, Credential Offer, Discovery, Authentication - -## 3.2.2 คำอธิบายแต่ละขั้นตอนโดยละเอียด (SETUP, Credential Offer, Discovery, Authentication) - -ต่อจากภาพรวมทั้งหมดใน [3.2.1 Full Flow](02b-full-flow-technical-detail.md) บทนี้อธิบาย **SETUP และ STEP 1–3** แบบละเอียดทีละ field ตั้งแต่การเตรียมระบบ ไปจนถึงผู้ใช้ยืนยันตัวตนด้วย OTP - ---- - -##### 🟢 SETUP — เตรียมระบบก่อนเริ่มใช้งาน - -> **เมื่อไหร่:** ทำครั้งเดียวตอน install app (Wallet) หรือ deploy ระบบ (DOPA) -> **ทำไม:** ทุกฝ่ายต้องมีข้อมูลพื้นฐานก่อน จึงจะ verify กันได้ - ---- - -###### 🔐 Pre-Setup — ETDA Public Key (root of trust) - -**ทำอะไร:** Wallet และ DOPA ดึง ETDA Public Key จาก endpoint ที่ ETDA ประกาศไว้ก่อน — ใช้ key นี้ verify ลายเซ็นบน Trustlist - -**Request:** -```http -GET https://trust.etda.or.th/.well-known/trust-anchor -``` - -**Response:** -```json -{ - "kty": "EC", "crv": "P-256", - "x": "ETDA_public_x_value", "y": "ETDA_public_y_value", - "kid": "etda-trust-anchor-2025", "use": "sig" -} -``` - -> **Root of Trust:** key นี้เป็นจุดเริ่มของ trust chain — ETDA อัปเดต key ได้โดยไม่ต้อง redeploy app - ---- - -###### GET /trustlist — ดึงรายชื่อผู้น่าเชื่อถือ - -**ทำอะไร:** Wallet และ DOPA ต่างดึง Trustlist จาก ETDA พร้อมแนบ API Key ยืนยันตัวตน - -**Request ตัวอย่าง:** -```http -GET https://trust.etda.or.th/trustlist -Authorization: Bearer etda-api-key-dopa-2025-xyz -``` - -**Response ตัวอย่าง:** -```json -{ - "iss": "https://trust.etda.or.th", - "iat": 1750000000, - "exp": 1750086400, - "entries": [ - { - "type": "IssuerEntry", - "credential_issuer": "https://issuer.dopa.go.th", - "status": "active", - "jwks_uri": "https://issuer.dopa.go.th/.well-known/jwt-vc-issuer", - "allowed_credential_types": [["VerifiableCredential", "ThaiNationalIDCredential"]] - }, - { - "type": "WalletProviderEntry", - "id": "wp-thangrath-001", - "endpoint": "https://wp.thangrath.go.th", - "status": "active" - } - ] -} -``` - -**หลัง verify JWS แล้ว** Wallet/DOPA เก็บ Trustlist นี้ไว้ใน Local Storage พร้อมบันทึก `exp = 1750086400` (**ตัวอย่างสมมติเท่านั้น** — คำนวณได้ประมาณ 24 ชั่วโมงนับจากนี้ แต่ค่า TTL มาตรฐานจริงยังไม่ถูกกำหนดอย่างเป็นทางการ ดู [§ 1.3 ตาราง "Pull บ่อยแค่ไหน"](../../01-concepts-and-roles/02-roles-and-trust-model.md)) เพื่อรู้ว่าต้องดึงใหม่เมื่อไหร่ - ---- - -###### ⚙️ Wallet Attestation — หลักฐานว่าแอปนี้ผ่านการรับรอง - -**ทำอะไร:** Wallet Provider ออก JWT ให้ Wallet เพื่อใช้เป็นหลักฐานว่าแอปนี้ได้รับการตรวจสอบแล้ว - -**Wallet Attestation JWT ตัวอย่าง (decoded):** -```json -{ - "header": { "alg": "ES256", "typ": "wallet-attestation+jwt", "kid": "wp-key-001" }, - "payload": { - "iss": "wp-thangrath-001", - "sub": "wallet-instance-abc123", - "iat": 1750000000, - "exp": 1750604800, - "cnf": { - "jwk": { - "kty": "EC", "crv": "P-256", - "x": "wallet_pub_x", "y": "wallet_pub_y" - } - }, - "aal": "high", - "attested_security_context": "hardware_backed" - } -} -``` - -> **เปรียบเหมือน:** บัตรพนักงานที่บริษัทออกให้ พิสูจน์ว่าคนถือเป็นพนักงานจริง ไม่ใช่คนปลอม - ---- - -##### 🔵 STEP 1 — Credential Offer (รับคำเชิญ) - -> **Steps:** [1] → [5] - ---- - -**[1] ผู้ใช้กดขอบัตร** - -ผู้ใช้เข้าเว็บไซต์ DOPA และกดปุ่ม "ขอบัตรประชาชนดิจิทัล" ระบบตรวจว่าผู้ใช้ login ถูกต้องก่อน - ---- - -**[2] DOPA สร้างรหัสสำหรับ flow นี้** - -DOPA สร้างข้อมูล 2 ชิ้นสำหรับ session นี้โดยเฉพาะ: - -| ข้อมูล | ค่าตัวอย่าง | อายุ | ใช้ทำอะไร | -|--------|------------|------|-----------| -| `pre_authorized_code` | `pac-a1b2c3d4e5f6` | 5 นาที | รหัสลับที่ Wallet ใช้แลก token | -| `tx_code` (OTP) | `512847` | 5 นาที | ผู้ใช้กรอกเพื่อยืนยันตัวตน | - ---- - -**[3] QR Code บนหน้าจอ** - -QR Code เก็บ Credential Offer Object ซึ่งมีข้อมูลสำคัญ: - -```json -openid-credential-offer://?credential_offer={ - "credential_issuer": "https://issuer.dopa.go.th", - "credential_configuration_ids": ["ThaiNationalIDCredential-config"], - "grants": { - "urn:ietf:params:oauth:grant-type:pre-authorized_code": { - "pre-authorized_code": "pac-a1b2c3d4e5f6", - "tx_code": { "input_mode": "numeric", "length": 6 } - } - } -} -``` - ---- - -**[4] SMS OTP ส่งไปที่มือถือผู้ใช้** - -DOPA ส่ง SMS: `"รหัส OTP สำหรับบัตรประชาชนดิจิทัล: 512847 ใช้ได้ภายใน 5 นาที"` - ---- - -**[5] ผู้ใช้สแกน QR ด้วย Wallet app** - -Wallet อ่าน QR แล้วรู้ว่า: -- ต้องไปขอบัตรที่ `https://issuer.dopa.go.th` -- ต้องใช้ pre_authorized_code `pac-a1b2c3d4e5f6` -- ต้องให้ผู้ใช้กรอก OTP 6 หลัก - ---- - -##### 🟠 STEP 1.5 — Early Trust Check: Issuer - -> **Steps:** [5.5] → [5.7] | **ทำโดย:** Wallet ฝั่งเดียว ใช้ local cache ไม่ต้องมี network call - -**🟠 [5.5–5.7] STEP 1.5 — Early Trust Check: Issuer** -Wallet ตรวจ `credential_issuer` จาก QR ใน Local Trustlist ทันที — fail fast ถ้า Issuer ไม่อยู่ใน TL (ไม่ต้องมี network call — ใช้ local cache) -ตรวจ status = active + อยู่ใน validity window + `credential_configuration_id` อยู่ใน `allowed_credential_types` - ---- - -##### 🔵 STEP 2 — Discovery (ดูข้อมูลผู้ออกบัตร) - -> **Steps:** [6] → [7.1] - ---- - -**[6] GET /.well-known/openid-credential-issuer** - -Wallet ดึงข้อมูลเบื้องต้นของ DOPA Issuer: - -```http -GET https://issuer.dopa.go.th/.well-known/openid-credential-issuer -``` - ---- - -**[7] Credential Issuer Metadata** - -DOPA ตอบกลับด้วยข้อมูลสำคัญ: - -```json -{ - "credential_issuer": "https://issuer.dopa.go.th", - "token_endpoint": "https://issuer.dopa.go.th/token", - "credential_endpoint": "https://issuer.dopa.go.th/credential", - "notification_endpoint": "https://issuer.dopa.go.th/notification", - "credential_configurations_supported": { - "ThaiNationalIDCredential-config": { - "format": "dc+sd-jwt", - "vct": "ThaiNationalIDCredential", - "display": [{ "name": "บัตรประชาชนดิจิทัล", "locale": "th-TH" }], - "claims": { - "credentialSubject": { - "ชื่อ": { "display": [{ "name": "ชื่อ-นามสกุล" }] }, - "เลขบัตร": { "display": [{ "name": "เลขบัตรประชาชน" }] }, - "วันเกิด": { "display": [{ "name": "วันเกิด" }] } - } - } - } - } -} -``` - -Wallet ได้รู้: -- **ส่ง token request ไปที่ไหน:** `/token` -- **ส่ง credential request ไปที่ไหน:** `/credential` -- **บัตรแบบไหนที่ DOPA ออกได้:** `ThaiNationalIDCredential-config` - ---- - -**[7.1] Scope Validation — ตรวจก่อนดำเนินการต่อ** - -Wallet ตรวจว่า `ThaiNationalIDCredential-config` (จาก QR) อยู่ใน `credential_configurations_supported` หรือไม่ - -``` -QR มี: "ThaiNationalIDCredential-config" -Metadata มี: "ThaiNationalIDCredential-config" ✅ → ดำเนินการต่อ -``` - -> ถ้าไม่พบ → Wallet หยุดและแจ้ง error — ป้องกันการส่งคำขอที่ DOPA ไม่รองรับ - ---- - -##### 🔵 STEP 3 — Authentication (ยืนยันตัวตน) - -> **Steps:** [8] → [9] - ---- - -**[8] Wallet ถามผู้ใช้ขอ OTP** - -Wallet แสดง UI: `"กรุณากรอกรหัส OTP 6 หลักที่ได้รับทาง SMS"` - ---- - -**[9] ผู้ใช้กรอก OTP** - -ผู้ใช้กรอก `512847` → Wallet เก็บไว้ใช้ในขั้นตอนถัดไป - -> **ทำไมต้องมี OTP:** พิสูจน์ว่าผู้ใช้เป็นเจ้าของเบอร์โทรที่ผูกกับบัญชี ป้องกันคนอื่นสแกน QR แทน - ---- - -*(ต่อใน [§ 3.2.3 Token Request, Credential Request, ตรวจ Wallet Provider](04-token-and-credential-request.md))* diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/04-token-and-credential-request.md b/docs/trust-guide/03-issuance-flow/02-full-flow/04-token-and-credential-request.md deleted file mode 100644 index 12206bd..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/04-token-and-credential-request.md +++ /dev/null @@ -1,323 +0,0 @@ ---- -sidebar_position: 4 ---- - -# 3.2.3 ขั้นตอนที่ 2 — Token Request, Credential Request, ตรวจ Wallet Provider - -## 3.2.3 Token Request, Credential Request และ Trust Check: Wallet Provider - -ต่อจาก [§ 3.2.2](03-setup-and-credential-offer.md) ที่ผู้ใช้ยืนยันตัวตนด้วย OTP แล้ว บทนี้อธิบาย **STEP 4–5.5** — ตั้งแต่แลก token ไปจนถึง DOPA ตรวจสอบว่า Wallet Provider น่าเชื่อถือก่อนออกบัตร - -##### 🔵 STEP 4 — Token Request (แลกบัตรผ่าน) - -> **Steps:** [9.1] → [12] | **เพิ่ม DPoP สำหรับ LoA ≥ 3** (ดู [§ 3.2.6.1](07-data-flow-and-policy.md#32611-loa-แต่ละระดับหมายถึงอะไร)) - ---- - -**[9.1] สร้าง DPoP Key Pair + DPoP Proof JWT (LoA ≥ 3)** - -> **DPoP คืออะไร (อธิบายสั้น ๆ):** -> access_token ปกติ = Bearer token ใครขโมยไปก็ใช้ได้จากที่ไหนก็ได้ -> DPoP = ผูก token กับ key บนมือถือของเรา ขโมย token ไปใช้จากเครื่องอื่นไม่ได้ - -**สิ่งที่สร้าง:** - -| สิ่งที่สร้าง | เก็บที่ไหน | ส่งไปที่ไหน | -|-------------|-----------|------------| -| DPoP Private Key | Trusted Execution Environment (TEE) | ไม่ส่งออกเลย | -| DPoP Public Key | ฝังใน DPoP Proof JWT header | ส่งไปกับทุก request | -| DPoP Proof JWT | สร้างใหม่ทุก request | ส่งเป็น `DPoP` header | - -**DPoP Proof JWT ตัวอย่าง:** -```json -{ - "header": { - "typ": "dpop+jwt", - "alg": "ES256", - "jwk": { "kty": "EC", "crv": "P-256", "x": "dpop_pub_x", "y": "dpop_pub_y" } - }, - "payload": { - "jti": "unique-per-request-abc123", - "htm": "POST", - "htu": "https://issuer.dopa.go.th/token", - "iat": 1750000100 - } -} -``` - -| Field | ค่า | ทำหน้าที่ | -|-------|-----|----------| -| `typ` | `dpop+jwt` | บอก DOPA ว่านี่คือ DPoP Proof | -| `jwk` | DPoP Public Key | ให้ DOPA verify signature + ผูก token กับ key นี้ | -| `jti` | random unique ID | ป้องกัน replay — ใช้ซ้ำไม่ได้ | -| `htm` | `POST` | ต้องตรงกับ HTTP method ที่ใช้ | -| `htu` | URL ของ endpoint | ต้องตรงกับ endpoint ที่เรียก | -| `iat` | timestamp ปัจจุบัน | ป้องกัน Proof เก่าๆ ถูกนำมาใช้ซ้ำ | - ---- - -**[10] POST /token** - -Wallet ส่ง request พร้อม DPoP-Proof header (LoA ≥ 3): - -```http -POST https://issuer.dopa.go.th/token -Content-Type: application/x-www-form-urlencoded -DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand... ← DPoP Proof (LoA ≥ 3) - -grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code -&pre-authorized_code=pac-a1b2c3d4e5f6 -&tx_code=512847 -``` - ---- - -**[11] DOPA ตรวจหลักฐานทั้งหมด** - -DOPA ตรวจตามลำดับ: - -| # | ตรวจอะไร | ถ้าไม่ผ่าน | -|---|---------|----------| -| 1 | `pre-authorized_code` ถูกต้องและยังไม่หมดอายุ | `invalid_grant` | -| 2 | `tx_code` ตรงกับ OTP ที่ส่งไป | `invalid_grant` | -| 3 | code ถูกใช้ไปแล้วหรือเปล่า (single-use) | `invalid_grant` | -| 4 | **(LoA ≥ 3)** DPoP Proof header มีไหม | `use_dpop_nonce` | -| 5 | **(LoA ≥ 3)** DPoP Proof signature ถูกต้อง | `invalid_dpop_proof` | -| 6 | **(LoA ≥ 3)** DPoP `jti` ยังไม่เคยใช้ | `invalid_dpop_proof` | - -ถ้าผ่านทั้งหมด → DOPA **ผูก access_token กับ DPoP public key** (LoA ≥ 3) - ---- - -**[12] access_token + c_nonce** - -ถ้าผ่านทั้งหมด DOPA ตอบกลับ: - -```json -// LoA ≥ 3 (DPoP) -{ - "access_token": "eyJhbGci...token...", - "token_type": "DPoP", - "expires_in": 300, - "c_nonce": "nonce-random-xyz-789", - "c_nonce_expires_in": 300 -} - -// LoA 1 (Bearer) -{ - "access_token": "eyJhbGci...token...", - "token_type": "Bearer", - "expires_in": 300, - "c_nonce": "nonce-random-xyz-789", - "c_nonce_expires_in": 300 -} -``` - -| ข้อมูล | LoA 1 | LoA ≥ 3 | ใช้ทำอะไร | -|--------|-------|---------|----------| -| `token_type` | `Bearer` | **`DPoP`** | บอก Wallet ว่าต้องแนบ DPoP-Proof ทุก request | -| `access_token` | JWT | JWT (ผูก DPoP key) | ใช้แนบใน Credential Request | -| `c_nonce` | `nonce-random-xyz-789` | เหมือนกัน | ป้องกัน replay ใน proof.jwt | -| `expires_in` | 300 วินาที | เหมือนกัน | access_token หมดอายุใน 5 นาที | - -> **token_type: DPoP** แปลว่า Wallet ต้องสร้าง DPoP Proof ใหม่ทุกครั้งที่ใช้ token นี้ - ---- - -###### 🔵 STEP 5 — Credential Request (ขอรับบัตร) - -> **Steps:** [13] → [14] | **เพิ่ม DPoP-Proof ใหม่สำหรับ LoA ≥ 3** - ---- - -**[13] Wallet สร้าง Key Pair + JWT Proof** - -Wallet สร้าง key คู่บนเครื่อง (Holder Key) แล้วลงนาม JWT Proof: - -**Key Pair ที่สร้าง:** -``` -Private Key → เก็บใน Trusted Execution Environment (TEE) -Public Key → แนบใน proof.jwt เพื่อส่งให้ DOPA -``` - -**JWT Proof (decoded):** -```json -{ - "header": { "alg": "ES256", "typ": "openid4vci-proof+jwt", "kid": "wallet-key-abc" }, - "payload": { - "iss": "wallet-instance-abc123", - "aud": "https://issuer.dopa.go.th", - "iat": 1750000100, - "nonce": "nonce-random-xyz-789", - "cnf": { - "jwk": { "kty": "EC", "crv": "P-256", "x": "wallet_pub_x", "y": "wallet_pub_y" } - } - } -} -``` - -> **ทำไมต้องมี c_nonce ใน proof:** ถ้าไม่มี nonce คนอื่นอาจขโมย proof เก่าไปใช้ซ้ำได้ (Replay Attack) - ---- - -**[13.1] สร้าง DPoP Proof JWT ใหม่ (LoA ≥ 3)** - -DPoP Proof ต้องสร้างใหม่ทุก request (ใช้ซ้ำไม่ได้) โดยเปลี่ยน `htu` ให้ตรงกับ endpoint ที่จะเรียก: - -```json -{ - "payload": { - "jti": "new-unique-id-per-request-789", - "htm": "POST", - "htu": "https://issuer.dopa.go.th/credential", - "iat": 1750000200, - "ath": "SHA256(access_token)" - } -} -``` - -> **`ath` (access token hash):** ผูก DPoP Proof กับ access_token ที่ใช้ — ป้องกันนำ DPoP Proof ไปใช้กับ token อื่น - ---- - -**[14] POST /credential** - -Wallet รวบรวมข้อมูลจากหลาย step ก่อนหน้า แล้วส่งเป็น Credential Request: - -| Field | มาจาก Step | ทำหน้าที่ | -|-------|-----------|----------| -| `Authorization: DPoP {token}` | [12] Token Response | บัตรผ่าน — พิสูจน์ว่า authen แล้ว (LoA 3+: DPoP-bound) | -| `DPoP: {proof}` header | [13.1] สร้างใหม่ทุก request | ผูก request นี้กับ DPoP key — token ที่ขโมยมาใช้ที่นี่ไม่ได้ | -| `credential_configuration_id` | [5] QR Code | ระบุว่าต้องการบัตรประเภทไหน | -| `proof.jwt` | [13] สร้างใหม่ + c_nonce จาก [12] | พิสูจน์ว่า Wallet ควบคุม Holder Key จริง + ป้องกัน replay | -| `wallet_attestation` | SETUP (WP ออกให้ล่วงหน้า) | พิสูจน์ว่าแอปนี้ผ่านการรับรองจาก ETDA | - -**HTTP Request ตัวอย่าง:** - -```http -POST https://issuer.dopa.go.th/credential -Authorization: DPoP eyJhbGci...access_token... -DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand...DPoP_Proof... -Content-Type: application/json - -{ - "credential_configuration_id": "ThaiNationalIDCredential-config", - "proof": { - "proof_type": "jwt", - "jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9wZW5pZDR2Y2ktcHJvb2Yrand..." - }, - "wallet_attestation": "eyJhbGciOiJFUzI1NiIsInR5cCI6IndhbGxldC1hdHRlc3RhdGlvbirand..." -} -``` - -> **ข้อสังเกต:** แต่ละ field มาจาก step ที่ต่างกัน — ถ้าขาด field ใดก็จะ fail ที่ step นั้น -> ลำดับ: SETUP (WA) → [5] QR (cred_id) → [12] Token (access_token) → [13] proof.jwt → [13.1] DPoP Proof - ---- - -###### ⚙️ Trustlist Freshness Check (ก่อน STEP 5.5) - -DOPA ตรวจว่า Trustlist ที่เก็บไว้ยังใช้ได้ไหม: - -``` -ปัจจุบัน: 2025-06-15 10:30:00 -Trustlist exp: 2025-06-15 23:59:59 ← ยังไม่หมด ✅ ใช้ของเดิม -``` - -ถ้าหมดอายุ → ดึงใหม่จาก ETDA ก่อนทำ STEP 5.5 - ---- - -###### 🟠 STEP 5.5 — Trust Check: Wallet Provider (DOPA ตรวจ Wallet) - -> **Steps:** [15] → [21.1] | **ทำโดย:** DOPA ฝั่งเดียว ผู้ใช้ไม่รู้สึกอะไร - ---- - -**[15] แกะ WP ID จาก wallet_attestation** - -DOPA decode Wallet Attestation JWT (ไม่ต้อง verify ก่อน): -``` -iss = "wp-thangrath-001" ← นี่คือ Wallet Provider ID -sub = "wallet-instance-abc123" -``` - ---- - -**[16] Lookup WP ใน Trustlist** - -DOPA ค้นหา `wp-thangrath-001` ใน Local Trustlist: -```json -{ - "id": "wp-thangrath-001", - "endpoint": "https://wp.thangrath.go.th", - "status": "active" ← ✅ พบและ active -} -``` -→ ได้ WP endpoint: `https://wp.thangrath.go.th` - ---- - -**[17] → [18] GET WP Public Key** - -```http -GET https://wp.thangrath.go.th/.well-known/provider-info -``` - -```json -{ - "provider_id": "wp-thangrath-001", - "jwks": { - "keys": [{ - "kid": "wp-key-001", - "kty": "EC", "crv": "P-256", - "x": "wp_pub_x", "y": "wp_pub_y" - }] - } -} -``` - ---- - -**[19] → [20] ตรวจสถานะ Wallet Instance** - -```http -GET https://wp.thangrath.go.th/wallet-registry?wallet_id=wallet-instance-abc123 -``` - -```json -{ - "wallet_id": "wallet-instance-abc123", - "status": "registered", - "valid": true, - "revoked": false -} -``` - -> ถ้า `valid: false` หรือ `revoked: true` → DOPA ปฏิเสธทันที ไม่ออกบัตร - ---- - -**[21] Verify ลายเซ็น wallet_attestation** - -DOPA ตรวจ signature ของ Wallet Attestation JWT ด้วย WP Public Key: -``` -signature ของ WA JWT + WP Public Key → verify สำเร็จ ✅ -หมายความว่า WP ออก WA นี้จริง ไม่ถูกปลอม -``` - ---- - -**[21.1] Self-check credential type** - -DOPA ตรวจว่าตัวเองออก `ThaiNationalIDCredential` ได้ไหม: -``` -credential_configuration_id: "ThaiNationalIDCredential-config" -DOPA allowed_credential_types: ["ThaiNationalIDCredential-config", ...] ✅ -``` - -> **ทำไมต้องตรวจ:** เพื่อป้องกันการขอบัตรประเภทที่ DOPA ไม่ได้รับอนุมัติจาก ETDA - ---- - -*(ต่อใน [§ 3.2.4 Issue VC, ตรวจ Issuer, Notification](05-issue-and-notify.md))* diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/05-issue-and-notify.md b/docs/trust-guide/03-issuance-flow/02-full-flow/05-issue-and-notify.md deleted file mode 100644 index d299ace..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/05-issue-and-notify.md +++ /dev/null @@ -1,309 +0,0 @@ ---- -sidebar_position: 5 ---- - -# 3.2.4 ขั้นตอนที่ 3 — Issue VC, ตรวจ Issuer, Notification - -## 3.2.4 Issue VC, Trust Check: Issuer และ Notification - -ต่อจาก [§ 3.2.3](04-token-and-credential-request.md) ที่ DOPA ตรวจ Wallet Provider ผ่านแล้ว บทนี้อธิบาย **STEP 6–7** — ตั้งแต่ DOPA ออก SD-JWT VC, Wallet ตรวจสอบ Issuer ก่อนบันทึกบัตร ไปจนถึงการแจ้งเตือนกลับ (Optional) - -###### 🔵 STEP 6 — Issue VC (ออกบัตรประชาชนดิจิทัล) - -> **Steps:** [22] → [25] - ---- - -**[22] DOPA สร้าง SD-JWT VC (IETF SD-JWT VC (dc+sd-jwt))** - -DOPA สร้าง SD-JWT VC ที่สอดคล้อง IETF SD-JWT VC (dc+sd-jwt): - -**JOSE Header:** -```json -{ - "typ": "dc+sd-jwt", - "alg": "ES256", - "kid": "https://issuer.dopa.go.th#key-2025-1" -} -``` - -**JWT Payload:** -```json -{ - "iss": "https://issuer.dopa.go.th", - "sub": "wallet-instance-abc123", - "jti": "https://credentials.dopa.go.th/credentials/tid-2025-00042", - "nbf": 1750000200, - "exp": 1781536200, - "cnf": { "jwk": { "kty": "EC", "crv": "P-256", "x": "wallet_pub_x", "y": "wallet_pub_y" } }, - "_sd_alg": "sha-256", - "status": { - "status_list": { - "idx": 42, - "uri": "https://status.dopa.go.th/statuslist/1" - } - }, - "@context": [ - "https://www.w3.org/ns/credentials/v2", - "https://vocab.dopa.go.th/credentials/v1" - ], - "id": "https://credentials.dopa.go.th/credentials/tid-2025-00042", - "type": ["VerifiableCredential", "ThaiNationalIDCredential"], - "issuer": { - "id": "https://issuer.dopa.go.th", - "name": "กรมการปกครอง" - }, - "validFrom": "2025-06-15T00:00:00Z", - "validUntil": "2030-06-15T00:00:00Z", - "credentialSubject": { - "id": "wallet-instance-abc123", - "ชื่อ": "สมชาย ใจดี", - "เลขบัตร": "1234567890123", - "วันเกิด": "1990-01-15" - }, - "credentialSchema": { - "id": "https://schema.dopa.go.th/ThaiNationalID-v1.json", - "type": "JsonSchema" - }, - "_sd": [ - "X9yH3mK2vP...digest_ที่อยู่", - "aB7cD4eF1g...digest_รูปภาพ" - ] -} -``` - -**SD Disclosures** (สำหรับ claims ที่ซ่อน): -``` -~base64url(["kp_salt1", "ที่อยู่", "123 ถนนสุขุมวิท กรุงเทพฯ 10110"]) -~base64url(["kp_salt2", "รูปภาพ", "base64url_portrait..."]) -``` - ---- - -**[23] Immediate vs Deferred Issuance** - -| กรณี | ความหมาย | Response | -|------|---------|----------| -| **Immediate** | ออกบัตรทันที (ปกติ) | `{ "credential": "eyJ...SD-JWT..." }` | -| **Deferred** | ต้องรอ (เช่น ต้องตรวจสอบเพิ่ม) | `{ "transaction_id": "txn-dopa-456" }` | - -**Deferred flow — Polling Parameters:** - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** ETDA ยังไม่ได้ประกาศตัวเลขที่แน่นอนสำหรับ polling interval/timeout — ค่าด้านล่างเป็นแนวทางชั่วคราว (interim) อ้างอิงแนวปฏิบัติทั่วไปของ OID4VCI Deferred Flow - -| พารามิเตอร์ | ค่าแนะนำชั่วคราว | -|-------------|----------------------| -| Polling interval ขั้นต่ำ | ทุก 5 วินาที (ไม่ควร poll ถี่กว่านี้เพื่อลดภาระ Gateway) | -| Timeout รวม | 10 นาที นับจากคำขอ deferred ครั้งแรก | -| Max retry | ไม่จำกัดจำนวนครั้งภายใน timeout รวม แต่ **SHOULD** เว้นระยะตาม polling interval | -| Error response เมื่อตรวจสอบไม่ผ่าน | `{ "error": "credential_request_denied" }` พร้อมเหตุผลใน `error_description` (ถ้ามี) | -| Error response เมื่อ transaction หมดอายุ (เกิน timeout) | `{ "error": "invalid_transaction_id" }` — Wallet ต้องเริ่ม flow การขอ credential ใหม่ | - -**Deferred flow** — Wallet ต้องกลับมาถามซ้ำ: -```http -POST /deferred_credential -{ "transaction_id": "txn-dopa-456" } -→ DOPA ตรวจสอบเสร็จแล้ว → ส่ง credential กลับมา -``` - ---- - -###### ⚙️ Trustlist Freshness Check (ก่อน STEP 6.5) - -Wallet ตรวจว่า Trustlist ยังใช้ได้หรือไม่ (เหมือนที่ DOPA ทำในขั้น 5.5) ถ้าหมดอายุ → ดึงใหม่ก่อน - ---- - -###### 🟠 STEP 6.5 — Trust Check: Issuer (Wallet ตรวจบัตร) - -> **Steps:** [26] → [30.7] | **ทำโดย:** Wallet ฝั่งเดียว ผู้ใช้ยังรอผลอยู่ - ---- - -**ตารางสรุป: แต่ละ step หยิบค่าจากไหน เอาไปเช็คกับอะไร** - -| Step | หยิบค่าจาก | เช็คกับ | ผ่านเมื่อ | -|------|-----------|---------|----------| -| **[26]** แกะ claims | JWT payload | — | decode สำเร็จ | -| **[27]** Trustlist lookup | `iss` จาก [26] | `Trustlist entry.credential_issuer` | พบ + status = active | -| **[27.1]** iss/jti match | `iss`, `jti` จาก JWT | `issuer.id`, `id` ใน VC body | ทั้งคู่ต้องเท่ากัน | -| **[27.2]** @context check | `@context[0]` จาก VC body | `"https://www.w3.org/ns/credentials/v2"` | ตรงทั้งหมด | -| **[27.3]** type check | `type[0]`, `type[1]` จาก VC body | `"VerifiableCredential"` + Trustlist `allowed_credential_types` | ทั้งสองพบ | -| **[28]** GET JWKS | `credential_issuer` จาก Trustlist | — | HTTP 200 | -| **[29]** รับ JWKS | JWKS response | — | parse สำเร็จ | -| **[29.1]** kid match | `kid` จาก JOSE header | `JWKS keys[].kid` | พบ key → ได้ `publicKeyJwk` | -| **[30]** verify signature | SD-JWT signature | `publicKeyJwk` จาก [29.1] | ES256 verify สำเร็จ | -| **[30.1]** nbf/sub | `nbf` (Unix) และ `sub` จาก JWT | `validFrom` (ISO 8601) และ `credentialSubject.id` ใน VC body | ตรงกัน | -| **[30.2]** validity window | `validFrom`, `validUntil` จาก VC body | เวลาปัจจุบัน | validFrom ≤ now ≤ validUntil | -| **[30.3]** GET statuslist | `status.status_list.uri` จาก JWT | — | HTTP 200 | -| **[30.4]** รับ statuslist+jwt | response | — | parse JWT สำเร็จ | -| **[30.5]** verify statuslist | statuslist+jwt signature | `publicKeyJwk` ของ Issuer | verify สำเร็จ + `iss` ตรงกับ credential issuer | -| **[30.6]** validity statuslist | `iat`, `exp` จาก statuslist+jwt | now | iat ≤ now ≤ exp | -| **[30.7]** decode bit | `lst` ที่ index `status.status_list.idx` | 0 | bit = 0 (valid) | - ---- - -**[26] แกะ claims จาก JWT payload** - -Wallet decode JWT (ยังไม่ verify signature) ได้ค่า 3 ตัวที่จะใช้ต่อในขั้นตอนถัดไป: - -``` -iss = "https://issuer.dopa.go.th" → ใช้ใน [27] Trustlist lookup -jti = "https://credentials.dopa.go.th/credentials/tid-2025-00042" → ใช้ใน [27.1] -sub = "wallet-instance-abc123" → ใช้ใน [30.1] -``` - ---- - -**[27] Trustlist lookup** - -ค้น `iss` ใน Local Trustlist → ได้ entry ของ DOPA: - -```json -{ - "credential_issuer": "https://issuer.dopa.go.th", - "status": "active", - "allowed_credential_types": [["VerifiableCredential", "ThaiNationalIDCredential"]] -} -``` - ---- - -**[27.1] iss == issuer.id และ jti == id** - -ตรวจว่า JWT layer กับ VC body ไม่ถูกสลับกัน: - -``` -JWT iss = "https://issuer.dopa.go.th" -VC issuer.id = "https://issuer.dopa.go.th" ✅ - -JWT jti = "https://credentials.dopa.go.th/credentials/tid-2025-00042" -VC id = "https://credentials.dopa.go.th/credentials/tid-2025-00042" ✅ -``` - -> ป้องกันการนำ JWT ของ issuer หนึ่งไปจับคู่กับ VC body ของอีก issuer - ---- - -**[27.2] ตรวจ @context** - -``` -VC @context[0] = "https://www.w3.org/ns/credentials/v2" ✅ -``` -พิสูจน์ว่าบัตรนี้เป็น IETF SD-JWT VC (dc+sd-jwt) — ถ้าไม่มี = format ผิด reject ทันที - ---- - -**[27.3] ตรวจ type** - -``` -VC type[0] = "VerifiableCredential" ✅ บังคับต้องมี -VC type[1] = "ThaiNationalIDCredential" -Trustlist allowed_credential_types: [["VerifiableCredential", "ThaiNationalIDCredential"]] ✅ -``` -พิสูจน์ว่า DOPA ได้รับอนุมัติจาก ETDA ให้ออกบัตรประเภทนี้ - ---- - -**[28–29.1] GET JWKS และ kid matching** - -``` -GET https://issuer.dopa.go.th/.well-known/jwt-vc-issuer - -JOSE header kid: "https://issuer.dopa.go.th#key-2025-1" -JWKS keys[0].kid: "https://issuer.dopa.go.th#key-2025-1" ✅ ตรงกัน -→ ดึง publicKeyJwk ของ keys[0] ไปใช้ verify signature -``` - -> ถ้า DOPA มีหลาย key (ระหว่าง key rotation) — kid ช่วยระบุว่าใช้ key ไหนเซ็น - ---- - -**[30] Verify SD-JWT signature** - -``` -SD-JWT signature + publicKeyJwk (จาก [29.1]) → ES256 verify → ✅ -``` -พิสูจน์ว่า DOPA เซ็นบัตรนี้จริง และไม่มีใครแก้ไข payload ภายหลัง - ---- - -**[30.1] ตรวจ nbf กับ validFrom และ sub กับ credentialSubject.id** - -``` -JWT nbf: 1750000200 (Unix) -VC validFrom: "2025-06-15T00:00:03Z" (ISO 8601) ✅ ตรงกัน (±30 วินาที) - -JWT sub: "wallet-instance-abc123" -VC credentialSubject.id: "wallet-instance-abc123" ✅ ตรงกัน -``` - ---- - -**[30.2] ตรวจ validity window** - -``` -validFrom: 2025-06-15T00:00:00Z ≤ now: 2025-06-15T10:30:00Z ≤ validUntil: 2030-06-15T00:00:00Z ✅ -``` - ---- - -**[30.3–30.7] ตรวจ IETF Token Status List** - -``` -status.status_list.uri = "https://status.dopa.go.th/statuslist/1" -status.status_list.idx = 42 - -GET https://status.dopa.go.th/statuslist/1 → statuslist+jwt -→ verify signature (Issuer key เดิม) ✅ -→ iss = "https://issuer.dopa.go.th" ✅ ตรงกับ credential issuer -→ iat ≤ now ≤ exp ✅ -→ decode lst → bit ที่ index 42 = 0 (VALID) ✅ บัตรยังไม่ถูกยกเลิก -``` - ---- - -##### 🔵 STEP 7 — Notification (แจ้งว่ารับแล้ว, Optional) - -> **Steps:** [31] → [33] - ---- - -**[31] POST /notification** - -```http -POST https://issuer.dopa.go.th/notification -Authorization: Bearer eyJhbGci... -Content-Type: application/json - -{ - "notification_id": "tid-2025-00042", - "event": "credential_accepted" -} -``` - ---- - -**[32] DOPA รับทราบ** - -```http -HTTP/1.1 204 No Content -``` - -> DOPA บันทึกว่าบัตรถูกรับไปแล้ว ใช้สำหรับ audit log และ analytics - ---- - -**[33] ✅ บันทึกบัตรสำเร็จ** - -Wallet แสดงผล: `"บัตรประชาชนดิจิทัลของคุณพร้อมใช้งานแล้ว"` - -ผู้ใช้ได้บัตรที่: -- **ออกโดย DOPA จริง** — ตรวจ signature แล้ว -- **ผ่านการรับรองจาก ETDA** — ทั้ง DOPA และ Wallet อยู่ใน Trustlist -- **ยังไม่ถูกยกเลิก** — ตรวจ status list แล้ว -- **ผูกกับ Wallet นี้** — ด้วย cnf.jwk (ใครขโมยไปก็ใช้ไม่ได้) - ---- - -*(ต่อใน [§ 3.2.5 Trustlist Verification — รายละเอียดการตรวจสอบ](06-verification-details.md))* diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/06-verification-details.md b/docs/trust-guide/03-issuance-flow/02-full-flow/06-verification-details.md deleted file mode 100644 index abf04ad..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/06-verification-details.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -sidebar_position: 6 ---- - -# 3.2.5 Trustlist Verification — รายละเอียดการตรวจสอบ (STEP 5.5 และ 6.5) - -## 3.2.5 Trustlist Verification — รายละเอียดการตรวจสอบ (STEP 5.5 และ 6.5) - -ต่อจาก [§ 3.2.4](05-issue-and-notify.md) ที่กล่าวถึงจุดตรวจ Trustlist โดยสรุปแล้ว บทนี้ขยายรายละเอียดของจุดตรวจทั้ง 2 จุด (STEP 5.5 และ 6.5) แบบ step-by-step พร้อม diagram เต็ม - -ใน flow การออกบัตรประชาชนดิจิทัล มีจุดตรวจ Trustlist อยู่ **2 จุด** โดยแต่ละฝ่ายตรวจคนละบทบาท - -| จุดตรวจ | ใครตรวจ | ตรวจอะไร | เกิดขึ้นตอนไหน | -|---------|---------|---------|----------------| -| STEP 5.5 | DOPA (Issuer) | Wallet Provider น่าเชื่อถือไหม? | ก่อนออกบัตร | -| STEP 6.5 | Wallet | DOPA (Issuer) น่าเชื่อถือไหม? | หลังรับบัตรมา | - ---- - -##### STEP 5.5 — DOPA ตรวจ Wallet Provider - -ก่อนออกบัตรให้ DOPA ต้องมั่นใจว่า Wallet ที่ขอรับบัตรเป็น Wallet ที่ ETDA รับรองแล้ว - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart TD - classDef input fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a - classDef check fill:#fef9c3,stroke:#ca8a04,color:#713f12 - classDef pass fill:#dcfce7,stroke:#16a34a,color:#14532d - classDef fail fill:#fee2e2,stroke:#dc2626,color:#7f1d1d - classDef store fill:#f3f4f6,stroke:#6b7280,color:#374151 - - WA["wallet_attestation JWT - (แนบมาใน Credential Request)"]:::input - - TL[("Trustlist\n(DOPA เก็บไว้ใน Local Storage)")]:::store - - S1["① แกะ wallet_attestation\nได้ iss = Wallet Provider ID"]:::check - S2["② Lookup WP ID ใน Trustlist\nตรวจว่า status = active?"]:::check - S3["③ ดึง WP endpoint จาก Trustlist\nGET {endpoint}/.well-known/provider-info"]:::check - S4["④ ได้ WP Public Key"]:::check - S5["⑤ GET /wallet-registry?wallet_id=...\nตรวจสถานะ Wallet Instance"]:::check - S6["⑥ Verify ลายเซ็น wallet_attestation\nด้วย WP Public Key"]:::check - S7["⑦ ตรวจ aal และ security context\nอยู่ใน allowed_aal ของ entry?"]:::check - S8["⑧ Self-check — credential_configuration_id\nอยู่ใน Issuer allowed_credential_types?"]:::check - - OK["✅ ผ่านทุกข้อ\nออกบัตรได้"]:::pass - F1["❌ WP ไม่อยู่ใน Trustlist\nหรือ status ≠ active"]:::fail - F2["❌ Wallet ถูก revoke\nหรือไม่ได้ลงทะเบียน"]:::fail - F3["❌ ลายเซ็น wallet_attestation\nไม่ถูกต้อง"]:::fail - F4["❌ aal ไม่ผ่านเกณฑ์"]:::fail - F5["❌ ประเภท credential ไม่ได้รับอนุมัติ"]:::fail - - WA --> S1 - TL --> S2 - S1 --> S2 - S2 -->|"ไม่พบ หรือ inactive"| F1 - S2 -->|"พบ และ active"| S3 --> S4 --> S5 - S5 -->|"Wallet ถูก revoke"| F2 - S5 -->|"Wallet valid"| S6 - S6 -->|"ลายเซ็นผิด"| F3 - S6 -->|"ลายเซ็นถูก"| S7 - S7 -->|"aal ไม่ผ่าน"| F4 - S7 -->|"ผ่าน"| S8 - S8 -->|"ไม่อยู่ใน list"| F5 - S8 -->|"ผ่าน"| OK -``` - -**ข้อมูลที่ตรวจเทียบกับ Trustlist entry ของ WP:** - -| ข้อมูลจาก wallet_attestation | เทียบกับ Trustlist entry | ต้องผ่าน | -|------------------------------|--------------------------|----------| -| `iss` (WP ID) | `entry.id` | พบและ status = active | -| ลายเซ็น JWT | `entry.public_key` (จาก WP endpoint) | verify สำเร็จ | -| `aal` (assurance level) | `entry.allowed_aal` | อยู่ใน list | -| `attested_security_context` | `entry.allowed_security_contexts` | อยู่ใน list | -| Wallet Instance ID | WP Registry | registered และ valid | -| `credential_configuration_id` (จาก Credential Request) | `Issuer.allowed_credential_types` | อยู่ใน list | - ---- - -##### STEP 6.5 — Wallet ตรวจ Issuer (DOPA) - -หลังได้รับบัตรมาแล้ว Wallet ต้องมั่นใจว่าบัตรนี้ออกโดย DOPA ที่ ETDA รับรองจริง และลายเซ็นไม่ถูกปลอมแปลง - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart TD - classDef input fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a - classDef check fill:#fef9c3,stroke:#ca8a04,color:#713f12 - classDef pass fill:#dcfce7,stroke:#16a34a,color:#14532d - classDef fail fill:#fee2e2,stroke:#dc2626,color:#7f1d1d - classDef store fill:#f3f4f6,stroke:#6b7280,color:#374151 - classDef ietf fill:#fde8d8,stroke:#c2410c,color:#7c2d12 - - VC["SD-JWT VC\n(IETF SD-JWT VC (dc+sd-jwt), typ:dc+sd-jwt)"]:::input - - TL_W[("Trustlist\n(Wallet Local Storage)")]:::store - JWKS_E[("JWKS Endpoint\nhttps://issuer.dopa.go.th\n/.well-known/jwt-vc-issuer")]:::store - SL_E[("statuslist+jwt\nhttps://status.dopa.go.th/statuslist/1")]:::ietf - - V1["① แกะ iss, jti, sub\nจาก JWT payload"]:::check - V2["② Lookup iss ใน Trustlist\nstatus = active?"]:::check - V2b["③ iss == issuer.id?\njti == id?"]:::check - V3a["④ @context[0] ==\nhttps://www.w3.org/ns/credentials/v2?"]:::check - V3b["⑤ type[0] == VerifiableCredential?\ntype[1] ∈ allowed_credential_types?"]:::check - V4["⑥ GET JWKS Endpoint"]:::check - V5b["⑦ kid matching\nหา key ใน JWKS ที่ kid ตรง\n→ publicKeyJwk"]:::check - V6["⑧ Verify SD-JWT signature\nด้วย publicKeyJwk"]:::check - V7["⑨ nbf == validFrom?\nvalidFrom ≤ now ≤ validUntil?\nsub == credentialSubject.id?"]:::check - V8["⑩ GET statuslist+jwt\nAccept: application/statuslist+jwt"]:::check - V9["⑪ Verify statuslist+jwt\niss == credential iss?\ntyp == statuslist+jwt? sub == uri?"]:::check - V10["⑫ Decode lst\nbit[status_list.idx] == 0?"]:::check - - OK2["✅ ผ่านทุกข้อ\nบันทึกบัตรลง Wallet"]:::pass - F1["❌ Issuer ไม่อยู่\nใน Trustlist"]:::fail - F2["❌ iss/jti\nไม่ตรงกัน"]:::fail - F3["❌ @context ผิด\nไม่ใช่ VC DM 2.0"]:::fail - F4["❌ type ผิด\nหรือไม่ได้รับอนุมัติ"]:::fail - F5["❌ JWKS ล้มเหลว"]:::fail - F6["❌ kid ไม่พบ\nใน JWKS"]:::fail - F7["❌ Signature\nไม่ถูกต้อง"]:::fail - F8["❌ Validity\nไม่ผ่าน"]:::fail - F9["❌ statuslist+jwt\nverify ล้มเหลว"]:::fail - F10["❌ Revoked\nbit = 1"]:::fail - - VC --> V1 - TL_W --> V2 - V1 --> V2 - V2 -->|"ไม่พบ"| F1 - V2 -->|"active"| V2b - V2b -->|"ไม่ตรง"| F2 - V2b -->|"ตรง"| V3a - V3a -->|"ผิด"| F3 - V3a -->|"ถูก"| V3b - V3b -->|"ผิด"| F4 - V3b -->|"ถูก"| V4 - JWKS_E --> V4 - V4 -->|"ล้มเหลว"| F5 - V4 -->|"สำเร็จ"| V5b - V5b -->|"kid ไม่พบ"| F6 - V5b -->|"พบ"| V6 - V6 -->|"ล้มเหลว"| F7 - V6 -->|"สำเร็จ"| V7 - V7 -->|"ไม่ผ่าน"| F8 - V7 -->|"ผ่าน"| V8 - SL_E --> V8 - V8 --> V9 - V9 -->|"ล้มเหลว"| F9 - V9 -->|"ผ่าน"| V10 - V10 -->|"bit=1"| F10 - V10 -->|"bit=0"| OK2 -``` - -**ข้อมูลที่ตรวจเทียบกับ Trustlist entry ของ Issuer:** - -| ข้อมูลจาก SD-JWT VC | เทียบกับ Trustlist entry | ต้องผ่าน | -|---------------------|--------------------------|----------| -| `iss` | Trustlist `entry.credential_issuer` + == `issuer.id` | พบ + active + ตรง | -| `jti` | VC `id` field | `jti == id` (ต้องเท่ากัน) | -| `sub` | `credentialSubject.id` | `sub == credentialSubject.id` | -| `@context[0]` | `https://www.w3.org/ns/credentials/v2` | ต้องตรง | -| `type[0]` / `type[1]` | `"VerifiableCredential"` / `entry.allowed_credential_types` | ทั้งคู่พบ | -| `kid` (JOSE header) | JWKS `keys[].kid` | พบ key | -| SD-JWT signature | `publicKeyJwk` จาก JWKS | verify สำเร็จ | -| `nbf` ↔ `validFrom` | Unix ↔ ISO 8601 | ต้องตรงกัน | -| `validFrom` / `validUntil` | เวลาปัจจุบัน | `validFrom ≤ now ≤ validUntil` | -| `status.status_list.idx` | bit ใน `statuslist+jwt` | bit = 0 (valid) | - ---- - -##### เปรียบเทียบ: ตรวจ Trustlist vs ไม่ตรวจ - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -graph LR - subgraph no_tl["❌ ไม่มี Trustlist"] - N1["Wallet รับบัตรจากใครก็ได้"] - N2["DOPA ออกบัตรให้ Wallet ใดก็ได้"] - N3["ไม่มีใครการันตีว่าใครน่าเชื่อถือ"] - end - - subgraph with_tl["✅ มี Trustlist (ETDA)"] - W1["DOPA ตรวจก่อนออกบัตร\nว่า Wallet ผ่านมาตรฐาน ETDA"] - W2["Wallet ตรวจก่อนรับบัตร\nว่า DOPA ได้รับอนุมัติจาก ETDA"] - W3["ทั้งสองฝ่ายการันตีโดย ETDA"] - end -``` - ---- - -*(ต่อใน [§ 3.2.6 Data Flow และ LoA/DPoP Policy](07-data-flow-and-policy.md))* diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/07-data-flow-and-policy.md b/docs/trust-guide/03-issuance-flow/02-full-flow/07-data-flow-and-policy.md deleted file mode 100644 index d9a50c9..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/07-data-flow-and-policy.md +++ /dev/null @@ -1,397 +0,0 @@ ---- -sidebar_position: 7 ---- - -# 3.2.6 Data Flow และ LoA/DPoP Policy — รายละเอียดเพิ่มเติม - -## 3.2.6 Data Flow และ LoA/DPoP Policy - -ต่อจาก [§ 3.2.5](06-verification-details.md) ที่อธิบายจุดตรวจ Trustlist แบบละเอียดแล้ว บทนี้สรุปว่าแต่ละ STEP ใช้ข้อมูลอะไรบ้าง (Data Flow) และอธิบายนโยบาย LoA/DPoP ที่ควบคุมความเข้มของการป้องกัน token — เป็นบทสุดท้ายของ Issuance Flow (บทที่ 3) - -##### ● SETUP — เตรียมข้อมูลก่อนเริ่ม - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - classDef req fill:#d4edda,stroke:#28a745,color:#155724 - classDef store fill:#e2e3e5,stroke:#6c757d,color:#343a40 - classDef src fill:#cce5ff,stroke:#0056b3,color:#003d80 - classDef key fill:#fff3cd,stroke:#ffc107,color:#856404 - classDef pin fill:#f3e8ff,stroke:#7c3aed,color:#4c1d95 - - ETDA[("🛡️ ETDA")]:::src - WP_SRC[("🏢 Wallet Provider")]:::src - - ETDA_PK["🔐 ETDA Public Key\n(pre-pinned ตอน install/deploy)\nใช้ verify Trustlist JWS"]:::pin - APIKEY["🔑 API Key\n(ออกโดย ETDA หลังลงทะเบียน)"]:::key - - TL["Trustlist + TTL -• WP ID → endpoint -• Issuer DID list -• Verifier DID list -• exp (วันหมดอายุ)"]:::req - WA["Wallet Attestation JWT -• iss: WP ID -• sub: Wallet Instance ID -• cnf.jwk: Wallet PubKey -• aal: assurance level"]:::req - - W_LS[("📱 Wallet\nLocal Storage")]:::store - D_LS[("🏛️ DOPA\nLocal Storage")]:::store - - ETDA_PK -->|"pre-installed"| W_LS & D_LS - APIKEY -->|"Authorization: Bearer"| ETDA - ETDA --> TL - TL -->|"✅ จำเป็น"| W_LS & D_LS - WP_SRC --> WA - WA -->|"✅ จำเป็น"| W_LS -``` - -| ข้อมูล | มาจาก | จำเป็น | ใช้เมื่อไหน | -|--------|-------|--------|----------| -| ETDA Public Key | pre-pinned ตอน install/deploy | ✅ | SETUP — verify Trustlist JWS signature | -| API Key | ETDA (ออกให้หลังลงทะเบียน) | ✅ | SETUP — ใช้ดึง Trustlist (Authorization header) | -| Trustlist | ETDA | ✅ | STEP 5.5 และ 6.5 (lookup) | -| Wallet Attestation JWT | Wallet Provider | ✅ | STEP 5 (แนบใน Credential Request) | - ---- - -##### ● STEP 1–3 — Credential Offer + Discovery + OTP - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - classDef req fill:#d4edda,stroke:#28a745,color:#155724 - classDef opt fill:#fff3cd,stroke:#ffc107,color:#856404 - classDef src fill:#cce5ff,stroke:#0056b3,color:#003d80 - - DOPA[("🏛️ DOPA")]:::src - - QR["Credential Offer (QR) -• credential_issuer URL ✅ -• credential_configuration_ids ✅ -• pre_authorized_code ✅"]:::req - - OTP["SMS OTP = tx_code ✅"]:::req - - META["Credential Issuer Metadata -• token_endpoint ✅ -• credential_endpoint ✅ -• credential_configurations_supported ✅ -• display (UI) ⚠️"]:::req - - DOPA --> QR & OTP - DOPA -->|"GET /.well-known/\nopenid-credential-issuer"| META -``` - -| ข้อมูล | มาจาก | จำเป็น | ใช้เมื่อไหน | -|--------|-------|--------|----------| -| `pre_authorized_code` | QR Code | ✅ | STEP 4 (POST /token) | -| `tx_code` (OTP) | SMS | ✅ | STEP 4 (POST /token) | -| `token_endpoint` | Credential Issuer Metadata | ✅ | STEP 4 (URL ที่จะ call) | -| `credential_endpoint` | Credential Issuer Metadata | ✅ | STEP 5 (URL ที่จะ call) | -| `credential_configuration_id` | QR Code (`credential_configuration_ids`) | ✅ | STEP 5 (ระบุประเภทบัตรใน Credential Request), [7.1] ตรวจกับ Metadata | -| `display` | Credential Issuer Metadata | ⚠️ เสริม | แสดงชื่อ/โลโก้ใน UI เท่านั้น | - ---- - -##### ● STEP 4–5 — Token Request + Credential Request - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart TD - classDef req fill:#d4edda,stroke:#28a745,color:#155724 - classDef store fill:#e2e3e5,stroke:#6c757d,color:#343a40 - - subgraph token_req["POST /token"] - TR["grant_type: pre-authorized_code ✅\npre_authorized_code ✅\ntx_code (OTP) ✅"]:::req - end - - subgraph token_res["ผล Token Response"] - AT["access_token ✅"]:::req - CN["c_nonce ✅"]:::req - end - - subgraph cred_req["POST /credential"] - CR["Bearer: access_token ✅\ncredential_configuration_id ✅\nproof.jwt ✅\nwallet_attestation ✅"]:::req - end - - subgraph proof_build["🔐 Wallet สร้าง proof.jwt"] - KP["Key Pair\n(Private Key เก็บในเครื่อง) ✅"]:::req - JP["JWT signed\nด้วย Private Key + c_nonce ✅"]:::req - KP --> JP - end - - QR_S[("จาก QR (STEP 1)")]:::store --> TR - OTP_S[("จาก SMS (STEP 3)")]:::store --> TR - TR --> AT & CN - CN --> JP - AT --> CR - JP --> CR - WA_S[("จาก SETUP")]:::store --> CR - META_S[("จาก Metadata (STEP 2)")]:::store --> CR -``` - -| ข้อมูล | มาจาก | จำเป็น | หมายเหตุ | -|--------|-------|--------|----------| -| `access_token` | Token Response | ✅ | บัตรผ่านเข้า /credential | -| `token_type` | Token Response | ✅ | `DPoP` (LoA ≥ 3) หรือ `Bearer` (LoA 1) | -| `c_nonce` | Token Response | ✅ | ป้องกัน replay ใน proof | -| DPoP key pair | สร้างใน step [9.1] | ✅ (LoA ≥ 3) | private key เก็บใน Trusted Execution Environment (TEE) | -| DPoP Proof JWT | Wallet สร้างใหม่ทุก request | ✅ (LoA ≥ 3) | แนบเป็น `DPoP` header | -| Private Key | สร้างในเครื่อง | ✅ | ผูกบัตรกับ Wallet นี้ | -| `proof.jwt` | Wallet สร้างเอง | ✅ | พิสูจน์ควบคุม key | -| `credential_configuration_id` | QR Code (STEP 1) | ✅ | ระบุประเภทบัตรใน Credential Request | -| `wallet_attestation` | SETUP | ✅ | DOPA ใช้ตรวจ WP ใน STEP 5.5 | - ---- - -##### ● STEP 5.5 — Trustlist Verification: Wallet Provider - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - classDef req fill:#d4edda,stroke:#28a745,color:#155724 - classDef check fill:#fff3cd,stroke:#ffc107,color:#856404 - classDef store fill:#e2e3e5,stroke:#6c757d,color:#343a40 - classDef pass fill:#d4edda,stroke:#28a745,color:#155724 - classDef fail fill:#f8d7da,stroke:#721c24,color:#721c24 - - WA["wallet_attestation -(INPUT)"]:::req - TL[("🏛️ DOPA\nLocal Trustlist")]:::store - WP[("🏢 Wallet Provider")]:::store - - C1["① WP ID"]:::check - C2["② WP endpoint"]:::check - C3["③ WP Public Key"]:::check - C4["④ Wallet status"]:::check - C5{"⑤ Verify -signature"}:::check - C6["⑥ Self-check -credential_configuration_id"]:::check - - OK["✅ → ออกบัตร"]:::pass - NG["❌ → ปฏิเสธ (sig)"]:::fail - NG2["❌ → ปฏิเสธ (type)"]:::fail - - WA --> C1 - TL -->|"ใช้ lookup"| C2 - C1 --> C2 --> C3 - WP --> C3 & C4 - C3 & C4 --> C5 - C5 -->|"ผ่าน"| C6 - C5 -->|"ล้มเหลว"| NG - C6 -->|"อนุมัติ"| OK - C6 -->|"ไม่อนุมัติ"| NG2 -``` - -| ข้อมูล | มาจาก | ตรวจอะไร | -|--------|-------|----------| -| WP ID | wallet_attestation | ใช้ lookup ใน Trustlist | -| WP endpoint | Local Trustlist | URL ที่จะถาม WP | -| WP Public Key | GET WP endpoint | ใช้ verify ลายเซ็น WA | -| Wallet status | GET /wallet-registry | ตรวจว่า Wallet ยังใช้งานได้ไหม | -| `credential_configuration_id` | Credential Request | ตรวจ Issuer allowed_credential_types (self-check ⑥) | - ---- - -##### ● STEP 6–6.5 — Issue VC + Trustlist Verification: Issuer - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - classDef req fill:#d4edda,stroke:#28a745,color:#155724 - classDef opt fill:#fff3cd,stroke:#ffc107,color:#856404 - classDef check fill:#fff3cd,stroke:#ffc107,color:#856404 - classDef store fill:#e2e3e5,stroke:#6c757d,color:#343a40 - classDef pass fill:#d4edda,stroke:#28a745,color:#155724 - classDef fail fill:#f8d7da,stroke:#721c24,color:#721c24 - - subgraph vc["SD-JWT VC ที่ DOPA สร้าง (IETF SD-JWT VC (dc+sd-jwt))"] - VC_C["— JOSE Header —\ntyp:dc+sd-jwt ✅ alg:ES256 ✅ kid:... ✅\n— JWT Claims —\niss, sub, jti, nbf, exp, cnf.jwk ✅\n_sd_alg:sha-256 ✅\nstatus.status_list:{idx:42, uri:...} ✅\n— VC Body (IETF SD-JWT VC (dc+sd-jwt)) —\n@context:[w3.org/ns/credentials/v2,...] ✅\ntype:[VerifiableCredential, ThaiNationalIDCredential] ✅\nissuer:{id, name} ✅\nvalidFrom / validUntil ✅\ncredentialSubject:{id, ชื่อ, เลขบัตร, วันเกิด} ✅\ncredentialSchema ✅\n_sd:[digest_ที่อยู่, digest_รูปภาพ] ⚠️"]:::req - end - - subgraph verify["Wallet ตรวจ (STEP 6.5) — 16 ขั้น"] - V1["① iss/jti/sub + Trustlist\n+ @context + type"]:::check - V2["② GET JWKS → kid\n→ Verify signature"]:::check - V3["③ validFrom/validUntil\n+ sub check"]:::check - V4["④ GET statuslist+jwt\n→ bit[idx]=0"]:::check - OK["✅ บันทึกบัตร"]:::pass - NG["❌ ทิ้งบัตร"]:::fail - V1 --> V2 --> V3 --> V4 - V4 -->|"ผ่าน"| OK - V4 -->|"ล้มเหลว"| NG - end - - TL_W[("📱 Wallet\nLocal Trustlist")]:::store - JWKS_S[("🔑 JWKS Endpoint")]:::store - SL_S[("📋 statuslist+jwt")]:::store - - vc --> V1 - TL_W -.->|"ใช้ lookup"| V1 - JWKS_S -->|"ได้ publicKeyJwk"| V2 - SL_S -->|"ได้ statuslist+jwt"| V4 -``` - -| ข้อมูลใน VC | มาจาก | จำเป็น | หมายเหตุ | -|-----------|-------|--------|----------| -| JOSE `typ: dc+sd-jwt` | DOPA | ✅ | ระบุ format ให้ Wallet/Verifier รู้ | -| `iss` | DOPA | ✅ | = `issuer.id` (ต้องตรงกัน) | -| `sub` | Wallet (จาก proof.jwt) | ✅ | = `credentialSubject.id` | -| `jti` | DOPA | ✅ | = VC `id` field (ต้องตรงกัน) | -| `nbf` / `exp` | DOPA | ✅ | = `validFrom` / `validUntil` (Unix timestamp) | -| `_sd_alg: sha-256` | DOPA | ✅ | บังคับระบุ สำหรับ SD verification | -| `status.status_list` | DOPA | ✅ | IETF Token Status List `{idx, uri}` | -| `@context` | W3C + DOPA context | ✅ | JSON-LD semantics | -| `type` | DOPA | ✅ | `[VerifiableCredential, ThaiNationalIDCredential]` | -| `issuer: {id, name}` | DOPA | ✅ | object form | -| `validFrom` / `validUntil` | DOPA | ✅ | ISO 8601 ใน VC body | -| `credentialSubject` | ฐานข้อมูล DOPA | ✅ | ห่อทุก claims (ชื่อ, เลขบัตร, วันเกิด) | -| `credentialSchema` | DOPA | ✅ | JSON Schema URL | -| `_sd` (SD claims) | DOPA | ⚠️ เสริม | digest ของ `ที่อยู่`, `รูปภาพ` | -| `cnf.jwk` (Wallet PubKey) | จาก proof.jwt STEP 5 | ✅ | Holder Binding | -| JWKS | `issuer.dopa.go.th/.well-known/jwt-vc-issuer` | ✅ | ดึง `publicKeyJwk` มา verify | -| `statuslist+jwt` | `status.dopa.go.th/statuslist/1` | ✅ | IETF Token Status List | - ---- - -##### ● STEP 7 — Notification (Optional) - -| ข้อมูล | มาจาก | จำเป็น | หมายเหตุ | -|--------|-------|--------|----------| -| `notification_id` | Credential Response (jti ของ VC) | ⚠️ เสริม | ระบุว่า VC ใบไหน | -| `event` | Wallet กำหนด | ⚠️ เสริม | `credential_accepted` หรือ `credential_failure` | - -> STEP 7 เป็น Optional — ถ้า Wallet ส่ง DOPA จะบันทึก audit log ว่าบัตรถูกรับแล้ว -> ถ้าไม่ส่ง — ไม่กระทบการใช้งานบัตร แต่ DOPA ไม่รู้ว่าบัตรถูกรับหรือเปล่า - ---- - - -### 3.2.6.1 LoA และ DPoP Policy - -> **LoA (Level of Assurance)** = ระดับความมั่นใจในตัวตนของผู้ถือ VC -> ยิ่ง LoA สูง → ข้อมูลอ่อนไหวมากขึ้น → ต้องป้องกัน token ให้แข็งแกร่งขึ้น - ---- - -#### 3.2.6.1.1 LoA แต่ละระดับหมายถึงอะไร? - -| LoA | ความหมาย | วิธียืนยันตัวตน | ตัวอย่าง VC (ไทย) | -|-----|---------|----------------|-----------------| -| **LoA 1** | ความเชื่อมั่นต่ำ — สมัครด้วยตัวเอง ไม่ verify | กรอกชื่อเองโดยไม่ต้องพิสูจน์ | บัตรสมาชิก, Loyalty card | -| **LoA 2** | ความเชื่อมั่นปานกลาง — verify เอกสาร online | สแกนบัตร + OTP ออนไลน์ | ใบขับขี่ดิจิทัล, ใบรับรองวิชาชีพ | -| **LoA 3** | ความเชื่อมั่นสูง — verify ใบหน้า + เอกสาร | Biometric + เอกสารจริง (เช่น DOPA) | **บัตรประชาชน**, หนังสือเดินทาง | -| **LoA 4** | ความเชื่อมั่นสูงมาก — hardware key + biometric | Hardware token + ยืนยันตัวตน in-person | บัตรสุขภาพระดับสูง | - -> **บัตรประชาชนดิจิทัลใน section 5.1 = LoA 3** ตามมาตรฐาน ETDA และ NIST SP 800-63 - ---- - -#### 3.2.6.1.2 DPoP คืออะไร ทำไมต้องมี? - -**DPoP (Demonstrating Proof of Possession)** ผูก access_token กับ key ของ Wallet โดยเฉพาะ — ขโมย token ไปใช้จากที่อื่นไม่ได้ - -**โดยไม่มี DPoP:** -``` -Wallet ได้ access_token "eyJhbG..." ← Bearer token -Attacker ขโมย token ไป -Attacker POST /credential + token ที่ขโมยมา → ได้บัตรของเหยื่อ 😱 -``` - -**เมื่อมี DPoP:** -``` -Wallet สร้าง DPoP key pair (private key อยู่ใน Wallet เท่านั้น) -Wallet ได้ access_token ที่ผูกกับ DPoP public key -Attacker ขโมย token ไป แต่ไม่มี DPoP private key -Attacker POST /credential → DOPA ตรวจ DPoP ไม่ผ่าน → 401 ❌ -``` - ---- - -#### 3.2.6.1.3 DPoP Policy: ใช้เมื่อไหร่? - -| LoA | tx_code (OTP) | Wallet Attestation | DPoP | เหตุผล | -|-----|-------------|-------------------|------|--------| -| **LoA 1** | ⚪ ไม่บังคับ | ⚪ ไม่บังคับ | ⚪ ไม่บังคับ — Bearer OK | ข้อมูลไม่ sensitive ถ้าหายออกใหม่ได้ง่าย | -| **LoA 2** | ✅ แนะนำ | 🟡 แนะนำ | 🟡 แนะนำ (SHOULD) | ข้อมูลสำคัญ ควรป้องกัน | -| **LoA 3** | ✅ บังคับ | ✅ บังคับ | ✅ บังคับ (MUST) | ข้อมูล identity หลัก — HAIP กำหนด | -| **LoA 4** | ✅ บังคับ | ✅ บังคับ | ✅ บังคับ + hardware key | ข้อมูลอ่อนไหวสูงสุด | - ---- - -#### 3.2.6.1.4 DPoP ทำงานอย่างไรใน Pre-Auth Flow? - -DPoP เพิ่มใน 2 จุด — **STEP 4** (แลก token) และ **STEP 5** (ขอบัตร) - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -sequenceDiagram - participant W as 📱 Wallet - participant DOPA as 🏛️ DOPA - - Note over W: ก่อน STEP 4 — สร้าง DPoP key pair - W->>W: [9.1] DPoP private key → Trusted Execution Environment (TEE) - - rect rgb(220, 240, 255) - Note over W,DOPA: STEP 4 — POST /token - W->>W: สร้าง DPoP Proof { htu:/token, htm:POST, jti:abc } - W->>DOPA: POST /token + DPoP-Proof: eyJ... - DOPA->>DOPA: verify DPoP → ผูก token กับ DPoP public key - DOPA-->>W: access_token (token_type: DPoP) - end - - rect rgb(220, 240, 255) - Note over W,DOPA: STEP 5 — POST /credential - W->>W: สร้าง DPoP Proof ใหม่ { htu:/credential, jti:xyz } - Note over W: ⚠️ jti ต้องใหม่ทุก request — ห้ามนำ proof เก่ามาใช้ซ้ำ - W->>DOPA: POST /credential + Authorization: DPoP {token} + DPoP-Proof: eyJ... - DOPA->>DOPA: verify DPoP key ตรงกับที่ผูกไว้ + jti ไม่ซ้ำ - DOPA-->>W: SD-JWT VC ✅ - end -``` - ---- - -#### 3.2.6.1.5 Trustlist Entry ต้องระบุ LoA และ DPoP requirement - -เพื่อให้ Issuer รู้ว่า VC ประเภทนี้ต้องบังคับ DPoP ระดับไหน: - -```json -// ตัวอย่าง entry ใน Trustlist สำหรับ credential configuration -{ - "credential_configuration_id": "ThaiNationalIDCredential-config", - "required_loa": 3, - "require_dpop": true, - "require_wallet_attestation": true, - "require_tx_code": true -} -``` - -```json -{ - "credential_configuration_id": "MembershipCard-config", - "required_loa": 1, - "require_dpop": false, - "require_wallet_attestation": false, - "require_tx_code": false -} -``` - ---- - -#### 3.2.6.1.6 สิ่งที่ Issuer ต้องตรวจใน STEP 4 ตาม LoA - -| การตรวจ | LoA 1 | LoA 2 | LoA 3 | LoA 4 | -|---------|:-----:|:-----:|:-----:|:-----:| -| `pre_authorized_code` valid + single-use | ✅ | ✅ | ✅ | ✅ | -| `tx_code` (OTP) ถูกต้อง | ⚪ | ✅ | ✅ | ✅ | -| Wallet Attestation ถูกต้อง | ⚪ | 🟡 | ✅ | ✅ | -| DPoP-Proof header มีและถูกต้อง | ⚪ | 🟡 | ✅ | ✅ | -| DPoP `jti` ไม่ซ้ำ (replay protection) | ⚪ | 🟡 | ✅ | ✅ | -| Hardware-backed key | ⚪ | ⚪ | ⚪ | ✅ | - ---- - -จบเนื้อหาบทที่ 3 (กระบวนการออกเอกสาร) ทั้งหมด — บทถัดไปคือ [บทที่ 4 กระบวนการแสดงเอกสาร (OID4VP)](../../04-presentation-flow/01-overview/index.md) ซึ่งอธิบายว่าเมื่อ Holder มี VC อยู่ใน Wallet แล้ว จะแสดงให้ Verifier ตรวจสอบอย่างไร diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/_category_.json b/docs/trust-guide/03-issuance-flow/02-full-flow/_category_.json deleted file mode 100644 index a4c7054..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "3.2 Flow การออก VC — Full Flow", - "position": 2 -} diff --git a/docs/trust-guide/03-issuance-flow/02-full-flow/index.md b/docs/trust-guide/03-issuance-flow/02-full-flow/index.md deleted file mode 100644 index 864f236..0000000 --- a/docs/trust-guide/03-issuance-flow/02-full-flow/index.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -sidebar_position: 1 ---- - -# 3.2 Flow การออก VC — Full Flow (ภาพรวม) - -หมวดนี้ (3.2) ขยายภาพจาก [3.1 Minimal Flow](../01-minimal-flow.md) ให้เห็น **ทุก STEP ของการออก VC ผ่าน OID4VCI ตั้งแต่ SETUP จนถึง STEP 7 (Notification)** อย่างละเอียด รวมผู้เกี่ยวข้องทั้งหมด (ประชาชน, Wallet, Wallet Provider, ETDA Trustlist, DOPA, DOPA Status List) และกรณีพิเศษที่ Minimal Flow ยังไม่ครอบคลุม เช่น DPoP (LoA 3+), Immediate vs. Deferred Issuance, Trustlist Freshness Check และโครงสร้าง SD-JWT VC แบบ 3 ชั้น - -เลือกอ่านหัวข้อย่อยได้ตามนี้: - -| หัวข้อ | เนื้อหา | -|--------|---------| -| [3.2.1 Full Flow เทคนิคโดยละเอียด](02b-full-flow-technical-detail.md) | ภาพรวมขั้นตอนทั้งหมด (STEP 1–7) + Sequence Diagram ฉบับเต็ม (field-by-field) | -| [3.2.2 SETUP & Credential Offer](03-setup-and-credential-offer.md) | SETUP, STEP 1–3: Credential Offer, Early Trust Check, Discovery, Authentication | -| [3.2.3 Token & Credential Request](04-token-and-credential-request.md) | STEP 4–5.5: Token Request, Credential Request, Trust Check: Wallet Provider | -| [3.2.4 Issue & Notify](05-issue-and-notify.md) | STEP 6–7: Issue VC, Trust Check: Issuer, Notification | -| [3.2.5 การตรวจสอบ Trustlist](06-verification-details.md) | รายละเอียดจุดตรวจ Trustlist ทุกจุด (L1–L7) | -| [3.2.6 Data Flow & Policy](07-data-flow-and-policy.md) | Data Flow และ LoA/DPoP Policy | - -> **ภาพรวมขั้นตอนทั้งหมด (STEP 1–7) ฉบับเต็ม** พร้อมตารางสรุปและ Sequence Diagram อยู่ที่หน้า [3.2.1 Full Flow เทคนิคโดยละเอียด](02b-full-flow-technical-detail.md) diff --git a/docs/trust-guide/03-issuance-flow/_category_.json b/docs/trust-guide/03-issuance-flow/_category_.json deleted file mode 100644 index ef41ce5..0000000 --- a/docs/trust-guide/03-issuance-flow/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "3. กระบวนการออกเอกสาร (OID4VCI)", - "position": 3 -} diff --git a/docs/trust-guide/04-presentation-flow/01-overview/02-setup-and-request.md b/docs/trust-guide/04-presentation-flow/01-overview/02-setup-and-request.md deleted file mode 100644 index bb52979..0000000 --- a/docs/trust-guide/04-presentation-flow/01-overview/02-setup-and-request.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -sidebar_position: 2 ---- - -# 4.1.1 ขั้นตอนที่ 1 — SETUP, สแกน QR, คำขอฉบับสมบูรณ์, ตรวจ Verifier - -## 4.1.1 SETUP, สแกน QR, คำขอฉบับสมบูรณ์ และตรวจ Verifier - -#### 🟢 ก่อนเริ่ม — SETUP (ETDA คุม) - -ก่อนเริ่มใช้งาน ทั้ง Wallet และ Verifier ต้องดาวน์โหลด **"รายชื่อผู้น่าเชื่อถือ" (Trust list)** จาก ETDA มาเก็บไว้ก่อน (ผ่าน `GET /.well-known/trust-anchor` เพื่อได้ ETDA Public Key และ `GET /trustlist` เพื่อได้ Trustlist) หลักการเหมือนกับ SETUP ในบทการออกเอกสาร (ดู [§ 3.2.2 SETUP](../../03-issuance-flow/02-full-flow/03-setup-and-credential-offer.md#-setup--เตรียมระบบก่อนเริ่มใช้งาน)) ต่างกันตรงที่บทนั้น **Wallet และ Issuer (DOPA)** เป็นผู้ดึง Trustlist ส่วนบทนี้ **Wallet และ Verifier** เป็นผู้ดึงแทน เปรียบเหมือนเจ้าหน้าที่รับบัตรทุกคนต้องมีรายชื่อหน่วยงานและแอปที่ได้รับอนุมัติแล้วก่อนเริ่มงาน - -#### 🔵 ขั้นตอนที่ 1 — ผู้ใช้สแกน QR Code - -ผู้ใช้สแกน QR Code ที่ปรากฏ ณ จุดให้บริการ กระเป๋าดิจิทัลอ่านข้อมูลเบื้องต้นจาก QR Code เพื่อรู้ช่องทางสำหรับดึงคำขอฉบับสมบูรณ์ - -#### 🔵 ขั้นตอนที่ 2 — ดึงคำขอฉบับสมบูรณ์ - -กระเป๋าดิจิทัลส่งคำขอผ่านอินเทอร์เน็ตไปยังผู้ตรวจสอบ (2.5) เพื่อรับคำขอฉบับสมบูรณ์ที่ลงนามแล้ว - -🟠 2.6 จุดตรวจสอบที่ 1 — ตรวจสอบความน่าเชื่อถือของ Verifier - -**นี่คือจุดควบคุมที่สำคัญที่สุดของฝั่งกระเป๋าดิจิทัล** ก่อนดำเนินการใด ๆ ต่อ กระเป๋าดิจิทัลต้องมั่นใจก่อนว่าผู้ที่ส่งคำขอมาได้รับการรับรองจริง โดยค้นหาว่าผู้ตรวจสอบอยู่ใน Trust list หรือไม่ - -หากตรวจสอบไม่ผ่าน กระเป๋าดิจิทัลจะยุติกระบวนการทันที - -#### 🔵 ขั้นตอนที่ 3 — ส่งเอกสารตอบกลับ - -กระเป๋าดิจิทัลส่งเอกสารทั้งหมดไปยังผู้ตรวจสอบ - -#### ขั้นตอนที่ 4 — ผู้ตรวจสอบดำเนินการตรวจสอบ - -ฝั่งผู้ตรวจสอบเริ่มตรวจสอบเอกสารที่ได้รับ โดยแบ่งเป็นหลายขั้นตอนซ้อนกัน - -🟠 4.1 จุดตรวจสอบที่ 2 — ตรวจสอบความน่าเชื่อถือของ Wallet Provider (optional) - -ผู้ตรวจสอบยืนยันว่าแอปกระเป๋าดิจิทัลที่ส่งข้อมูลมาเป็นของแท้ โดยค้นหาผู้ให้บริการกระเป๋าดิจิทัลในรายการ Trust list - -[บังคับต้องทำเฉพาะ Wallet ของรัฐบาลเท่านั้น] - -🟠 4.2 จุดตรวจสอบที่ 3 — ตรวจสอบความน่าเชื่อถือของ Issuer - -ผู้ตรวจสอบยืนยันว่าหน่วยงานที่ออกเอกสารได้รับการรับรอง โดยค้นหาผู้ออกเอกสารใน Trust list ตรวจสอบว่าสถานะปัจจุบันยังใช้งานได้ พร้อมตรวจประเภทของเอกสารว่าผู้ออกได้รับอนุญาตให้ออกเอกสารประเภทนี้หรือไม่ - ---- - -ข้างต้นคือคำอธิบายภาพรวมสำหรับผู้อ่านทั่วไป หากต้องการดู **Sequence Diagram ฉบับเทคนิคทั้งหมด** พร้อมคำอธิบายทุก field และทุกขั้นตอนย่อย (สำหรับผู้พัฒนาและสถาปนิกระบบ) ดูได้ที่ [4.2.1 Full Flow เทคนิคโดยละเอียด](../02-full-flow/02b-full-flow-technical-detail.md) — เนื้อหาส่วนนี้ซ่อนโดยดีฟอลต์ ต้องคลิกเพื่อเปิดดู - -จบเนื้อหาบทที่ 4 (กระบวนการแสดงเอกสาร) — เมื่อทั้งการออกเอกสาร (บทที่ 3) และการแสดงเอกสาร (บทที่ 4) ผ่านการตรวจสอบครบทุกจุดแล้ว ต่อไปดู [บทที่ 5 โครงสร้างพื้นฐานความน่าเชื่อถือ](../../05-trust-infrastructure/01-technical-requirements-and-schema.md) ว่า Trustlist ที่ใช้ตรวจสอบทั้งสอง flow มีโครงสร้างอย่างไร - diff --git a/docs/trust-guide/04-presentation-flow/01-overview/_category_.json b/docs/trust-guide/04-presentation-flow/01-overview/_category_.json deleted file mode 100644 index 64f7f30..0000000 --- a/docs/trust-guide/04-presentation-flow/01-overview/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "4.1 กระบวนการแสดงและตรวจสอบเอกสาร (OID4VP)", - "position": 1 -} diff --git a/docs/trust-guide/04-presentation-flow/01-overview/index.md b/docs/trust-guide/04-presentation-flow/01-overview/index.md deleted file mode 100644 index aa6d7e2..0000000 --- a/docs/trust-guide/04-presentation-flow/01-overview/index.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -sidebar_position: 1 ---- - -# 4.1 กระบวนการแสดงและตรวจสอบเอกสาร (OID4VP) - -ต่อจาก [บทที่ 3 กระบวนการออกเอกสาร (OID4VCI)](../../03-issuance-flow/01-minimal-flow.md) ที่ Holder ได้รับ VC มาเก็บใน Wallet แล้ว บทนี้อธิบายอีกฝั่งหนึ่งของวงจรชีวิต VC — เมื่อ Holder ต้องการ **แสดง** เอกสารนั้นให้ผู้ตรวจสอบ (Verifier) ดู ระบบตรวจสอบความน่าเชื่อถืออย่างไร - -## 4.1 ภาพรวมขั้นตอนการแสดงและตรวจสอบเอกสาร (OID4VP + Trustlist) - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -sequenceDiagram - autonumber - participant W as Wallet - participant V as Verifier - participant TL as ETDA Trust List - - rect rgb(210, 240, 210) - note over W,TL: 🟢 [ETDA คุม] SETUP — ดึง Trustlist - W->>TL: GET /trustlist - TL-->>W: Trustlist data - W->>W: เก็บ Trustlist ไว้ใน Local Storage - V->>TL: GET /trustlist - TL-->>V: Trustlist data - V->>V: เก็บ Trustlist ไว้ใน Local Storage - end - - rect rgb(210, 230, 255) - Note over W,V: 🔵 1. QR Transfer - V->>V: Render QR - W->>V: Scan QR - end - - rect rgb(210, 230, 255) - Note over W,V: 🔵 2. Fetch Request Object - W->>V: Request the Request Object - V-->>W: (2.5)Signed Request Object - - end - - rect rgb(255, 225, 180) - Note over W,TL: 🟠 2.6 Trust Check 1 — Wallet check Verifier - W->>W: Check if Verifier in TL - end - - rect rgb(210, 230, 255) - Note over W,V: 🔵 3. Send Response - W->>V: ส่ง VP กลับไป - end - - - rect rgb(255, 225, 180) - - Note over V,TL: 4. Verifier Validation - opt บังคับเป็นต้องทำสำหรับ Wallet ของรัฐบาลเท่านั้น - - Note over V,TL: 🟠 4.1 Trust Check 2 — Verifier
check Wallet Provider - V->>V: Check if walllet provider's in TL - end - loop For each credential - - rect rgb(255, 225, 180) - Note over V,TL: 🟠 4.2 Trust Check 3 — Verifier
check Issuer - - V->>V: Check if Issuer in TL - V->>V: Check issuer status + allowed credential types - end - - end - end - -``` - -**สีกรอบในผังหมายถึงอะไร:** - -| สี | หมายถึง | ใครกำหนด | -|----|---------|----------| -| 🟢 สีเขียว | ขั้นตอนที่ ETDA ดูแลโดยตรง (Trustlist) | ETDA | -| 🔵 สีน้ำเงิน | ขั้นตอนตามมาตรฐาน OID4VP สากล | OpenID Foundation | -| 🟠 สีส้ม | จุดตรวจสอบ trust ว่าใครน่าเชื่อถือ | Trust Framework (ETDA) | - -**Trustlist Verification — จุดตรวจที่ ETDA คุม (3 จุดใน flow):** - -ในการแสดงเอกสาร มีจุดตรวจ Trustlist อยู่ **3 จุด** โดยตรวจกันคนละฝั่ง คือ กระเป๋าดิจิทัลตรวจผู้ตรวจสอบก่อนส่งข้อมูล 1 จุด และผู้ตรวจสอบตรวจฝั่งกระเป๋า/ผู้ออกเอกสารอีก 2 จุด - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart TD - classDef store fill:#e2e3e5,stroke:#6c757d,color:#343a40 - classDef check fill:#fff3cd,stroke:#ffc107,color:#856404 - classDef pass fill:#d4edda,stroke:#28a745,color:#155724 - classDef fail fill:#f8d7da,stroke:#721c24,color:#721c24 - classDef src fill:#cce5ff,stroke:#0056b3,color:#003d80 - - ETDA[("🛡️ ETDA Trustlist")]:::src - TL_W[("📱 Wallet Local Trustlist\n• Verifier")]:::store - TL_V[("🖥️ Verifier Local Trustlist\n• Wallet Provider\n• Issuer")]:::store - ETDA -->|"SETUP ดึงก่อนใช้งาน (API Key required) "| TL_W & TL_V - - subgraph check1["🟠 จุดตรวจ 1 — Wallet ตรวจ Verifier (ก่อนส่งข้อมูล)"] - V_PK["① ดึง Public Key ของ Verifier\nจาก Request Object"]:::check - V_TL{"② ตรวจ Verifier\nอยู่ใน Trustlist ไหม"}:::check - V_SIG["③ ตรวจลายเซ็นคำขอ\n+ ผู้รับ (audience) + อายุ"]:::check - V_SC{"④ ตรวจขอบเขตข้อมูลที่ขอ\n+ วัตถุประสงค์"}:::check - OK1["✅ ส่งเอกสารต่อได้"]:::pass - FL1["❌ หยุด ไม่ส่งข้อมูล"]:::fail - TL_W -.->|"ดึงข้อมูล"| V_TL - V_PK --> V_TL - V_TL -->|"พบ"| V_SIG --> V_SC - V_TL -->|"ไม่พบ"| FL1 - V_SC -->|"อยู่ในขอบเขต"| OK1 - V_SC -->|"เกินขอบเขต"| FL1 - end - - subgraph check2["🟠 จุดตรวจ 2 — Verifier ตรวจ Wallet Provider (เฉพาะ Wallet รัฐ)"] - WP_ID["① ดึง WP ID + Public Key\nจาก Wallet Attestation"]:::check - WP_TL{"② ตรวจ Wallet Provider\nอยู่ใน Trustlist ไหม"}:::check - WP_SIG{"③ ตรวจลายเซ็น WIA + WIA-PoP\n(กันปลอม/เล่นซ้ำ)"}:::check - OK2["✅ ผ่าน"]:::pass - FL2["❌ ปฏิเสธ"]:::fail - TL_V -.->|"ดึงข้อมูล"| WP_TL - WP_ID --> WP_TL - WP_TL -->|"พบ"| WP_SIG - WP_TL -->|"ไม่พบ"| FL2 - WP_SIG -->|"ถูกต้อง"| OK2 - WP_SIG -->|"ผิด"| FL2 - end - - subgraph check3["🟠 จุดตรวจ 3 — Verifier ตรวจ Issuer (ตรวจเอกสารแต่ละฉบับ)"] - IS_DID["① แกะ Issuer DID จากเอกสาร\n→ Resolve เป็น Public Key"]:::check - IS_TL{"② ตรวจ Issuer\nอยู่ใน Trustlist ไหม"}:::check - IS_ST{"③ ตรวจสถานะ active\n+ ประเภทเอกสารที่อนุญาต"}:::check - IS_SIG{"④ ตรวจลายเซ็นเอกสาร"}:::check - OK3["✅ ยอมรับเอกสาร"]:::pass - FL3["❌ ปฏิเสธ"]:::fail - TL_V -.->|"ดึงข้อมูล"| IS_TL - IS_DID --> IS_TL - IS_TL -->|"พบ"| IS_ST - IS_TL -->|"ไม่พบ"| FL3 - IS_ST -->|"อนุญาต"| IS_SIG - IS_ST -->|"ไม่อนุญาต"| FL3 - IS_SIG -->|"ลายเซ็นถูกต้อง"| OK3 - IS_SIG -->|"ลายเซ็นผิด"| FL3 - end -``` - -> หมายเหตุ: หลังผ่านจุดตรวจ 3 แล้ว ผู้ตรวจสอบยังเช็ค **สถานะเอกสาร** (ถูกยกเลิก/พักใช้หรือไม่) ต่ออีกขั้นหนึ่ง แต่ขั้นนี้ดึงข้อมูลจาก Status List ของผู้ออกเอกสาร ไม่ได้ใช้ ETDA Trustlist จึงไม่นับรวมใน 3 จุดข้างต้น (ดูรายละเอียดใน [4.2.1 Full Flow](../02-full-flow/02b-full-flow-technical-detail.md)) - -ขั้นตอนการทำงานทั้งหมดของ flow นี้ อ่านต่อได้ในหน้าถัดไป: [4.2 SETUP, สแกน QR, คำขอฉบับสมบูรณ์, ตรวจ Verifier](02-setup-and-request.md) และรายละเอียดทางเทคนิคทุกขั้นตอนที่ [4.2.1 Full Flow เทคนิคโดยละเอียด](../02-full-flow/02b-full-flow-technical-detail.md) (ซ่อนโดยดีฟอลต์ คลิกเพื่อดู) diff --git a/docs/trust-guide/04-presentation-flow/02-full-flow/02b-full-flow-technical-detail.md b/docs/trust-guide/04-presentation-flow/02-full-flow/02b-full-flow-technical-detail.md deleted file mode 100644 index eb883be..0000000 --- a/docs/trust-guide/04-presentation-flow/02-full-flow/02b-full-flow-technical-detail.md +++ /dev/null @@ -1,428 +0,0 @@ ---- -sidebar_position: 1 ---- - -# 4.2.1 Full Flow เทคนิคโดยละเอียด — OID4VP (การแสดงและตรวจ VC) - -> เนื้อหาในบทนี้เป็นรายละเอียดทางเทคนิคระดับ field-by-field ของขั้นตอนการแสดงและตรวจ VC (OID4VP) ทั้งหมด เหมาะสำหรับผู้พัฒนาและสถาปนิกระบบที่ต้องการรายละเอียดครบทุกขั้นตอน (นับจากคำอธิบายภาพรวมใน [4.1.1](../01-overview/02-setup-and-request.md)) -> -> คลิกที่หัวข้อด้านล่างเพื่อดูเนื้อหาแบบเต็ม (ซ่อนไว้โดยดีฟอลต์เพื่อไม่ให้หน้าเอกสารยาวเกินไป) - -
-📖 คลิกเพื่อดู Full Flow เทคนิคทั้งหมด (Sequence Diagram + คำอธิบายทุกขั้นตอน) - -## Sequence Diagram ฉบับเต็ม - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -sequenceDiagram - autonumber - participant U as User - participant C as Holder + Wallet Instance
Key Store - participant W as Wallet (Holder) - participant V as Verifier (RP) - participant TL as ETDA Trust List
(signed by ETDA) - participant UR as Universal DID Resolver - participant IR as Issuer Registry
(credential status endpoint) - - %% ========================================= - rect rgb(210, 240, 210) - note over W,TL: 🟢 [ETDA คุม] SETUP — ดึง ETDA Public Key + Trustlist - W->>TL: GET https://trust.etda.or.th/.well-known/trust-anchor - TL-->>W: ETDA Public Key (JWK) - W->>W: เก็บ ETDA Public Key ไว้ใน Local Storage - V->>TL: GET https://trust.etda.or.th/.well-known/trust-anchor - TL-->>V: ETDA Public Key (JWK) - V->>V: เก็บ ETDA Public Key ไว้ใน Local Storage - W->>TL: GET /trustlist + Authorization: Bearer *** - TL-->>W: Trustlist JSON + Detached JWS Signature - W->>W: Verify Detached JWS ด้วย ETDA Public Key - W->>W: เก็บ Trustlist + บันทึก TTL (exp claim) - V->>TL: GET /trustlist + Authorization: Bearer *** - TL-->>V: Trustlist JSON + Detached JWS Signature - V->>V: Verify Detached JWS ด้วย ETDA Public Key - V->>V: เก็บ Trustlist + บันทึก TTL (exp claim) - end - - rect rgb(210, 230, 255) - Note over V: 📋 Step 1 — Prepare Authorization Request - V->>V: Generate nonce, state, request_id - V->>V: Build Request Object JWT incl. dcql_query
(format: "dc+sd-jwt") - V->>V: Sign with Verifier key - V->>V: Render QR - end - - rect rgb(210, 230, 255) - Note over W,V: 📱 Step 2 — Cross-device transfer - U->>V: Access RP website - V-->>U: Display QR - U->>W: Scan QR - end - - rect rgb(210, 230, 255) - Note over W,V: 📥 Step 2.5 — Fetch Request Object - W->>V: Request the Request Object - V-->>W: Signed Request Object JWT (with dcql_query) - end - - %% ========================================= - rect rgb(255, 225, 180) - Note over W,TL: 🔍 Between 2.5 and 3 — Verifier trust + DCQL scope - - W->>W: Parse RO Extract PK_v - - alt TL cached & fresh - W->>W: Load cached TL - else - W->>TL: GET ETDA Trust List - TL-->>W: TL { verifier_entries[],
issuer_entries[],
wallet_provider_entries[] } - W->>W: 🔎 L1: Verify TL signature with ETDA public key - W->>W: Cache with TTL - end - - W->>W: 🔎 L2: Lookup at TL.verifier_entries[]
WHERE entry.PK_v == PK_v - alt Not found OR status ≠ active - W--xV: Abort — untrusted_verifier - else Found - W->>W: Verify RO sig with entry.PUBkey (PK_v)
aud == wallet id, exp/iat,
nonce structural ≥128-bit
dcql_query.claims paths ⊆ entry.allowed_attributes
purpose ∈ entry.allowed_purposes - W->>W: Store session ctx - end - end - - %% ========================================= - rect rgb(210, 230, 255) - Note over W,U: ✅ Step 3 — DCQL local match + Consent - W->>W: For each q in dcql_query.credentials:
filter local SD-JWT VCs by q.format=="dc+sd-jwt"
+ q.meta.vct_values (vct claim)
walk q.claims[].path natively in each candidate - W->>W: Evaluate credential_sets (required options satisfied?) - W->>U: Show consent UI (legal_name, purpose, matched claims) - U->>W: Approve, pick option per set, select claims to disclose - W->>C: Unlock holder + wallet instance keys - C-->>W: Authorized - end - - %% ========================================= - rect rgb(210, 230, 255) - Note over W,C: 🔐 Step 4 — Build per-id SD-JWT Presentation + WIA-PoP - - loop For each picked q.id - W->>W: Select disclosures matching q.claims[].path
Drop non-selected disclosures
Concatenate: issuer_jwt~d1~d2~...~ - W->>W: Compute sd_hash = SHA-256 of
the presentation up to and including trailing ~ - W->>C: Sign KB-JWT with holder key
header: { typ: "kb+jwt", alg }
payload: { iss=holder,
aud=client_id,
nonce=⟨V nonce⟩,
sd_hash, iat } - C-->>W: KB-JWT - W->>W: Assemble vp_token[q.id] =
issuer_jwt~d1~d2~...~KB-JWT
(SD-JWT VC presentation format) - end - W->>C: Sign WIA-PoP with Wallet Instance key
{ iss=wallet instance ID, aud=client_id,
nonce=⟨V nonce⟩, jti, iat, exp } - C-->>W: wia_pop - end - - rect rgb(210, 230, 255) - Note over W,V: 📤 Step 5 — Authorization Response (direct_post.jwt) - W->>V: POST response_uri {
vp_token: { id → SD-JWT presentation string },
wallet_attestation: ⟨WIA⟩,
wallet_attestation_pop: ⟨WIA-PoP⟩,
state } - V-->>W: 200 OK - end - - %% ========================================= - rect rgb(255, 225, 180) - Note over V,IR: 🔎 Step 6 — Verifier validates - - V->>V: Recover { issued_nonce, client_id, dcql_query } by state - - Note over V,TL: ⓪ Wallet Instance Attestation - V->>V: Parse WIA → extract WP iss + PK_wp - - alt TL cached & fresh (Verifier side) - V->>V: Use cached TL - else - V->>TL: GET ETDA Trust List - TL-->>V: TL artefact - V->>V: 🔎 L1: Verify TL sig with ETDA public key - V->>V: Cache with TTL - end - - V->>V: 🔎 L3: Lookup at TL.wallet_provider_entries[]
WHERE entry.PK_wp == PK_wp - alt Not found - V--xW: 400 untrusted_wallet_provider - else Found - V->>V: Verify WIA sig with entry.PUBkey (PK_wp)
WIA: iat ≤ now < exp - V->>V: Verify WIA-PoP sig with WIA.cnf.jwk
iss == WIA.sub, aud == client_id,
nonce == issued_nonce, jti not consumed - V->>V: Mark WIA-PoP jti consumed - end - - Note over V: Per-id DCQL loop - loop For each q in dcql_query.credentials - V->>V: Look up vp_token[q.id]
Split by ~ →
{ issuer_jwt, disclosures[], KB-JWT } - - Note over V,UR: ① KB-JWT — holder binding (SD-JWT native) - V->>V: Parse issuer_jwt (typ:"dc+sd-jwt")
Extract cnf.jwk (holder key confirmation)
Extract iss (issuer DID), kid - V->>V: Parse KB-JWT (typ:"kb+jwt") - V->>V: Verify KB-JWT sig with issuer_jwt.cnf.jwk
aud == client_id
nonce == issued_nonce
iat recent - V->>V: Recompute sd_hash over
issuer_jwt~d1~d2~...~
Confirm KB-JWT.sd_hash matches - V->>V: Mark nonce + KB-JWT jti consumed - - Note over V,TL: ② Issuer JWT — issuer PUBkey + TL authority - V->>UR: GET /1.0/identifiers/⟨issuer DID⟩ - UR-->>V: DID Resolution Result - V->>V: 🔎 L6: Dereference kid in didDocument
WHERE kid ∈ doc.assertionMethod[]
→ publicKeyJwk (PK_iss)
- - V->>V: 🔎 L4: Lookup at TL.issuer_entries[]
WHERE entry.PK_iss == PK_iss - alt Not found - V--xW: 400 untrusted_issuer (key) - else Found - V->>V: Check entry.status == active
now ∈ [key_valid_from, key_valid_until]
issuer_jwt.vct ∈ entry.allowed_credential_types
DID method ∈ entry.allowed_did_methods - - alt Policy check fails - V--xW: 400 untrusted_issuer (policy) - else - V->>V: Verify issuer_jwt sig with PK_iss
nbf ≤ now ≤ exp
_sd_alg supported (e.g. "sha-256") - - V->>V: For each disclosure d in disclosures[]:
compute hash(d) with _sd_alg
confirm hash(d) ∈ issuer_jwt._sd
parse d = [salt, claim_name, claim_value]
build reconstructed_credential - alt Any disclosure hash mismatch - V--xW: 400 invalid_disclosure - end - - V->>V: DCQL match against reconstructed_credential:
vct ∈ q.meta.vct_values?
Each c in q.claims:
walk c.path → value present
If specific values required → check value matches one of them - alt DCQL match fails - V--xW: 400 dcql_failed - end - - opt status claim present - V->>V: Read status.status_list { uri, idx } - alt Cached & fresh - V->>V: Use cached statuslist+jwt - else - V->>IR: GET status.status_list.uri
Accept: application/statuslist+jwt - IR-->>V: statuslist+jwt - V->>V: Verify statuslist JWS:
typ == "statuslist+jwt",
sub == URI of the Status List,
now > iat,
now < exp,
signed by PK_iss (already from L6) - end - V->>V: 🔎 L7: Decode lst (base64url → zlib)
Extract entry at status_list.idx
(LSB-first, bits-per-entry from header) - alt 0x00 VALID - Note over V: ✅ Active - else 0x01 INVALID - V--xW: 400 credential_revoked - else 0x02 SUSPENDED - V--xW: 400 credential_suspended - end - end - end - end - end - - Note over V: ③ credential_sets evaluation - V->>V: For each required set:
some option in set.options where every id satisfied - alt Some required set unsatisfied - V--xW: 400 credential_set_unsatisfied - end - end - - alt All checks pass - V-->>U: ✅ Service granted - else Any failure - V-->>W: 400 invalid_request (specific code) - end -``` - -## คำอธิบายทุกขั้นตอน - -#### 🟢 SETUP — ดึง ETDA Public Key + Trustlist - -Wallet และ Verifier ต่างดึง ETDA Public Key จาก `GET /.well-known/trust-anchor` เก็บไว้ใช้ verify ลายเซ็นของ Trustlist จากนั้นดึง Trustlist ผ่าน `GET /trustlist` (ต้องแนบ API Key) พร้อม Detached JWS Signature แล้ว verify ด้วย ETDA Public Key ก่อนเก็บพร้อม TTL (exp claim) — ขั้นตอนนี้เป็นจุดเริ่มของ trust chain ทั้งหมดในฝั่งการแสดง VC เช่นเดียวกับฝั่งการออก VC (ดู [3.1 Minimal Flow](../../03-issuance-flow/01-minimal-flow.md)) แต่ที่นี่ทั้ง Wallet และ **Verifier** ต่างเป็นผู้ดึง Trustlist เอง (ไม่ใช่ Wallet กับ Issuer/DOPA เหมือนในบทที่ 3) - -#### 📋 Step 1 — ผู้ตรวจสอบเตรียมคำขอ - -**ฝั่ง Verifier ดำเนินการ**: -1. **สร้างค่าสุ่ม** — nonce (สำหรับป้องกันการเล่นซ้ำ), state (เก็บข้อมูลธุรกรรมฝั่ง Verifier), request_id -2. **สร้าง Request Object** ในรูปแบบ JWT บรรจุ `dcql_query` ที่ระบุว่าต้องการเอกสารประเภทใด (format: `dc+sd-jwt`) และต้องการข้อมูลอะไรบ้าง -3. **ลงนาม** Request Object ด้วยกุญแจส่วนตัวขององค์กร (รูปแบบของกุญแจส่วนตัวใช้ตามที่ oid4vp กำหนด เช่น x509_hash, decentralized_identifier หรืออื่น ๆ แต่ต้องเป็นแบบที่ wallet รองรับ เพื่อให้ทำงานร่วมกันได้) -4. **สร้าง QR Code** ที่มีลิงก์ไปยัง Request Object เต็ม (request_uri) - ---- - -#### 📱 Step 2 — การถ่ายโอนข้ามอุปกรณ์ - -**ฝั่ง User และ Wallet**: -1. ผู้ใช้เข้าใกล้จุดให้บริการของผู้ตรวจสอบ -2. ผู้ตรวจสอบแสดง QR Code บนจอ -3. ผู้ใช้ใช้กระเป๋าดิจิทัลบนโทรศัพท์สแกน QR Code - ---- - -#### 📥 Step 2.5 — ดึง Request Object ฉบับสมบูรณ์ - -**ฝั่ง Wallet**: -1. กระเป๋าดิจิทัลส่ง HTTP GET ไปยัง `request_uri` ที่ระบุใน QR Code -2. ผู้ตรวจสอบตอบกลับด้วย Request Object JWT ฉบับเต็มที่ได้ลงนามแล้ว - -**เหตุผล**: QR Code มีขนาดจำกัด บรรจุ Request Object ทั้งหมดไม่ได้ จึงส่งเป็นลิงก์แทน - ---- - -#### 🟠 จุดตรวจสอบที่ 1 — ตรวจ Verifier (ระหว่าง Step 2.5 และ Step 3) - -**ความสำคัญ**: นี่คือจุดควบคุมความปลอดภัยที่สำคัญที่สุดของฝั่งกระเป๋าดิจิทัล ต้องผ่านการตรวจนี้ก่อน จึงจะยอมส่งข้อมูลใด ๆ - -**การดำเนินการ**: -1. **แยก Request Object** เพื่อดึง public key ของผู้ตรวจสอบ -2. **ตรวจสอบ Trust List** — ใช้สำเนาในเครื่องหากยังไม่หมดอายุ มิฉะนั้นดึงใหม่และตรวจลายมือชื่อด้วยกุญแจ ETDA (**L1**) -3. **ค้นหา PK_v ใน `verifier_entries`** ของ Trust List (**L2**) -4. **หากไม่พบ** — หยุดกระบวนการทันที ไม่ส่งข้อมูลใด ๆ ให้ผู้ตรวจสอบที่ไม่ได้รับการรับรอง -5. **หากพบ** — ดำเนินการตรวจต่อ ได้แก่ - - ตรวจลายมือชื่อของ Request Object ด้วยกุญแจในรายการ - - ยืนยัน audience, ระยะเวลาใช้งาน, ความถูกต้องของใบรับรอง - - ตรวจว่าโดเมนตรงกับที่ลงทะเบียนไว้ - - ตรวจ nonce มีความยาวเพียงพอ - - **ตรวจขอบเขตข้อมูล** — สิ่งที่ Verifier ขอต้องอยู่ในขอบเขตที่ได้รับอนุญาต - - ตรวจว่าวัตถุประสงค์อยู่ในรายการที่ได้รับอนุญาต - ---- - -#### ✅ Step 3 — DCQL Local Match และการขอความยินยอม - -**ฝั่ง Wallet**: -1. **DCQL Match** — สำหรับคำขอแต่ละรายการใน `dcql_query` - - กรองเอกสาร SD-JWT VC ในเครื่องด้วย format และ `vct_values` - - เดินตาม path ที่ระบุใน claims เพื่อหา disclosure ที่ตรงเงื่อนไข -2. **ประเมิน credential_sets** — ตรวจว่าเอกสารในเครื่องสามารถตอบสนองเงื่อนไขที่จำเป็นได้หรือไม่ -3. **แสดง Consent UI** — บอกผู้ใช้ว่า - - Verifier คือใคร (legal_name จาก Trust List) - - วัตถุประสงค์ในการขอ - - Claim ที่จะเปิดเผย -4. **ผู้ใช้ตัดสินใจ** — เลือก option ที่จะใช้ (กรณีมีหลายทางเลือก) และเลือกเอกสาร -5. **ปลดล็อกกุญแจ** — ผู้ใช้ยืนยันตัวตนผ่าน biometric หรือ PIN เพื่อปลดล็อกกุญแจ holder และ wallet instance - ---- - -#### 🔐 Step 4 — สร้าง SD-JWT Presentation และ WIA-PoP - -**ฝั่ง Wallet** สำหรับเอกสารแต่ละฉบับที่ผู้ใช้เลือก: - -1. **เลือก Disclosures** — เก็บเฉพาะ disclosure ที่ตรงกับ claims path ที่ Verifier ขอ ทิ้งที่เหลือ -2. **ประกอบ Presentation** — ต่อกันด้วยเครื่องหมาย `~` - ``` - issuer_jwt~disclosure_1~disclosure_2~...~ - ``` -3. **คำนวณ sd_hash** — SHA-256 ของทั้ง presentation ข้างต้น (รวม `~` สุดท้าย) -4. **สร้าง KB-JWT (Key Binding JWT)** ลงนามด้วยกุญแจ holder - - Header: `typ: "kb+jwt"` - - Payload: audience (client_id), nonce (จาก Verifier), sd_hash, iat -5. **ประกอบขั้นสุดท้าย** ต่อ KB-JWT ต่อท้าย - ``` - issuer_jwt~disclosure_1~disclosure_2~...~KB-JWT - ``` - นี่คือ `vp_token[q.id]` - -**สร้าง WIA-PoP** เพิ่มเติม — Proof of Possession ของกระเป๋าดิจิทัล ลงนามด้วยกุญแจ wallet instance บรรจุ nonce, audience, iat, exp - -**หมายเหตุ**: SD-JWT VC ไม่ต้องมี JWT-VP ห่ออีกชั้น เพราะ **KB-JWT ทำหน้าที่ Holder Binding ในตัว** - ---- - -#### 📤 Step 5 — ส่งเอกสารตอบกลับ - -**ฝั่ง Wallet** ส่ง HTTP POST ไปยัง `response_uri` ในรูปแบบ `direct_post.jwt` (JWE) ประกอบด้วย -- `vp_token` — object ที่ key เป็น id จาก DCQL, value เป็น SD-JWT presentation string -- `wallet_attestation` — WIA (Wallet Instance Attestation) -- `wallet_attestation_pop` — WIA-PoP -- `state` — สำหรับเชื่อมโยงกับธุรกรรมฝั่ง Verifier - ---- - -#### 🔎 Step 6 — ผู้ตรวจสอบดำเนินการตรวจสอบ - -Verifier เริ่มจากค้นหาบริบทของธุรกรรมจาก state ที่ได้รับ - -##### 🟠 จุดตรวจสอบที่ 2 — ตรวจ Wallet Provider (Step 6 ⓪) - -**วัตถุประสงค์**: ยืนยันว่าแอปกระเป๋าดิจิทัลที่ส่งข้อมูลมาเป็นของแท้และได้รับการรับรอง - -**การดำเนินการ**: -1. **แยก WIA** เพื่อดึงกุญแจสาธารณะของผู้ให้บริการกระเป๋าดิจิทัล (PK_wp) -2. **โหลด Trust List** (ใช้สำเนาหรือดึงใหม่) -3. **ค้นหา PK_wp ใน `wallet_provider_entries`** (**L3**) -4. **หากไม่พบ** — ปฏิเสธด้วย `untrusted_wallet_provider` -5. **หากพบ** — ตรวจต่อ - - ตรวจลายมือชื่อของ WIA ด้วย PK_wp (WIA: iat ≤ now < exp) -6. **ตรวจ WIA-PoP** — ยืนยันว่ากระเป๋าดิจิทัลกำลังทำงานในธุรกรรมนี้จริง - - ลายมือชื่อของ WIA-PoP ตรงกับกุญแจใน `WIA.cnf.jwk` - - nonce ตรงกับที่ส่งไป - - jti ยังไม่ถูกใช้ (ป้องกันการเล่นซ้ำ) - -##### วนตรวจเอกสารแต่ละฉบับใน `vp_token` - -สำหรับเอกสารแต่ละรายการ Verifier ดำเนินการดังนี้ - -###### แยก Presentation - -**ตัด `vp_token[q.id]` ด้วยเครื่องหมาย `~`** → ได้ 3 ส่วน คือ issuer_jwt, disclosures[], และ KB-JWT - -###### ① ตรวจ KB-JWT — Holder Binding - -1. **แยก issuer_jwt** เพื่อดึงกุญแจของ holder จาก `cnf.jwk` และ issuer DID -2. **แยก KB-JWT** (typ: `kb+jwt`) -3. **ตรวจลายมือชื่อ KB-JWT** ด้วย `issuer_jwt.cnf.jwk` - - audience ตรงกับ client_id - - nonce ตรงกับที่ส่งไป - - iat เป็นเวลาปัจจุบัน -4. **คำนวณ sd_hash ใหม่** จาก presentation แล้วเทียบกับที่ระบุใน KB-JWT - - ป้องกันการ tampering ระหว่างทาง -5. **บันทึกการใช้ nonce และ jti** เพื่อป้องกันการเล่นซ้ำ - -**หมายเหตุ**: SD-JWT VC ตรวจ holder binding ผ่าน `cnf.jwk` โดยตรง ไม่ต้องพึ่ง DID Resolver สำหรับ holder - -##### 🟠 จุดตรวจสอบที่ 3 — ตรวจ Issuer (Step 6 ②) - -###### หา Public Key ของ Issuer - -1. **Resolve issuer DID** ผ่าน Universal DID Resolver (**L6**) -2. ดึง publicKeyJwk (PK_iss) จาก DID Document ที่ตรงกับ kid ใน issuer_jwt - -###### ตรวจใน Trust List - -3. **ค้นหา PK_iss ใน `issuer_entries`** (**L4**) -4. **หากไม่พบ** — ปฏิเสธด้วย `untrusted_issuer (key)` -5. **ตรวจสถานะและ policy** - - สถานะ = active - - อยู่ในช่วงเวลาที่กุญแจใช้งานได้ - - `vct` ของเอกสารอยู่ในประเภทที่ Issuer ได้รับอนุญาตให้ออก - - DID method ที่ใช้อยู่ในรายการที่อนุญาต - -###### ตรวจ Issuer JWT และ Disclosures - -6. **ตรวจลายมือชื่อของ issuer_jwt** ด้วย PK_iss -7. **ตรวจ Disclosures แต่ละรายการ** - - Hash disclosure ด้วย `_sd_alg` (เช่น sha-256) - - ตรวจว่า hash ที่ได้อยู่ในรายการ `_sd` ของ issuer_jwt - - ถ้าไม่ตรง → ปฏิเสธด้วย `invalid_disclosure` - - ถ้าตรง → แยก `[salt, claim_name, claim_value]` แล้วนำมาประกอบเป็น credential ที่ reconstructed -8. **DCQL Match** — ตรวจกับ credential ที่ reconstructed แล้ว - - `vct` ตรงกับที่ขอ - - Claims ที่ path ระบุมีอยู่จริง - - ถ้าระบุ values ที่ต้องการ → ตรวจว่า value ตรงกับหนึ่งในนั้น - -##### 🟠 จุดตรวจสอบที่ 4 — ตรวจสถานะเอกสาร - -**หาก issuer_jwt มี `status` claim** - -1. **อ่าน uri และ idx** จาก `status.status_list` -2. **โหลด status list** — ใช้สำเนาหรือดึงใหม่จาก Issuer Registry -3. **ตรวจลายมือชื่อของ statuslist+jwt** - - typ = `statuslist+jwt` - - sub ตรงกับ URI ของ Status List - - now > iat, now < exp - - ลงนามด้วย PK_iss ที่ได้จาก L6 -4. **แตกและถอด bitstring** (**L7**) - - base64url decode - - zlib decompress - - ดึง bit ที่ตำแหน่ง idx (LSB-first) -5. **ตีความสถานะ** - - `0x00` = VALID → ✅ ผ่าน - - `0x01` = INVALID → ปฏิเสธด้วย `credential_revoked` - - `0x02` = SUSPENDED → ปฏิเสธด้วย `credential_suspended` - -##### ③ ตรวจความครบถ้วนของ credential_sets - -Verifier ตรวจว่าเอกสารที่ได้รับครอบคลุมเงื่อนไข `credential_sets` ที่จำเป็นทุกรายการ - -- สำหรับ set ที่ `required: true` ต้องมีอย่างน้อยหนึ่ง option ที่ทุก id ในนั้นได้รับการยืนยันครบถ้วน -- หากมี set ที่จำเป็นแต่ไม่ครบ → ปฏิเสธด้วย `credential_set_unsatisfied` - -
diff --git a/docs/trust-guide/04-presentation-flow/02-full-flow/_category_.json b/docs/trust-guide/04-presentation-flow/02-full-flow/_category_.json deleted file mode 100644 index 67f29e5..0000000 --- a/docs/trust-guide/04-presentation-flow/02-full-flow/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "4.2 Full Flow เทคนิคโดยละเอียด (OID4VP)", - "position": 2 -} diff --git a/docs/trust-guide/04-presentation-flow/_category_.json b/docs/trust-guide/04-presentation-flow/_category_.json deleted file mode 100644 index 91ccc7d..0000000 --- a/docs/trust-guide/04-presentation-flow/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "4. กระบวนการแสดงเอกสาร (OID4VP)", - "position": 4 -} diff --git a/docs/trust-guide/05-trust-infrastructure/01-technical-requirements-and-schema.md b/docs/trust-guide/05-trust-infrastructure/01-technical-requirements-and-schema.md deleted file mode 100644 index 38804cc..0000000 --- a/docs/trust-guide/05-trust-infrastructure/01-technical-requirements-and-schema.md +++ /dev/null @@ -1,105 +0,0 @@ -# 5.1 ข้อกำหนดทางเทคนิค และ Trusted List Schema - -ต่อจาก [บทที่ 4 กระบวนการแสดงเอกสาร](../04-presentation-flow/01-overview/index.md) ที่ทั้งการออกและแสดง VC ต้องพึ่ง Trustlist ในการตรวจสอบ บทนี้อธิบายว่า Trustlist มีโครงสร้างข้อมูล (schema) อย่างไร - -## 5.1 ข้อกำหนดทางเทคนิค - -- **Protocol:** [เช่น protocol สำหรับ issue / present] -- **รูปแบบ credential:** [เช่น data model และวิธีเซ็น] -- **Identifier:** [เช่น DID method, registry] -- **Status และ revocation:** [ตรวจสถานะอย่างไร] - -## 5.2 โครงสร้าง Trust (Trust Infrastructure) - -- **Trusted List / Registry:** เก็บอะไร ใครเซ็น -- **PKI / Key:** ออก key, หมุน key, ถอน key อย่างไร -- **Discovery:** ค้นหาและตรวจสอบอีกฝ่ายอย่างไร - -ตัวอย่าง Trustlist ที่ ETDA เซ็นและ Entity ดาวน์โหลดมาเก็บไว้ -`trust_list_jwt` คือ trustlist ใน format JWT -`trust_anchor_key` คือ public key (JWK) ที่ใช้ตรวจลายเซ็นบน `trust_list_jwt` (token และ key ด้านล่างเป็นตัวอย่าง ไม่ใช่ของจริง) - -```json -{ - "trust_list_jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkVUREEtdHJ1c3QtYW5jaG9yLWtleS0xIn0.eyJpc3MiOiJodHRwczovL3RydXN0LmV0ZGEub3IudGgiLCJpYXQiOjE3NTAwMDAwMDAsImV4cCI6MTc4MTUzNjAwMCwiZW50cmllcyI6WyJkaWQ6d2ViOmRvcGEuZ28udGgiXX0.NOT-REAL-SIGNATURE-DO-NOT-USE-0000000000000000000000000000000000000000", - "trust_anchor_key": { - "kty": "RSA", - "n": "0EXAMPLE1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPqrstuvwxyz0123456789exampleMODULUSnotREALdoNOTuse0123456789abcdefABCDEF0123456789abcdefABCDEF0123456789abcdefABCDEF0123456789abcdefEXAMPLE", - "e": "AQAB", - "kid": "ETDA-trust-anchor-key-1", - "alg": "RS256", - "use": "sig" - } -} -``` - -### 5.2.1 Trusted List Schema (Organization-Centric) - -TL จัดกลุ่มข้อมูลตาม**องค์กร** — แต่ละ entry คือ 1 องค์กร ที่มี `services[]` เก็บบทบาทที่ลงทะเบียน: - -| Concept | คำอธิบาย | -|---------|----------| -| **1 entity** | = 1 องค์กร (เช่น กรมการปกครอง, ธนาคารกรุงเทพ) | -| **services[]** | = รายการบทบาทที่องค์กรนั้นลงทะเบียนไว้ | -| **1 service** | = 1 บทบาท + 1 DID + 1 keypair (`signing_pubkey`) | - -**ตัวอย่าง:** -- **DOPA** → 1 service: `credential_issuer` (ออกบัตรประชาชนดิจิทัล) -- **DGA** → 1 service: `wallet_provider` (ให้บริการ Wallet ทางรัฐ) -- **BBL** → 2 services: `credential_issuer` + `verifier` (ออก VC + verify ลูกค้า) - -> **ทำไมต้องแยก service?** แม้เป็นองค์กรเดียวกัน แต่แต่ละบทบาทมี keypair ต่างกัน และสามารถ suspend/revoke แยกกันได้ - -#### TL Root Wrapper - -```json -{ - "@context": "https://schemas.etda.or.th/trusted-list/v1", - "id": "urn:etda:trusted-list:2026-v1", - "issuer": "did:web:etda.or.th:trusted-list", - "issued": "2026-09-01T00:00:00Z", - "next_update": "2026-09-08T00:00:00Z", - "entities": [ - // ... organization entries with services[] - ] -} -``` - -| Field | Type | Required | หมายเหตุ | -|-------|------|:--------:|---------| -| `@context` | string | ✅ | ETDA TL schema URL — ระบุ version ของ TL format | -| `id` | string | ✅ | TL identifier (URN format) | -| `issuer` | string | ✅ | DID ของ ETDA ที่ sign TL | -| `issued` | ISO8601 | ✅ | วันที่ publish | -| `next_update` | ISO8601 | ✅ | วันที่จะ publish version ถัดไป | -| `entities` | array | ✅ | รายการ entities (organizations with services) ที่ลงทะเบียน | - -#### Entity-Level Fields (ข้อมูลองค์กร — ทุก entity มีเหมือนกัน) - -ข้อมูลระดับองค์กร ใช้ระบุว่า "ใคร" และ "สถานะรวม" ขององค์กรนั้น: - -| Field | Required | หมายเหตุ | -|-------|:--------:|---------| -| `id` | ✅ | รหัสองค์กร เช่น `org-10001` (DOPA), `org-20001` (BBL) | -| `name_th` | ✅ | ชื่อองค์กรภาษาไทย — แสดงใน Wallet UI | -| `name_en` | ✅ | ชื่อองค์กรภาษาอังกฤษ | -| `status` | ✅ | สถานะรวมขององค์กร: `active` / `suspended` / `revoked` — ถ้า org ถูก revoke → service ทั้งหมดใช้ไม่ได้ | -| `services[]` | ✅ | รายการบทบาทที่องค์กรลงทะเบียน (ดู Service-Level ด้านล่าง) | - -#### Service-Level Fields (ข้อมูลบทบาท — ทุก service มีเหมือนกัน) - -ข้อมูลระดับบทบาท อยู่ภายใน `services[]` ของแต่ละองค์กร ระบุว่าองค์กรนั้น "ทำหน้าที่อะไร" และ "ใช้ key ไหน": - -| Field | Required | หมายเหตุ | -|-------|:--------:|---------| -| `did` | ✅* | DID ของบทบาทนี้ เช่น `did:web:dopa.go.th` — Wallet/Verifier ใช้ตรวจลายเซ็น (**Verifier: optional** เพราะใช้ `client_id` แทนได้) | -| `type` | ✅ | ประเภทบทบาท: `credential_issuer` / `wallet_provider` / `verifier` | -| `status` | ✅ | สถานะเฉพาะบทบาทนี้: `active` / `suspended` / `revoked` — **อาจต่างจาก org-level** เช่น BBL Issuer ถูก suspend แต่ BBL Verifier ยังใช้ได้ | -| `signing_pubkey` | ✅* | Public keyของ entity ที่ ETDA ได้ตรวจ ณ ตอนลงทะเบียน — ใช้เวลา verify ลายเซ็นของ VC/WIA/VP Request Object | -| `valid_from` | ✅ | วันเริ่มใช้งาน (ISO8601) — ก่อนวันนี้ถือว่ายังไม่ active | -| `valid_until` | ✅ | วันหมดอายุ (ISO8601) — หลังวันนี้ต้องต่ออายุใหม่ | - -> **การตรวจสอบ:** consumer (Wallet/Verifier/Issuer) ต้องเช็คทั้ง org `status` และ service `status` — ถ้าอย่างใดอย่างหนึ่งไม่ใช่ `active` → ปฏิเสธ - -ต่อไปดู [§ 5.3 ตัวอย่าง TL JSON](02-tl-json-examples.md) เพื่อเห็นว่า schema ข้างต้นถูกใช้จริงกับแต่ละบทบาท (Issuer, Wallet Provider, Verifier) อย่างไร - diff --git a/docs/trust-guide/05-trust-infrastructure/02-tl-json-examples.md b/docs/trust-guide/05-trust-infrastructure/02-tl-json-examples.md deleted file mode 100644 index 52366b4..0000000 --- a/docs/trust-guide/05-trust-infrastructure/02-tl-json-examples.md +++ /dev/null @@ -1,179 +0,0 @@ -# 5.3 ตัวอย่าง TL JSON — Issuer, Wallet Provider, Verifier, Multi-Role - -ต่อจาก [§ 5.1–5.2 ข้อกำหนดทางเทคนิคและ Trusted List Schema](01-technical-requirements-and-schema.md) บทนี้แสดงตัวอย่าง JSON จริงของแต่ละบทบาท เพื่อให้เห็นว่า field ต่าง ๆ ถูกกรอกค่าจริงอย่างไร - -## 5.3 ตัวอย่าง TL JSON — Issuer, Wallet Provider, Verifier, Multi-Role - -| Field | Required | หมายเหตุ | -|-------|:--------:|---------| -| `credential_issuer` | ✅ | OID4VCI endpoint URL — ตรงกับ `credential_issuer` ใน Credential Offer (QR) ใช้สำหรับ Early Check (STEP 1.5) และ TC-3 (STEP 6.5) | -| `credential_type` | ✅ | ประเภท credential ที่ออกได้ เช่น `ThaiNationalIDCredential` | -| `signing_pubkey` | ✅ | Public key สำหรับ verify VC signature | -| `issuable_claims` | ✅ | claims ที่ Issuer สามารถออกได้ | -| `protocols` | ✅ | OID4VCI protocol versions supported | - -```json -{ - "id": "org-10001", - "name_th": "กรมการปกครอง", - "name_en": "Department of Provincial Administration", - "status": "active", - "services": [ - { - "did": "did:web:dopa.go.th", - "type": "credential_issuer", - "credential_issuer": "https://issuer.dopa.go.th", - "status": "active", - "credential_type": "ThaiNationalIDCredential", - "signing_pubkey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQ7X4...", - "issuable_claims": ["citizen_id", "name_th", "name_en", "birth_date", "address", "photo"], - "protocols": ["OID4VCI_1.0"], - "valid_from": "2026-09-01T00:00:00Z", - "valid_until": "2028-09-01T00:00:00Z" - } - ] -} -``` - -**Wallet ใช้ทำอะไร:** -- **Early Check (STEP 1.5):** ตรวจ `credential_issuer` URL จาก QR ตรงกับ `credential_issuer` ใน TL — fail fast ถ้าไม่พบ -- **TC-3 (STEP 6.5):** Verify ว่า `iss` ใน VC ตรงกับ `credential_issuer` ใน TL -- Verify signature ของ VC ด้วย `signing_pubkey` -- ตรวจสอบว่า `status` = `active` และยังไม่หมดอายุ -- ตรวจสอบว่า claims ที่รับมา อยู่ใน `issuable_claims` - -### 5.3.1 TL JSON — Wallet Provider - -| Field | Required | หมายเหตุ | -|-------|:--------:|---------| -| `signing_pubkey` | ✅ | Public key สำหรับ verify Wallet Instance Attestation (WIA) | -| `platforms` | ✅ | รองรับ platform อะไร (`ios`, `android`, `web`) | - -```json -{ - "id": "org-10002", - "name_th": "สำนักงานพัฒนารัฐบาลดิจิทัล (องค์การมหาชน)", - "name_en": "Digital Government Development Agency", - "status": "active", - "services": [ - { - "did": "did:web:wallet.dga.or.th", - "type": "wallet_provider", - "status": "active", - "signing_pubkey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExyz...", - "platforms": ["ios", "android"], - "valid_from": "2026-09-01T00:00:00Z", - "valid_until": "2028-09-01T00:00:00Z" - } - ] -} -``` - -**Verifier ใช้ทำอะไร:** -- Verify Wallet Instance Attestation (WIA) ด้วย `signing_pubkey` -- ตรวจสอบว่า Wallet Provider อยู่ใน Trusted List และ `status` = `active` - -### 5.3.2 TL JSON — Verifier - -| Field | Required | หมายเหตุ | -|-------|:--------:|---------| -| `did` | ❓ Optional | Verifier ไม่จำเป็นต้องมี DID | -| `signing_pubkey` | ❓ Optional | ใช้ sign VP Request (ถ้ามี) | -| `client_id` | ✅ | URL/domain สำหรับ OID4VP callback | -| `credential_type` | ✅ | credential ที่ verify | -| `protocols` | ✅ | OID4VP protocol versions supported | -| `use_case[]` | ✅ | รายการ use cases ที่ได้รับอนุมัติ | -| `use_case[].use_case_id` | ✅ | รหัส use case | -| `use_case[].purpose_th` | ✅ | วัตถุประสงค์ภาษาไทย (แสดงให้ user consent) | -| `use_case[].purpose_en` | ✅ | วัตถุประสงค์ภาษาอังกฤษ | -| `use_case[].allowed_claims` | ✅ | claims ที่ Verifier มีสิทธิ์ขอ | - -```json -{ - "id": "org-20001", - "name_th": "ธนาคารกรุงเทพ จำกัด (มหาชน)", - "name_en": "Bangkok Bank Public Company Limited", - "status": "active", - "services": [ - { - "did": "did:web:bangkokbank.com:verifier", - "type": "verifier", - "status": "active", - "signing_pubkey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEabc...", - "client_id": "https://verify.bangkokbank.com/callback", - "credential_type": "ThaiNationalIDCredential", - "protocols": ["OID4VP_1.0"], - "use_case": [ - { - "use_case_id": "UC-BBL-AML-001", - "purpose_th": "ยืนยันตัวตนตาม พ.ร.บ.ป้องกันฟอกเงิน", - "purpose_en": "Identity verification per AML Act", - "allowed_claims": ["citizen_id", "name_th", "name_en", "birth_date"] - }, - { - "use_case_id": "UC-BBL-LOAN-001", - "purpose_th": "ยืนยันตัวตนเพื่อสมัครสินเชื่อ", - "purpose_en": "Identity verification for loan application", - "allowed_claims": ["citizen_id", "name_th", "address"] - } - ], - "valid_from": "2026-09-01T00:00:00Z", - "valid_until": "2028-09-01T00:00:00Z" - } - ] -} -``` - -**Wallet ใช้ทำอะไร:** -- ตรวจสอบว่า `client_id` ใน VP Request ตรงกับ TL -- แสดง `purpose_th` / `purpose_en` ให้ user consent -- ตรวจสอบว่า claims ที่ขอมา อยู่ใน `allowed_claims` ของ use case ที่ระบุ -- ป้องกัน Verifier ขอ claims เกินสิทธิ์ - -### 5.3.3 TL JSON — Multi-Role Organization (BBL = Issuer + Verifier) - -1 organization สามารถมีหลาย services ได้ โดยแต่ละ service มี DID และ keypair แยกกัน: - -```json -{ - "id": "org-20001", - "name_th": "ธนาคารกรุงเทพ จำกัด (มหาชน)", - "name_en": "Bangkok Bank Public Company Limited", - "status": "active", - "services": [ - { - "did": "did:web:bangkokbank.com:issuer", - "type": "credential_issuer", - "status": "active", - "credential_type": "BankAccountCredential", - "signing_pubkey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdef...", - "issuable_claims": ["account_number", "account_type", "account_holder_name", "branch"], - "protocols": ["OID4VCI_1.0"], - "valid_from": "2026-10-01T00:00:00Z", - "valid_until": "2028-10-01T00:00:00Z" - }, - { - "did": "did:web:bangkokbank.com:verifier", - "type": "verifier", - "status": "active", - "signing_pubkey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEabc...", - "client_id": "https://verify.bangkokbank.com/callback", - "credential_type": "ThaiNationalIDCredential", - "protocols": ["OID4VP_1.0"], - "use_case": [ - { - "use_case_id": "UC-BBL-AML-001", - "purpose_th": "ยืนยันตัวตนตาม พ.ร.บ.ป้องกันฟอกเงิน", - "purpose_en": "Identity verification per AML Act", - "allowed_claims": ["citizen_id", "name_th", "name_en", "birth_date"] - } - ], - "valid_from": "2026-09-01T00:00:00Z", - "valid_until": "2028-09-01T00:00:00Z" - } - ] -} -``` - - -ต่อไปดู [§ 5.4 Registries — Status List และ Schema](03-registries.md) สำหรับข้อมูลเสริมที่อยู่นอกขอบเขตของ trust model แต่จำเป็นสำหรับ Verifier ตรวจ credential ให้ครบถ้วน diff --git a/docs/trust-guide/05-trust-infrastructure/03-registries.md b/docs/trust-guide/05-trust-infrastructure/03-registries.md deleted file mode 100644 index 44dfa05..0000000 --- a/docs/trust-guide/05-trust-infrastructure/03-registries.md +++ /dev/null @@ -1,22 +0,0 @@ -# 5.4 Registries — Status List และ Schema (นอกขอบเขต) - -ต่อจาก [§ 5.3 ตัวอย่าง TL JSON](02-tl-json-examples.md) บทสั้นนี้ปิดท้ายบทที่ 5 ด้วยข้อมูลเสริม 2 เรื่องที่อยู่นอกขอบเขตของ trust model โดยตรง แต่จำเป็นต้องมีเพื่อให้ Verifier ตรวจ credential ได้ครบ - -## 5.4 Registries — Status List และ Schema (นอกขอบเขต) - -> **หมายเหตุ:** ส่วนนี้ *ไม่อยู่* ในขอบเขตของ trust model แต่ใส่ไว้เพื่อให้ diagram มีข้อมูลครบถ้วน -> อ้างอิงแนวทาง EUDI ARF - -Issuer ต้องเก็บและ publish ข้อมูล 2 ชุด เพื่อให้ Verifier ตรวจ credential ได้: - -| Registry | เก็บอะไร | ใครใช้ | มาตรฐาน | -|----------|---------|--------|---------| -| **Status List** | สถานะของ credential (ใช้ได้ / เพิกถอน / ระงับ) | Verifier ดึงไปเช็คตอน verify VC| Token Status List (`statuslist+jwt`) | -| **Schema / Type Metadata** | โครงสร้างและความหมายของ claim ใน credential | Verifier ใช้ตรวจรูปแบบ, Wallet ใช้แสดงผล | SD-JWT VC Type Metadata (`vct`), JSON Schema | - -- **Status List:** Issuer เซ็นและ publish ไว้ที่ .well-known/statuslist.json ตอน verify Verifier ดึง list มาเช็คว่า credential ยังใช้ได้หรือไม่ -- **Schema / Type Metadata:** Issuer publish โครงสร้างของ credential ผ่าน JSON Schema Verifier ใช้ตรวจว่า claim ตรงตามแบบหรือไม่ Wallet ใช้ดูว่าจะแสดงผลอย่างไร - -> **ทำไมถึงนอกขอบเขต:** registry ทั้ง 2 อย่างนี้เกี่ยวกับ data integrity และ interoperability ระหว่าง Issuer กับ Verifier ไม่ใช่เรื่องการสร้างหรือถอน trust ระหว่างผู้เข้าร่วม จึงไม่นับเป็นส่วนของ trust model แต่ต้องแสดงไว้ใน diagram ของระบบ - -จบเนื้อหาบทที่ 5 (โครงสร้างพื้นฐานความน่าเชื่อถือ) — บทถัดไปคือ [บทที่ 6 ความปลอดภัยและความเป็นส่วนตัว](../06-security-and-privacy/01-verifier-confidence-and-registration.md) อธิบายมาตรการป้องกันที่ระบบนี้ใช้ควบคู่กับ Trustlist diff --git a/docs/trust-guide/05-trust-infrastructure/_category_.json b/docs/trust-guide/05-trust-infrastructure/_category_.json deleted file mode 100644 index 1c88567..0000000 --- a/docs/trust-guide/05-trust-infrastructure/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "5. โครงสร้างพื้นฐานความน่าเชื่อถือ", - "position": 5 -} diff --git a/docs/trust-guide/06-security-and-privacy/01-verifier-confidence-and-registration.md b/docs/trust-guide/06-security-and-privacy/01-verifier-confidence-and-registration.md deleted file mode 100644 index f1d909b..0000000 --- a/docs/trust-guide/06-security-and-privacy/01-verifier-confidence-and-registration.md +++ /dev/null @@ -1,128 +0,0 @@ -# 6.1 ระดับความเชื่อมั่นของ Verifier และการลงทะเบียน - -ต่อจาก [บทที่ 5 โครงสร้างพื้นฐานความน่าเชื่อถือ](../05-trust-infrastructure/01-technical-requirements-and-schema.md) ที่อธิบายรูปแบบข้อมูลของ Trustlist แล้ว บทนี้ (บทที่ 6) เริ่มอธิบายมาตรการความปลอดภัยและความเป็นส่วนตัว โดยเริ่มจากระดับความเชื่อมั่นของ Verifier และขั้นตอนลงทะเบียนเข้าระบบ - -## 6.1 ระดับความเชื่อมั่นของ Verifier และการลงทะเบียน - -| ระดับ | ข้อกำหนด | ตัวอย่างการใช้ | -|-------|---------|----------------| -| ทั่วไป (Standard) | [Verifier ตรวจอายุ ก่อนขายสินค้าควบคุมตามกฎหมาย] | [ไม่จำเป็นต้องพิสูจน์ Wallet] | -| เข้มงวด (Enhanced) | [Verifier ตรวจข้อมูลใบขับขี่ เพื่อยืนยันตัวตนก่อนทำธุรกรรมทางภาครัฐ] | [จำเป็นต้องพิสูจน์ Wallet] | - -### 6.1.1 การลงทะเบียน (Registration) - -ทุก Entity (Issuer, Wallet Provider, Verifier) ต้องลงทะเบียนกับ ETDA ก่อนเข้าร่วมระบบ เมื่อผ่านแล้วจะได้ **API key** ใช้ API key นี้เพื่อดึง **Trustlist** ผ่าน **ETDA Trust Gateway** - -| ขั้น | ใครทำ | ทำอะไร | ผลลัพธ์ | ระยะเวลาโดยประมาณ | -|------|-------|--------|---------|---------------------| -| 1 | Entity | สมัครลงทะเบียนกับ ETDA (ส่งข้อมูลองค์กร + key/DID) | คำขอลงทะเบียน | 1 วัน (เตรียมเอกสาร ดู [§ 7.5.1](../07-governance-and-conformance/01-conformance-governance-versioning.md#751-เอกสารที่ต้องเตรียม)) | -| 2 | ETDA | ตรวจและอนุมัติ | ออก **API key** ให้ Entity | **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ — ETDA จะประกาศ SLA การตรวจในเวอร์ชันถัดไป]** ดู [§ 7.5.3](../07-governance-and-conformance/01-conformance-governance-versioning.md#753-ระยะเวลาโดยรวม) | -| 3 | Entity | เรียก ETDA Trust Gateway พร้อมแนบ API key | ขอ Trustlist | ทันที (real-time API call) | -| 4 | Trust Gateway | ตรวจ API key แล้วส่ง Trustlist | Entity ได้ Trustlist | ทันที (real-time API call) | -| 5 | Entity | เก็บ Trustlist ไว้ใช้ตรวจ trust | พร้อมใช้งาน (ดูหัวข้อ 7 และ flow 5.1 / 5.2) | — | - -> **หมายเหตุ:** สำหรับรายละเอียดขั้นตอนสมัครแบบเต็ม (เอกสารที่ต้องยื่น, เกณฑ์ตรวจสอบของ ETDA, conformance testing, sandbox, ช่องทางติดต่อ) ดู [§ 7.5 ขั้นตอนสมัครเป็น Issuer](../07-governance-and-conformance/01-conformance-governance-versioning.md#75-ขั้นตอนสมัครเป็น-issuer) - -- **API key** MUST เก็บเป็นความลับ ใช้ยืนยันตัวตนของ Entity ตอนเรียก Trust Gateway -- Entity ต้องลงทะเบียนสำเร็จก่อน จึงจะดึง Trustlist และเข้าร่วม flow การออก/ตรวจ VC ได้ -- **API key** มีอายุ [TODO: ETDA ยังไม่ได้กำหนดระยะเวลา ณ ตอนนี้] และ Entity MUST ต่ออายุก่อนหมดอายุ เพื่อไม่ให้การเข้าถึง Trustlist ถูกระงับ - -#### 6.1.1.1 การใช้งาน Custom DID - -โดยปกติ Entity สามารถใช้ DID Method มาตรฐานที่ Resolver ส่วนกลางของ ETDA รองรับอยู่แล้ว ในกรณีที่ Entity ประสงค์จะใช้ **Custom DID Method** ซึ่ง Resolver ส่วนกลางยังไม่รองรับ Entity จะต้องประสานงานกับ ETDA เพิ่มเติมตามขั้นตอนดังต่อไปนี้ - -#### ขั้นตอนทั่วไป - -| ขั้น | ผู้ดำเนินการ | รายละเอียด | -|------|-------------|-----------| -| 1 | Entity | ในขั้นตอนการลงทะเบียนกับ ETDA Entity ต้องระบุอย่างชัดเจนว่าจะใช้ Custom DID Method ใด | -| 2 | ETDA | ETDA จะติดต่อกลับเพื่อหารือและกำหนดรูปแบบการเชื่อมต่อ (Integration) ร่วมกับ Entity ตามทางเลือกที่เหมาะสม | - -#### รายละเอียดทางเทคนิค - -รูปแบบการ Resolve DID ในปัจจุบันเป็นลักษณะ **Entity ↔ Gateway ↔ Resolver** กล่าวคือ Consumer (Wallet / Verifier / Issuer) จะเรียกใช้งานผ่าน API Gateway ของ ETDA ซึ่ง Gateway จะส่งต่อคำขอไปยัง Resolver ส่วนกลางเพื่อ Resolve DID - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - C["Consumer
(Wallet / Verifier / Issuer)"] - subgraph etda["ETDA Trust Boundary"] - GW["API Gateway"] - RES["Resolver
(ส่วนกลาง)"] - end - C -- "1. ขอ Resolve DID" --> GW - GW -- "2. ส่งต่อคำขอ" --> RES - RES -- "3. คืน DID Document" --> GW - GW -- "4. ส่งคืน DID Document" --> C -``` - -สำหรับการรองรับ Custom DID Method ETDA กำหนดทางเลือกในการเชื่อมต่อไว้ **2 รูปแบบ** ดังนี้ - -**รูปแบบที่ 1 — Entity จัดส่ง DID Driver ให้ ETDA ติดตั้งและให้บริการ** - -Entity จัดเตรียม DID Driver ตามมาตรฐาน และจัดส่งให้ ETDA เพื่อนำไปติดตั้ง (Deploy) ในระบบ Resolver ส่วนกลาง หลังจากนั้น Resolver ของ ETDA จะสามารถ Resolve Custom DID ได้โดยตรง - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - subgraph ent["Entity (เจ้าของ Custom DID)"] - E["Entity"] - DRV["DID Driver
(จัดส่งให้ ETDA)"] - end - subgraph etda["ETDA Trust Boundary"] - GW["API Gateway"] - RES["Resolver"] - end - C["Wallet / Verifier / Issuer"] - E -. "1. ลงทะเบียน: ระบุ Custom DID Method" .-> GW - DRV -- "2. จัดส่ง Driver ให้ ETDA ติดตั้ง" --> RES - C -- "3. Resolve Custom DID" --> GW - GW <-- "4. Resolve ผ่าน Driver" --> RES - GW -- "5. ส่งคืน DID Document" --> C -``` - -> **แนวทางการพัฒนา DID Driver (โดยสังเขป)** -> -> DID Driver ของ ETDA อ้างอิงตามแนวทางของ [DIF Universal Resolver](https://github.com/decentralized-identity/universal-resolver) โดยมีหลักการสำคัญดังนี้: -> -> - **รูปแบบ:** Driver คือ Docker image ที่เปิดให้เรียกใช้งานผ่าน HTTP interface -> - **มาตรฐาน DID Method:** DID Method ที่จะรองรับควรจดทะเบียนใน [W3C DID Method Registry](https://w3c.github.io/did-spec-registries/#did-methods) - -> **แหล่งศึกษาเพิ่มเติม:** -> - [Universal Resolver — Driver Development Guide](https://github.com/decentralized-identity/universal-resolver/blob/main/docs/driver-development.md) -> - [ตัวอย่าง DID Driver (uni-resolver-driver-did-example)](https://github.com/peacekeeper/uni-resolver-driver-did-example) -> - [DID Resolution Specification (W3C)](https://w3c.github.io/did-resolution/) - -**รูปแบบที่ 2 — Entity ให้บริการ Resolver ของตนเอง และอนุญาต IP (Whitelist) ของ ETDA** - -Entity ให้บริการ Resolver ของตนเอง โดย Entity จะดำเนินการอนุญาต (Whitelist) IP Address ของ ETDA Gateway เพื่อให้สามารถเชื่อมต่อกับ Resolver ของ Entity ได้ เมื่อมีคำขอ Resolve Custom DID กระบวนการเป็นดังนี้ - -- Entity ให้บริการ (Host) Resolver ของตนเอง -- API Gateway ของ ETDA ส่งคำขอ (DID) ไปยัง Resolver ของ Entity -- Resolver ของ Entity ดำเนินการ Resolve และส่งคืน DID Document กลับมายัง Gateway - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - subgraph ent["Entity (เจ้าของ Custom DID)"] - ER["Entity Resolver
(ให้บริการเอง)"] - end - subgraph etda["ETDA Trust Boundary"] - GW["API Gateway
(Whitelist IP โดย Entity)"] - end - C["Wallet / Verifier / Issuer"] - C -- "1. Resolve Custom DID" --> GW - GW -- "2. ส่งคำขอ (DID) ไปยัง Entity Resolver" --> ER - ER -- "3. Resolve และส่งคืน DID Document" --> GW - GW -- "4. ส่งคืน DID Document" --> C -``` - -**ตารางเปรียบเทียบทั้ง 2 รูปแบบ:** - -| ประเด็น | รูปแบบที่ 1 — DID Driver | รูปแบบที่ 2 — Delegated Resolver | -|---------|-------------------------|--------------------------------| -| ผู้ให้บริการ Resolver | ETDA (ใน Resolver ส่วนกลาง) | Entity (ให้บริการเอง) | -| สิ่งที่ Entity ต้องจัดส่ง | DID Driver | Endpoint | -| เหมาะสำหรับ | Entity ที่พัฒนา Driver ตามมาตรฐานได้ | Entity ที่มีระบบ Resolver ของตนเองอยู่แล้ว | - -ต่อไปดู [§ 6.2 ข้อกำหนดด้านความปลอดภัย](02-security-requirements.md) สำหรับมาตรการปกป้อง key และการรับมือภัยคุกคามที่ระบบนี้ใช้อยู่ diff --git a/docs/trust-guide/06-security-and-privacy/02-security-requirements.md b/docs/trust-guide/06-security-and-privacy/02-security-requirements.md deleted file mode 100644 index e963fbc..0000000 --- a/docs/trust-guide/06-security-and-privacy/02-security-requirements.md +++ /dev/null @@ -1,103 +0,0 @@ -# 6.2 ข้อกำหนดด้านความปลอดภัย - -ต่อจาก [§ 6.1 ระดับความเชื่อมั่นของ Verifier และการลงทะเบียน](01-verifier-confidence-and-registration.md) บทนี้อธิบายมาตรการความปลอดภัยที่ระบบใช้จริง ตั้งแต่การเก็บ key ไปจนถึงนโยบายรับมือเมื่อระบบล่ม - -## 6.2 ข้อกำหนดด้านความปลอดภัย - -ETDA ยังไม่ได้ออกมาตรฐานความปลอดภัยฉบับเต็ม ให้ยึดตามที่ OID4VCI และ OID4VP แนะนำเป็นหลัก ด้านล่างคือมาตรการที่ระบบนี้ใช้อยู่แล้ว (ดึงจาก flow ใน § 3 และ schema ใน § 5.2) - -### 6.2.1 มาตรฐาน Cryptographic - -ETDA ยังไม่ได้ออกมาตรฐานความปลอดภัยฉบับเต็ม ให้ยึดตามที่ OID4VCI และ OID4VP แนะนำเป็นหลัก - -| ใช้ที่ไหน | Algorithm | Key | -|-----------|-----------|-----| -| Digest(Hash) ของ selective disclosure | SHA-256 | — | -| ช่องทางเชื่อมต่อ Gateway | TLS 1.3 | — | - -### 6.2.2 การจัดการและเก็บ Key - -- Private key ของ Wallet (DPoP key, Holder key) MUST เก็บใน Trusted Execution Environment (TEE เช่น Strongbox หรือ Secure Enclave) ห้ามส่งออกจากเครื่อง -- Issuer / Wallet Provider / Verifier ส่ง `signing_pubkey` ให้ ETDA ตรวจตอนลงทะเบียน ETDA เก็บไว้ใน Trusted List -- แต่ละ service มี keypair แยกกัน (ดู § 5.2.1) ถ้า key ใดมีปัญหา จะ suspend หรือ revoke เฉพาะ service นั้นได้ โดยไม่กระทบ service อื่น -- ETDA หมุน trust anchor key ได้โดยไม่ต้อง redeploy app Entity ดึง key ใหม่จาก `/.well-known` -- แต่ละ key มี `valid_from` / `valid_until` หลัง `valid_until` ต้องต่ออายุก่อนใช้ต่อ - -**Policy — Verifier ไม่ต้องเก็บ key ใน HSM:** - -- Verifier เก็บ private key แบบ softkey ก็พอ **ไม่บังคับ** ให้เก็บใน HSM -- softkey คือ key ที่เก็บเป็นไฟล์ในเครื่อง ไม่ต้องมีฮาร์ดแวร์พิเศษ -- HSM (Hardware Security Module) คือกล่องฮาร์ดแวร์ที่เก็บ key ให้ปลอดภัยสูง แต่ราคาแพง -- key ของ Verifier ใช้แค่เซ็นคำขอ (VP request) ตอนขอดูบัตร ถ้า key หลุด ETDA แค่ revoke key เก่า แล้วให้ Verifier ทำ key ใหม่ -- ใครอยากใช้ HSM เพื่อความปลอดภัยเพิ่มขึ้นก็ทำได้ แต่ไม่ใช่ข้อบังคับ - -**Policy — ข้อกำหนดการเก็บ signing key สำหรับ Issuer:** - -Issuer เป็นผู้เซ็น VC โดยตรง จึงเป็นจุดที่มีความอ่อนไหวสูงสุดในระบบ (ต่างจาก Verifier ที่เซ็นแค่คำขอดูบัตร) ข้อกำหนดการเก็บ Issuer signing key จึงเข้มงวดกว่า Verifier ตามระดับ LoA ของ credential ที่ออก (อ้างอิงตาราง LoA ที่ [§ 3.2.6.1.1](../03-issuance-flow/02-full-flow/07-data-flow-and-policy.md#32611-loa-แต่ละระดับหมายถึงอะไร)): - -| LoA ของ credential ที่ออก | ข้อกำหนดการเก็บ Issuer signing key | -|---------------------------|-------------------------------------| -| LoA 1–2 | softkey (เก็บเป็นไฟล์ในเครื่อง) ยอมรับได้ แต่ **SHOULD** มีการควบคุมการเข้าถึงที่เหมาะสม (เช่น encrypted at rest, จำกัดสิทธิ์ผู้เข้าถึง) | -| LoA 3–4 (เช่น บัตรประชาชนดิจิทัล) | **MUST** เก็บ signing key ใน HSM | - -- ระดับการรับรอง HSM ที่ ETDA บังคับ (เช่น FIPS 140-2 Level 2 หรือ Level 3) **อยู่ระหว่างการพิจารณาของ ETDA จะประกาศในเวอร์ชันถัดไป** — Issuer ที่ออก credential ระดับ LoA 3–4 ควรวางแผนงบประมาณสำหรับ HSM ไว้ล่วงหน้า และติดตามประกาศฉบับเต็มก่อนขึ้นระบบจริง -- แนวทางการจัดการ key อื่น ๆ (การหมุน key, `valid_from`/`valid_until`, การ revoke เฉพาะ service) ใช้หลักการเดียวกับที่ระบุไว้ข้างต้นสำหรับทุก Entity - -### 6.2.3 Threat Model และการควบคุม - -| ภัย | การควบคุมในระบบ | -|-----|------------------| -| ปลอม Trustlist | Detached JWS เซ็นด้วย ETDA key, entity verify ก่อนใช้ | -| ใช้ Trustlist เก่า | Freshness check ทุกครั้ง — หมดอายุ (`exp`) แล้วดึงใหม่ | -| ใช้บัตรที่ถูกเพิกถอน | ตรวจ IETF Token Status List ก่อนเชื่อ | -| DoS (Denial of Service) | ใช้ API key ในการทำ rate limit — ตัวเลข quota ที่แน่นอนดู § 6.2.3.1 | - -#### 6.2.3.1 Rate Limit / Quota ของ API Gateway - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** ETDA ยังไม่ได้ประกาศตัวเลข rate limit/quota ที่แน่นอนสำหรับ API Gateway (เช่น request/นาที ต่อ API key) - -- ระหว่างนี้ให้ถือว่า rate limit ขึ้นอยู่กับ **tier ของ Entity** ที่ ETDA จัดสรร Entity ที่คาดว่าจะมี throughput สูง (เช่น ออก credential พร้อมกันหลักหมื่นใบ/วัน) **SHOULD** ติดต่อ ETDA ผ่านช่องทางใน [§ 7.5.4](../07-governance-and-conformance/01-conformance-governance-versioning.md#754-ช่องทางติดต่อ) ล่วงหน้าเพื่อขอ quota เฉพาะ (dedicated tier) ก่อนขึ้นระบบจริง -- ดูสรุปหัวข้อ SLA/ค่าใช้จ่ายที่เกี่ยวข้องที่ [§ 7.6](../07-governance-and-conformance/01-conformance-governance-versioning.md#76-sla-และค่าใช้จ่ายในการเข้าร่วมระบบ) - -- API key MUST เก็บเป็นความลับ ใช้ยืนยันตัวตน entity ตอนเรียก Trust Gateway (ดู § 6.1.1) - -### 6.2.4 Audit Log - - -ETDA ยังไม่ได้ออกมาตรฐาน Audit Log ฉบับเต็ม จะถูกเพิ่มเติมในเวอร์ชันหน้า - -- STEP 7 Notification — Wallet แจ้ง Issuer ว่ารับบัตรแล้ว (`event: credential_accepted`) Issuer เก็บไว้เป็น audit log และ analytics -- Entity SHOULD เก็บ log ของทุกจุดตรวจ trust (pass / fail) ไว้ตรวจย้อนหลัง - -### 6.2.5 Policy: เมื่อ Wallet ตรวจ Issuer หรือ Verifier ไม่ได้ - -บางครั้ง Wallet ตรวจ Issuer หรือ Verifier ไม่ได้ เช่น เปิดเน็ตไม่ได้ ดึง Trustlist ไม่ทัน หรือหา key มาตรวจไม่เจอ Policy นี้บอกว่าต้องทำอย่างไร - -**Policy (default = ทิ้ง):** - -- ถ้า Wallet ตรวจ trust ไม่ได้ → **ทิ้งทันที ไม่เตือน** นี่คือค่า default (แบบนี้เรียกว่า fail-closed — ไม่ชัวร์ ก็ไม่เชื่อ) -- ตัวเลือกนี้ตั้งค่า (config) ได้ 2 แบบ: - - `drop` (ค่า default) — ทิ้งเงียบ ๆ ไม่แจ้งผู้ใช้ - - `warn` — เตือนผู้ใช้ก่อน แล้วให้ผู้ใช้เลือกเองว่าจะไปต่อหรือไม่ -- ถ้าไม่ได้ตั้งค่าอะไรเลย → ใช้ `drop` เสมอ - -**ทำไม default เป็นทิ้ง:** เพื่อความปลอดภัยไว้ก่อน ถ้าตรวจไม่ได้ แปลว่าเราไม่รู้ว่าอีกฝ่ายเชื่อถือได้จริงหรือไม่ การทิ้งดีกว่าการเสี่ยงเชื่อคนปลอม - -### 6.2.6 Scenario: ถ้าระบบกลางล่ม จะทำยังไง - -ถ้า ETDA หรือ Issuer ล่ม (เปิดใช้งานไม่ได้ชั่วคราว) Wallet ก็ยังต้องใช้งานต่อได้ ไม่ค้าง - -**วิธีแก้: Wallet เก็บ cache ไว้ล่วงหน้า** Wallet เก็บ 2 อย่างนี้ไว้ในเครื่อง: - -1. **Public key** ของ Issuer และ Verifier (ได้จาก Trustlist ที่ดึงมาก่อนหน้า) -2. **Status list** ล่าสุด (บอกว่าบัตรใบไหนยังใช้ได้ ใบไหนถูกยกเลิก) - -**ตอนระบบล่ม:** - -- Wallet ใช้ public key จาก cache ตรวจลายเซ็นได้เลย ไม่ต้องต่อเน็ต -- Wallet ใช้ status list จาก cache เช็คสถานะบัตรได้ -- ทุก cache มีวันหมดอายุ (TTL) ถ้ายังไม่หมดอายุ ใช้ได้ - -**ข้อควรระวัง:** ตอนใช้ cache Wallet จะไม่เห็นการเปลี่ยนแปลงใหม่ ๆ เช่น บัตรที่เพิ่งถูกยกเลิกหลังจากดึง cache เมื่อ cache หมดอายุแล้วยังต่อเน็ตไม่ได้ → ใช้ Policy ตาม § 6.2.5 (default = ทิ้ง) - -ต่อไปดู [§ 6.3 ความเป็นส่วนตัวและการคุ้มครองข้อมูล](03-privacy-and-data-protection.md) สำหรับมาตรการปกป้องความเป็นส่วนตัวของผู้ถือ VC diff --git a/docs/trust-guide/06-security-and-privacy/03-privacy-and-data-protection.md b/docs/trust-guide/06-security-and-privacy/03-privacy-and-data-protection.md deleted file mode 100644 index 25d48eb..0000000 --- a/docs/trust-guide/06-security-and-privacy/03-privacy-and-data-protection.md +++ /dev/null @@ -1,30 +0,0 @@ -# 6.3 ความเป็นส่วนตัวและการคุ้มครองข้อมูล - -ต่อจาก [§ 6.2 ข้อกำหนดด้านความปลอดภัย](02-security-requirements.md) บทปิดท้ายของบทที่ 6 นี้อธิบายว่าระบบปกป้องความเป็นส่วนตัวของผู้ถือ VC อย่างไร ครอบคลุมทั้งการเปิดเผยข้อมูลขั้นต่ำ ความยินยอมของผู้ใช้ และฐานทางกฎหมาย - -## 6.3 ความเป็นส่วนตัวและการคุ้มครองข้อมูล (เฉพาะของ Trust Framework) - -ETDA ยังไม่ได้ออกมาตรฐานความเป็นส่วนตัวฉบับเต็ม ให้ยึดตามที่ OID4VCI และ OID4VP แนะนำเป็นหลัก ด้านล่างคือสิ่งที่ระบบนี้ทำอยู่แล้ว - -### 6.3.1 Data Minimization และ Selective Disclosure - -- VC ใช้รูปแบบ SD-JWT VC claim ที่อ่อนไหว (เช่น ที่อยู่ รูปภาพ) ถูกซ่อนเป็น digest ใน `_sd` Holder เลือกเปิดเฉพาะ claim ที่จำเป็นผ่าน SD Disclosures -- Verifier ขอ claim ได้เฉพาะที่อยู่ใน `allowed_claims` ของ use case ที่ ETDA อนุมัติ (ดู § 5.3 allowed_claims/allowed_purposes ในตัวอย่าง TL JSON) ขอเกินสิทธิ์ไม่ได้ -- ตัวอย่าง: use case สมัครสินเชื่อ (`UC-BBL-LOAN-001`) ขอได้แค่ `citizen_id`, `name_th`, `address` ไม่เห็น `birth_date` - -### 6.3.2 ความยินยอมและการควบคุมของผู้ใช้ - -- ก่อนส่งข้อมูล Wallet แสดง `purpose_th` / `purpose_en` ของ Verifier ให้ผู้ใช้อ่านและกดยินยอม -- Holder เป็นคนเลือกว่าจะแสดง credential ใบไหน (OID4VP) ข้อมูลไม่ออกจาก Wallet ถ้าผู้ใช้ไม่ยินยอม -- Wallet ตรวจว่า Verifier อยู่ใน Trustlist ก่อน (ดู § 4 จุด 2.6) ถ้าไม่ผ่าน หยุดทันที ไม่ส่งข้อมูล - -### 6.3.3 Unlinkability / ความเสี่ยง Correlation - -- ETDA ยังไม่มีมาตรการเฉพาะเรื่อง unlinkability (เช่น batch issuance เพื่อป้องกันการติดตามข้าม transaction) ให้ยึดแนวทาง SD-JWT VC และ OID4VP - -### 6.3.4 ฐานทางกฎหมายและการเก็บข้อมูล - -- แต่ละ use case ของ Verifier ต้องระบุวัตถุประสงค์ที่ชัดเจน เช่น "ยืนยันตัวตนตาม พ.ร.บ.ป้องกันฟอกเงิน" (ดู § 5.3 allowed_claims/allowed_purposes ในตัวอย่าง TL JSON) ใช้เป็นฐานทางกฎหมายของการขอข้อมูล -- ETDA ยังไม่ได้กำหนดกฎการเก็บและลบข้อมูล (retention) เฉพาะ Verifier SHOULD เก็บข้อมูลเท่าที่จำเป็นตามวัตถุประสงค์ที่ประกาศไว้เท่านั้น - -จบเนื้อหาบทที่ 6 (ความปลอดภัยและความเป็นส่วนตัว) — บทถัดไปคือ [บทที่ 7 ธรรมาภิบาลและการรับรอง](../07-governance-and-conformance/01-conformance-governance-versioning.md) อธิบายว่าใครเป็นผู้ควบคุมกฎ อนุมัติ และดูแลการเปลี่ยนแปลงเวอร์ชันของระบบ diff --git a/docs/trust-guide/06-security-and-privacy/_category_.json b/docs/trust-guide/06-security-and-privacy/_category_.json deleted file mode 100644 index dc9577c..0000000 --- a/docs/trust-guide/06-security-and-privacy/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "6. ความปลอดภัยและความเป็นส่วนตัว", - "position": 6 -} diff --git a/docs/trust-guide/07-governance-and-conformance/01-conformance-governance-versioning.md b/docs/trust-guide/07-governance-and-conformance/01-conformance-governance-versioning.md deleted file mode 100644 index 2a29b4a..0000000 --- a/docs/trust-guide/07-governance-and-conformance/01-conformance-governance-versioning.md +++ /dev/null @@ -1,185 +0,0 @@ -# 7.1 การตรวจสอบ/รับรอง, ธรรมาภิบาล และการจัดการเวอร์ชัน - -ต่อจาก [บทที่ 6 ความปลอดภัยและความเป็นส่วนตัว](../06-security-and-privacy/01-verifier-confidence-and-registration.md) บทนี้ (บทที่ 7) อธิบายว่าใครเป็นผู้ควบคุมกฎของระบบ (Governance), ขั้นตอนรับรอง/เพิกถอน Entity, และนโยบายจัดการเวอร์ชันเมื่อมีการเปลี่ยนแปลง - -## 7.1 การตรวจสอบและการรับรอง (Conformance & Certification) - -ETDA ยังไม่ได้ออกมาตรฐานการตรวจสอบและการรับรองฉบับเต็ม ด้านล่างนี้เป็นสิ่งที่ทำ ณ ตอนนี้เท่านั้น - -### 7.1.1 การรับรอง - -- ETDA (Governance Authority) เป็นผู้ตรวจรับรอง อนุมัติ และเพิ่ม entity เข้า Trusted List -- ใบรับรองมีอายุตาม `valid_from` – `valid_until` ของแต่ละ service ต้องต่ออายุก่อนหมด -- ETDA suspend หรือ revoke ได้ทั้งระดับองค์กร (org `status`) และระดับบทบาท (service `status`) - -> **TODO:** ระยะเวลามาตรฐานของใบรับรองยังไม่สรุป (ตัวอย่างใน § 5.2 ใช้ 2 ปี แต่ยังไม่ fix) - -## 7.2 ธรรมาภิบาล (Governance) - -ETDA ยังไม่ได้ออกมาตรฐานธรรมาภิบาลฉบับเต็ม ด้านล่างนี้เป็นสิ่งที่ทำ ณ ตอนนี้เท่านั้น - -ETDA เป็น Governance Authority และ Trust Service Provider ETDA วางกฎ อนุมัติผู้เข้าร่วม และดูแลบริการกลาง เช่น Trusted List และ API Key - -### 7.2.1 การเข้าและออกจากระบบ - -- **เข้า:** Entity ลงทะเบียนกับ ETDA → ETDA ตรวจและอนุมัติ → ได้ API key → ถูกเพิ่มเข้า Trusted List ด้วย `status: active` (ดู § 6.1.1, § 5.2.1) -- **ออก:** ETDA suspend หรือ revoke ได้ทั้งระดับองค์กร (org `status`) และระดับบทบาท (service `status`) (ดู § 5.2, § 5.2.1) -- เมื่อ `status` ไม่ใช่ `active` → consumer (Wallet / Verifier / Issuer) ปฏิเสธทันที และคนอื่นจะไม่เชื่อ credential ที่เกี่ยวข้องอีกต่อไป - -**ผลย้อนหลังต่อ credential ที่ออกไปแล้วก่อนถูก revoke:** - -การ revoke ระดับ Entity/Issuer (org หรือ service `status`) **ไม่เท่ากับ** การ revoke credential รายใบผ่าน Status List (ดู § 3.2.5 / § 6.2.3): - -| กรณี | ผลต่อ VC ที่ออกไปแล้วก่อนหน้า (อยู่ใน Wallet ผู้ใช้) | -|------|-------------------------------------------------------| -| Issuer ถูก **suspend** (ชั่วคราว) | VC เดิมยัง verify ผ่านทาง cryptographic signature ได้ แต่ Verifier ที่ตรวจ Trustlist สดจะเห็นว่า Issuer ไม่ `active` ณ ขณะตรวจ — **SHOULD** ปฏิเสธจนกว่า Issuer จะกลับมา `active` | -| Issuer ถูก **revoke** (ถาวร) | เช่นเดียวกับ suspend แต่เป็นการถาวร — VC ที่ออกโดย Issuer นี้ **MUST** ถือว่าไม่น่าเชื่อถืออีกต่อไปสำหรับ verifier ที่ตรวจ Trustlist ตาม timestamp ปัจจุบัน | -| ผู้ใช้ต้องการเพิกถอน VC ใบใดใบหนึ่งโดยเฉพาะ (ไม่ใช่ทั้ง Issuer) | ใช้กลไก Status List ของ VC นั้นโดยตรง (ดู § 3.2.5) — ไม่กระทบ VC ใบอื่นของผู้ใช้คนเดียวกันหรือ Issuer เดียวกัน | - -- Entity/Issuer **MUST** แจ้งผู้ถือ VC (ผ่านช่องทางของตนเอง) หากทราบว่าตนเองถูก suspend/revoke และ VC ที่เคยออกจะไม่ผ่านการตรวจสอบอีกต่อไป -- รายละเอียดกระบวนการอุทธรณ์เพื่อกลับเข้าสู่สถานะ `active` ดู § 7.2.2 - -### 7.2.2 กระบวนการอุทธรณ์และการกลับเข้าสู่ระบบ (Appeal Process) - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ — ขั้นตอนด้านล่างเป็นแนวทางชั่วคราว (interim) ETDA จะประกาศกระบวนการฉบับเต็มในเวอร์ชันถัดไป]** - -เมื่อ Entity ถูก suspend หรือ revoke แนวทางการอุทธรณ์ชั่วคราวมีดังนี้: - -| ขั้น | ผู้ดำเนินการ | รายละเอียด | -|------|-------------|-----------| -| 1 | Entity | ติดต่อ ETDA ผ่านช่องทางที่ระบุใน § 7.5.4 พร้อมระบุเหตุผลที่เชื่อว่าการ suspend/revoke ไม่ถูกต้อง หรือรายงานการแก้ไขปัญหาที่เป็นสาเหตุ | -| 2 | ETDA | พิจารณาคำอุทธรณ์และหลักฐานการแก้ไข | -| 3 | ETDA | แจ้งผลการพิจารณา — คืนสถานะ `active` หรือคงสถานะ suspend/revoke พร้อมเหตุผล | - -- **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** ระยะเวลาการพิจารณาคำอุทธรณ์ — ETDA จะประกาศ SLA ในเวอร์ชันถัดไป -- Entity ที่ถูก revoke ถาวรและต้องการสมัครใหม่ **SHOULD** ดำเนินการตามขั้นตอนสมัครปกติ (§ 7.5) พร้อมชี้แจงประวัติการถูก revoke ครั้งก่อน - -## 7.3 การจัดการเวอร์ชันและการเปลี่ยนแปลง - -ระบบมีเวอร์ชัน 2 ชุดที่ต้องดูแลแยกกัน: เวอร์ชันของ **เอกสาร framework นี้** และเวอร์ชันของ **Trusted List**. - -### 7.3.1 นโยบายเวอร์ชัน - -- **เอกสาร framework:** ใช้เลข `[X.Y]` ที่ header พร้อม Status `Draft` หรือ `Final` (ดูส่วนหัวเอกสาร) -- **Trusted List:** มี `id` แบบ versioned (เช่น `urn:etda:trusted-list:2026-v1`), `@context` ระบุ version ของ TL format, และ `issued` / `next_update` บอกรอบ publish (ดู § 5.2.1) -- **Protocol:** แต่ละ service ประกาศ version ที่รองรับใน `protocols` (เช่น `OID4VCI_1.0`, `OID4VP_1.0`) - -### 7.3.2 Backward Compatibility - -- `protocols` ให้แต่ละ entity บอกว่ารองรับ protocol version ไหน เพิ่ม version ใหม่ได้โดยไม่ทำให้ของเดิมพัง -- ETDA หมุน trust anchor key ได้โดยไม่ต้อง redeploy app -- `valid_from` / `valid_until` ให้ key เก่าและ key ใหม่ทำงานทับกันได้ในช่วงเปลี่ยนผ่าน - -### 7.3.3 นโยบายแจ้งเตือน Breaking Change ล่วงหน้า - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ — แนวทางด้านล่างเป็นข้อเสนอชั่วคราว (interim) ETDA จะยืนยัน/ปรับปรุงในเวอร์ชันถัดไป]** - -| ประเภทการเปลี่ยนแปลง | ตัวอย่าง | ระยะเวลาแจ้งล่วงหน้า (เสนอ) | ช่องทางแจ้ง | -|------------------------|----------|-------------------------------|---------------| -| Breaking change ต่อ schema ของ Trusted List | เปลี่ยนโครงสร้าง field บังคับใน TL entry | **อย่างน้อย 90 วัน** | Trusted List `next_update` + ช่องทางแจ้งข่าวของ ETDA (ดู § 7.5.4) | -| เปลี่ยน protocol version ที่บังคับ | ยกเลิกรองรับ `OID4VCI_1.0` เดิม | **อย่างน้อย 90 วัน** พร้อม deprecation period ที่ทั้ง version เก่า/ใหม่ทำงานคู่กัน | เช่นเดียวกับข้างต้น | -| Non-breaking change (เพิ่ม field/protocol version ใหม่แบบ backward-compatible) | เพิ่ม `OID4VCI_1.1` โดยยังรองรับ `1.0` | ไม่บังคับระยะเวลาแจ้งล่วงหน้า แต่ **SHOULD** ระบุใน Change Log (§ 7.3.4) | Change Log | - -- Entity **SHOULD** ติดตามช่องทางแจ้งข่าวของ ETDA เป็นระยะเพื่อไม่พลาดประกาศ breaking change - -### 7.3.4 Change Log - -- Trusted List publish ตามรอบ — ดู `issued` และ `next_update` Entity ดึงฉบับใหม่เมื่อของเดิมหมดอายุ (freshness check ด้วย `exp`, ดู § 6.2.3) -- **TODO:** รูปแบบ change log ของเอกสาร framework ยังไม่กำหนด - -## 7.4 Conformance Testing - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ — เนื้อหาด้านล่างเป็นแนวทางขั้นต่ำชั่วคราว (interim) ETDA จะประกาศ test suite ฉบับเต็มในเวอร์ชันถัดไป]** - -### 7.4.1 Sandbox / Staging Environment - -- **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** Endpoint ของ Sandbox/Staging แยกจาก Production (`trust.etda.or.th`) — ETDA จะประกาศ URL ที่แน่นอนพร้อมขั้นตอนสมัครสิทธิ์เข้าใช้งานในเวอร์ชันถัดไป -- แนวคิดที่วางแผนไว้: Entity ใหม่ควรได้ทดลอง integrate กับ Sandbox ก่อนขึ้น Production เสมอ เพื่อลดความเสี่ยงจาก integration bug ในระบบจริง - -### 7.4.2 Test Suite ขั้นต่ำที่ควรผ่าน (แนวทางชั่วคราว) - -| # | Test Case | ผลลัพธ์ที่คาดหวัง | -|---|-----------|---------------------| -| 1 | ออก VC ตัวอย่าง (ผ่าน OID4VCI flow เต็ม) ใน Sandbox | ได้ VC ที่ signature valid และ schema ตรงตาม `credential_type` ที่ขออนุมัติ | -| 2 | Wallet/Verifier ดึง Trustlist ผ่าน Sandbox Trust Gateway | ได้ Trustlist ที่มี entry ของ Entity ตัวเองครบถ้วน | -| 3 | Verifier ตรวจ VP request/response ผ่าน OID4VP flow เต็ม | ผ่านทุกจุดตรวจใน Common Verification Pattern (ดู [1.4 หลักการตรวจสอบ — ใครตรวจใคร](../01-concepts-and-roles/03-common-verification-pattern.md)) | -| 4 | จำลอง entity ที่ `status` ไม่ใช่ `active` | consumer ปฏิเสธ credential ที่เกี่ยวข้องตามที่คาดไว้ (ดู § 7.2.1) | - -- **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** เกณฑ์ผ่าน/ไม่ผ่านที่เป็นทางการ และจำนวน test case เต็มรูปแบบ — ETDA จะประกาศเพิ่มเติม - -## 7.5 ขั้นตอนสมัครเป็น Issuer - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการทั้งหมด — เนื้อหาด้านล่างรวบรวมจากขั้นตอนที่ทำอยู่ ณ ปัจจุบัน (ดู § 6.1.1) และเติมช่องว่างด้วยแนวทางชั่วคราว (interim) ที่มี placeholder ชัดเจนสำหรับส่วนที่ ETDA ยังไม่ประกาศ]** - -ภาพรวมขั้นตอนสำหรับองค์กร (ทั้งภาครัฐและเอกชนที่ผ่านเกณฑ์ ETDA — ดู `01-concepts-and-roles/01-glossary.md`) ที่ต้องการสมัครเป็น **Issuer**: - -### 7.5.1 เอกสารที่ต้องเตรียม - -| เอกสาร/ข้อมูล | หมายเหตุ | -|-----------------|----------| -| ข้อมูลองค์กร (นิติบุคคล, ใบอนุญาตประกอบธุรกิจที่เกี่ยวข้อง) | **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ — รายการใบอนุญาตที่ ETDA ต้องการจะประกาศตามประเภทธุรกิจ]** | -| Public key / DID ที่จะใช้เซ็น VC | ดู § 6.1.1.1 กรณีใช้ Custom DID | -| รายชื่อ `credential_type` ที่ต้องการขออนุมัติออก | อ้างอิงตัวอย่างที่ `05-trust-infrastructure/02-tl-json-examples.md` | -| ระดับ LoA ของแต่ละ `credential_type` ที่จะออก | ใช้กำหนดข้อกำหนด HSM ที่ต้องปฏิบัติตาม (ดู § 6.2.2 ตาราง LoA ↔ HSM) | -| แผนการเก็บ signing key (softkey/HSM) ตามระดับ LoA | ดู § 6.2.2 | - -### 7.5.2 เกณฑ์ที่ ETDA ใช้ตรวจสอบ (Audit) - -- ตรวจความครบถ้วนของเอกสารตาม § 7.5.1 -- ตรวจ security assessment เบื้องต้น (รายละเอียดเกณฑ์เต็ม **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]**) -- ตรวจผ่าน Conformance Testing ใน Sandbox ตาม § 7.4 ก่อนอนุมัติเข้า Production Trustlist - -### 7.5.3 ระยะเวลาโดยรวม - -| ขั้นตอน | ระยะเวลาโดยประมาณ | -|---------|----------------------| -| เตรียมเอกสาร (Entity) | ~1 วัน ถึงหลายสัปดาห์ ขึ้นกับความพร้อมขององค์กร | -| ETDA ตรวจและอนุมัติ (Audit) | **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** | -| ทดสอบใน Sandbox (Conformance Testing) | **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** | -| ออก API key + เพิ่มเข้า Production Trustlist | ทันทีหลังผ่านทุกขั้นตอนข้างต้น | - -> ETDA จะประกาศ SLA ที่เป็นตัวเลขชัดเจนของแต่ละขั้นตอนในเวอร์ชันถัดไป ผู้สมัคร Issuer ควรวางแผน timeline ธุรกิจโดยเผื่อระยะเวลาที่ยังไม่ fix นี้ไว้ - -### 7.5.4 ช่องทางติดต่อ - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ — หากยังไม่มีช่องทางที่ระบุไว้ ณ ที่นี้ ให้ถือว่าอยู่ระหว่างการพิจารณาของ ETDA จะประกาศในเวอร์ชันถัดไป]** - -- อีเมล/เว็บไซต์ลงทะเบียนอย่างเป็นทางการของ ETDA: **[รอ ETDA ประกาศ]** -- ช่องทางนี้ใช้สำหรับ: สอบถามการสมัคร, ยื่นคำขอ Custom DID (§ 6.1.1.1), ยื่นคำอุทธรณ์ (§ 7.2.2), ยื่นคำขอเพิ่ม `credential_type` (§ 7.5.5) - -### 7.5.5 การขอเพิ่ม `credential_type` หลังผ่านการอนุมัติครั้งแรก - -Issuer ที่ต้องการออก `credential_type` เพิ่มเติมจากที่ได้รับอนุมัติครั้งแรก **ไม่ต้องสมัครใหม่ทั้งหมด** — ดำเนินการยื่นคำขอแก้ไข (amendment) ดังนี้: - -| ขั้น | ผู้ดำเนินการ | รายละเอียด | -|------|-------------|-----------| -| 1 | Entity | ยื่นคำขอเพิ่ม `credential_type` ผ่านช่องทางใน § 7.5.4 พร้อมระบุ LoA ของ credential type ใหม่ | -| 2 | ETDA | ตรวจสอบตามเกณฑ์ § 7.5.2 เฉพาะส่วนที่เกี่ยวกับ credential type ใหม่ (ไม่ต้องตรวจซ้ำทั้งองค์กร) | -| 3 | ETDA | อัปเดต `allowed_credential_types` ของ Entity ใน Trusted List | - -- **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** ระยะเวลาของกระบวนการ amendment — คาดว่าเร็วกว่าการสมัครใหม่เนื่องจากไม่ต้องตรวจข้อมูลองค์กรซ้ำ - -## 7.6 SLA และค่าใช้จ่ายในการเข้าร่วมระบบ - -> **[ยังไม่ได้กำหนดค่ามาตรฐานอย่างเป็นทางการ]** - -| หัวข้อ | สถานะ | -|--------|-------| -| Uptime เป้าหมายของ ETDA Trust Gateway / Resolver | **[รอ ETDA ประกาศ]** | -| Response time เป้าหมายของ API | **[รอ ETDA ประกาศ]** | -| ค่าใช้จ่ายในการลงทะเบียน/เข้าร่วมระบบ | **[รอ ETDA ประกาศ — ยังไม่ยืนยันว่าฟรีหรือมีค่าใช้จ่าย]** | -| ตัวเลข Rate Limit / Quota ของ API Gateway | **[รอ ETDA ประกาศ]** — ระหว่างนี้ Entity ที่คาดว่าจะมี throughput สูง (เช่น ออกบัตรพร้อมกันหลักหมื่นใบ/วัน) **SHOULD** ติดต่อ ETDA ผ่าน § 7.5.4 เพื่อขอ quota เฉพาะ (dedicated tier) | - -## 7.7 Audit Log ขั้นต่ำชั่วคราว (Interim Minimum) - -ETDA ยังไม่ได้ออกมาตรฐาน Audit Log ฉบับเต็ม (ดู § 6.2.4) ระหว่างรอประกาศ Issuer/Entity **SHOULD** เก็บ audit log ขั้นต่ำดังนี้เพื่อลดความเสี่ยง rework เมื่อมาตรฐานเต็มออกมา: - -| รายการ | คำแนะนำขั้นต่ำชั่วคราว | -|--------|---------------------------| -| เหตุการณ์ที่ควรบันทึก | การออก VC ทุกครั้ง (พร้อม credential ID), การตรวจ/ปฏิเสธ VP ทุกครั้ง, การดึง Trustlist ใหม่ทุกครั้ง, การเปลี่ยนแปลง signing key | -| ระยะเวลาเก็บ | อย่างน้อย 1 ปี (ปรับเพิ่มได้เมื่อ ETDA ประกาศมาตรฐานจริง) | -| รูปแบบ | JSON หรือ structured log ที่ query ย้อนหลังได้ (ยังไม่บังคับ schema ตายตัว) | - -> คำแนะนำนี้เป็นแนวทางชั่วคราวเท่านั้น ไม่ใช่ข้อบังคับที่ ETDA ประกาศอย่างเป็นทางการ - -จบเนื้อหาบทที่ 7 (ธรรมาภิบาลและการรับรอง) — บทสุดท้ายของคู่มือนี้คือ [บทที่ 8 คำถามที่พบบ่อย](../08-faq/01-framework-faq.md) รวมคำถาม-คำตอบสั้น ๆ ที่ผู้อ่านมักสงสัยหลังอ่านมาถึงจุดนี้ diff --git a/docs/trust-guide/07-governance-and-conformance/_category_.json b/docs/trust-guide/07-governance-and-conformance/_category_.json deleted file mode 100644 index 142b7ba..0000000 --- a/docs/trust-guide/07-governance-and-conformance/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "7. ธรรมาภิบาลและการรับรอง", - "position": 7 -} diff --git a/docs/trust-guide/08-faq/01-framework-faq.md b/docs/trust-guide/08-faq/01-framework-faq.md deleted file mode 100644 index 6f1456b..0000000 --- a/docs/trust-guide/08-faq/01-framework-faq.md +++ /dev/null @@ -1,41 +0,0 @@ -# 8.1 คำถามที่พบบ่อย (FAQ) — Trust Framework - -ต่อจาก [บทที่ 7 ธรรมาภิบาลและการรับรอง](../07-governance-and-conformance/01-conformance-governance-versioning.md) นี่คือบทสุดท้ายของคู่มือ รวมคำถามที่ผู้อ่านมักสงสัยหลังอ่านเนื้อหาทางเทคนิคมาจนถึงจุดนี้ พร้อมลิงก์กลับไปยังหัวข้อที่เกี่ยวข้องในบทก่อนหน้า - -## 8.1 คำถามที่พบบ่อย (FAQ) - -**Q: ใครต้องทำตาม framework นี้?** -A: ทุก Entity ที่เข้าร่วม ecosystem ได้แก่ Issuer, Wallet และ Verifier โดยมี ETDA เป็นผู้กำกับดูแล - -**Q: ถ้าไม่ทำตามจะเกิดอะไรขึ้น?** -A: อาจไม่ได้รับอนุมัติให้เข้าร่วม หรือถูกถอดออกจาก Trustlist ทำให้คนอื่นไม่เชื่อ credential ที่เกี่ยวข้องอีกต่อไป - -**Q: Verifier รู้ได้อย่างไรว่า credential ถูกเพิกถอนแล้ว?** -A: Verifier ดึง Status List ที่ Issuer publish ไว้มาเช็ค ถ้าสถานะคือเพิกถอน ก็จะไม่รับ credential นั้น - -**Q: Registries (Status List, Schema) อยู่ในขอบเขตของ trust model หรือไม่?** -A: ไม่อยู่ เพราะเป็นเรื่อง data integrity และ interoperability ระหว่าง Issuer กับ Verifier ใส่ไว้เพียงเพื่อให้ diagram ครบเท่านั้น - -**Q: framework นี้ใช้ร่วมกับ EUDI ได้เลยหรือไม่?** -A: ใช้มาตรฐานเดียวกัน (OID4VCI, OID4VP, SD-JWT VC) แต่การข้ามพรมแดนต้องมีข้อตกลง trust ระหว่างกันก่อน - -**Q: อยากเข้าร่วมต้องทำอย่างไร?** -A: ลงทะเบียนกับ ETDA เพื่อรับ API key (ดูขั้นตอนการลงทะเบียนที่ [§ 6.1.1](../06-security-and-privacy/01-verifier-confidence-and-registration.md#611-การลงทะเบียน-registration)) ผ่านการตรวจ conformance (ดู [§ 7.4 Conformance Testing](../07-governance-and-conformance/01-conformance-governance-versioning.md#74-conformance-testing)) แล้วจะถูกเพิ่มเข้า Trustlist สำหรับขั้นตอนสมัครเป็น Issuer แบบละเอียด (เอกสารที่ต้องยื่น, เกณฑ์ตรวจสอบ, timeline) ดูที่ [§ 7.5 ขั้นตอนสมัครเป็น Issuer](../07-governance-and-conformance/01-conformance-governance-versioning.md#75-ขั้นตอนสมัครเป็น-issuer) - -**Q: ติดต่อ ETDA เพื่อสอบถามเรื่องการสมัครได้ทางไหน?** -A: ดูช่องทางติดต่ออย่างเป็นทางการที่ [§ 7.5.4 ช่องทางติดต่อ](../07-governance-and-conformance/01-conformance-governance-versioning.md#754-ช่องทางติดต่อ) — หากยังไม่มีช่องทางที่ระบุไว้ ให้ถือว่า **อยู่ระหว่างการพิจารณาของ ETDA จะประกาศในเวอร์ชันถัดไป** - ---- - -**Q: ถ้าเพิ่งเป็นเอกชนอยากสมัครเป็น Issuer ทำได้ไหม ไม่ใช่แค่หน่วยงานรัฐ?** -A: ได้ — ทั้งหน่วยงานรัฐและองค์กรเอกชนที่ผ่านเกณฑ์การตรวจสอบของ ETDA สมัครเป็น Issuer ได้ทั้งคู่ (ดูตัวอย่างธนาคารเอกชนที่เป็น Issuer ที่ [§ 5.3.3](../05-trust-infrastructure/02-tl-json-examples.md#533-tl-json--multi-role-organization-bbl--issuer--verifier) และนิยาม Issuer ที่ [§ 1.1 คำศัพท์และนิยาม](../01-concepts-and-roles/01-glossary.md)) - ---- - -> **เอกสารที่เกี่ยวข้อง:** -> - [OpenID4VCI 1.0 — OpenID for Verifiable Credential Issuance](https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-1_0-wg-draft.html) -> - [OpenID4VP 1.0 — OpenID for Verifiable Presentations](https://openid.github.io/OpenID4VP/openid-4-verifiable-presentations-1_0-wg-draft.html) -> - [OpenID4VC HAIP 1.1 — High Assurance Interoperability Profile](https://openid.github.io/OpenID4VC-HAIP/openid4vc-high-assurance-interoperability-profile-1_1-wg-draft.html) -> - [EUDI ARF 2.9.0 — Architecture and Reference Framework](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework) -> - [EUDI — Wallet Unit Attestation (discussion topic)](https://eudi.dev/latest/discussion-topics/c-wallet-unit-attestation/) -> - [EUDI TS3 — Wallet Unit Attestation (technical specification)](https://github.com/eu-digital-identity-wallet/eudi-doc-standards-and-technical-specifications/blob/main/docs/technical-specifications/ts3-wallet-unit-attestation.md#2-solution-description) diff --git a/docs/trust-guide/08-faq/_category_.json b/docs/trust-guide/08-faq/_category_.json deleted file mode 100644 index 3b62a2d..0000000 --- a/docs/trust-guide/08-faq/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "8. คำถามที่พบบ่อย", - "position": 8 -} diff --git a/docs/trust-guide/09-appendix/01-core-security-rules.md b/docs/trust-guide/09-appendix/01-core-security-rules.md deleted file mode 100644 index 2a29a46..0000000 --- a/docs/trust-guide/09-appendix/01-core-security-rules.md +++ /dev/null @@ -1,280 +0,0 @@ -# ภาคผนวก ก — Trust List ถูกใช้ที่ไหนบ้าง และหลักการตรวจสอบความปลอดภัยพื้นฐาน (Core Security Rules) - -ภาคผนวกนี้รวม **แผนที่จุดตรวจ Trust List ทั้งหมด** และ **ลำดับการตรวจสอบความปลอดภัยพื้นฐาน (Core Security Rules)** ที่ทุก flow ในคู่มือใช้ร่วมกัน แยกมาไว้ท้ายเล่มเพื่อให้ใช้เป็นแผ่นสรุป (cheat sheet) ที่เปิดดูได้เร็ว โดยไม่ต้องไล่อ่านทีละบท - -เนื้อหาส่วนนี้เคยอยู่ในเนื้อบทหลัก ก่อนถูกย้ายมารวมไว้เป็นภาคผนวก ดูรายละเอียดของแต่ละจุดในบริบทจริงได้ที่ [บทที่ 3 การออกเอกสาร (OID4VCI)](../03-issuance-flow/01-minimal-flow.md) และ [บทที่ 4 การแสดงเอกสาร (OID4VP)](../04-presentation-flow/01-overview/index.md) - -## ก.1 ภาพรวม — Trust List ถูกใช้ทุกครั้งที่ต้องตัดสินใจเรื่อง trust (จุดตรวจที่ 1–6) - -แต่ละ Entity นำ Trust List ที่ cache ไว้ ไปใช้ตรวจสอบคู่สื่อสาร — แบ่งเป็น 2 flow รวม **จุดตรวจที่ 1–6** เรียงตามลำดับเวลา (เลขในวงเล็บคือ step ใน flow เต็มของ [บทที่ 3](../03-issuance-flow/02-full-flow/02b-full-flow-technical-detail.md) และ [บทที่ 4](../04-presentation-flow/02-full-flow/02b-full-flow-technical-detail.md)) - -**Flow ออก VC (OID4VCI) — จุดตรวจที่ 1–3:** - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart TB - subgraph S0["🟡 จุดตรวจที่ 1 — Wallet ตรวจ Issuer ก่อนขอบัตร (STEP 3.1)"] - direction LR - WAL0["📱 Wallet"] -->|"① รับ Credential Offer\n(สแกน QR)"| CHK0 - CHK0{"② เช็คว่า Issuer\nอยู่ใน Trust List"} - CHK0 -->|"✅ อยู่"| OK0["③ ให้ User กรอก\nTransaction Code ต่อ"] - CHK0 -->|"❌ ไม่อยู่"| NO0["③ หยุด ไม่ส่ง\nTransaction Code"] - end - - subgraph S1["🟠 จุดตรวจที่ 2 — Issuer ตรวจ Wallet Provider (STEP 5.5)"] - direction LR - ISS1["🏛️ Issuer"] -->|"① Wallet ส่ง\nWallet Attestation"| CHK1 - CHK1{"② เช็คว่า Wallet Provider\nอยู่ใน Trust List"} - CHK1 -->|"✅ อยู่"| OK1["③ ออก VC ให้"] - CHK1 -->|"❌ ไม่อยู่"| NO1["③ ปฏิเสธ"] - end - - subgraph S2["🔵 จุดตรวจที่ 3 — Wallet ตรวจ Issuer ก่อนบันทึก (STEP 6.5)"] - direction LR - WAL2["📱 Wallet"] -->|"① รับ VC จาก Issuer"| CHK2 - CHK2{"② เช็คว่า Issuer\nอยู่ใน Trust List"} - CHK2 -->|"✅ อยู่"| OK2["③ บันทึก VC"] - CHK2 -->|"❌ ไม่อยู่"| NO2["③ ทิ้ง VC"] - end - - S0 ~~~ S1 ~~~ S2 - - style CHK0 fill:#FFFDE7,stroke:#F9A825 - style CHK1 fill:#FFF3E0,stroke:#E65100 - style CHK2 fill:#E3F2FD,stroke:#1565C0 - style OK0 fill:#C8E6C9,stroke:#2E7D32 - style OK1 fill:#C8E6C9,stroke:#2E7D32 - style OK2 fill:#C8E6C9,stroke:#2E7D32 - style NO0 fill:#FFCDD2,stroke:#C62828 - style NO1 fill:#FFCDD2,stroke:#C62828 - style NO2 fill:#FFCDD2,stroke:#C62828 -``` - -**Flow แสดง VP (OID4VP) — จุดตรวจที่ 4–6:** - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart TB - subgraph S3["🟣 จุดตรวจที่ 4 — Wallet ตรวจ Verifier ก่อนส่งข้อมูล (STEP 2.6)"] - direction LR - WAL3["📱 Wallet"] -->|"① Verifier ส่งคำขอ VP"| CHK3 - CHK3{"② เช็คว่า Verifier\nอยู่ใน Trust List"} - CHK3 -->|"✅ อยู่"| OK3["③ ขอ consent User\nแล้วส่ง VP"] - CHK3 -->|"❌ ไม่อยู่"| NO3["③ ไม่ส่งข้อมูล"] - end - - subgraph S5["🟤 จุดตรวจที่ 5 — Verifier ตรวจ Wallet Provider (STEP 4.1, บังคับเฉพาะ Wallet ของรัฐ)"] - direction LR - VER5["🔍 Verifier"] -->|"① รับ VP + ตรวจที่มา\nของ Wallet"| CHK5 - CHK5{"② เช็คว่า Wallet Provider\nอยู่ใน Trust List"} - CHK5 -->|"✅ อยู่"| OK5["③ ตรวจขั้นถัดไป"] - CHK5 -->|"❌ ไม่อยู่"| NO5["③ ปฏิเสธ"] - end - - subgraph S4["🟢 จุดตรวจที่ 6 — Verifier ตรวจ Issuer (STEP 4.2)"] - direction LR - VER4["🔍 Verifier"] -->|"① ตรวจ VC ที่อยู่ใน VP"| CHK4 - CHK4{"② เช็คว่า Issuer ที่ออก VC\nอยู่ใน Trust List\n+ status + สิทธิ์ออกบัตรชนิดนี้"} - CHK4 -->|"✅ ผ่าน"| OK4["③ เชื่อถือข้อมูล"] - CHK4 -->|"❌ ไม่ผ่าน"| NO4["③ ปฏิเสธ"] - end - - S3 ~~~ S5 ~~~ S4 - - style CHK3 fill:#F3E5F5,stroke:#7B1FA2 - style CHK5 fill:#EFEBE9,stroke:#5D4037 - style CHK4 fill:#E8F5E9,stroke:#2E7D32 - style OK3 fill:#C8E6C9,stroke:#2E7D32 - style OK5 fill:#C8E6C9,stroke:#2E7D32 - style OK4 fill:#C8E6C9,stroke:#2E7D32 - style NO3 fill:#FFCDD2,stroke:#C62828 - style NO5 fill:#FFCDD2,stroke:#C62828 - style NO4 fill:#FFCDD2,stroke:#C62828 -``` - -**สรุป: Trust List ถูกใช้ทุกครั้งที่ Entity ต้องตัดสินใจเรื่อง trust** - -| จุดตรวจที่ | Flow | Step ใน flow เต็ม | ใครเช็ค | เช็คอะไร | ถ้าไม่ผ่าน | -|:---------:|------|:-----------------:|---------|---------|-----------| -| **1** | ออก VC | 3.1 (บทที่ 3) | Wallet | Issuer ใน Credential Offer อยู่ใน Trust List? | หยุด ไม่ส่ง Transaction Code | -| **2** | ออก VC | 5.5 (บทที่ 3) | Issuer | Wallet Provider อยู่ใน Trust List? | ไม่ออก VC | -| **3** | ออก VC | 6.5 (บทที่ 3) | Wallet | Issuer อยู่ใน Trust List? | ทิ้ง VC ไม่บันทึก | -| **4** | แสดง VP | 2.6 (บทที่ 4) | Wallet | Verifier อยู่ใน Trust List? | ไม่ส่งข้อมูลให้ (จุดที่สำคัญที่สุดฝั่ง Wallet) | -| **5** | แสดง VP | 4.1 (บทที่ 4) | Verifier | Wallet Provider อยู่ใน Trust List? (บังคับเฉพาะ Wallet ของรัฐ) | ปฏิเสธ | -| **6** | แสดง VP | 4.2 (บทที่ 4) | Verifier | Issuer ของ VC อยู่ใน Trust List + status + สิทธิ์ออกบัตร? | ไม่เชื่อถือข้อมูล | - -## ก.2 Core Security Rules — ลำดับการตรวจโดยละเอียดของแต่ละฝ่าย - -ตารางนี้แบ่งประเภทของการตรวจแต่ละขั้นด้วยสี เพื่อให้เห็นว่าขั้นไหนเป็นแค่การเทียบข้อมูลในเครื่อง (ถูก/เร็ว) และขั้นไหนต้องเรียกออกไปนอกเครื่อง (ช้า/มีค่าใช้จ่าย) - -| สี | ประเภทเช็ค | ตัวอย่าง | -|------|-----------|---------| -| 🟢 | parse field, lookup ใน Trustlist local cache, เทียบ status/scope/เวลา | WP/Issuer/Verifier active?, type อนุญาต?, iat/exp | -| 🔵 | verify signature (crypto) | RO sig, WIA sig, KB-JWT, VC sig | -| 🟠 | network call | Resolve DID → PK, Fetch Status List | - -> **หลักสำคัญ:** ถ้าขั้น 🟢 (lookup Trustlist) ไม่ผ่าน ให้ **ปฏิเสธทันที ไม่ต้องเรียก network (🟠) ต่อ** เพื่อไม่ให้เปลืองทรัพยากรและไม่เสี่ยงถูกหลอกให้ยิงคำขอไปยังปลายทางที่ไม่น่าเชื่อถือ - -### ก.2.1 Issuer — ตรวจก่อนออก VC - -1. 🟢 Parse `wallet_attestation` → ดึง WP id (iss) -2. 🟢 Lookup: WP อยู่ใน Trustlist และ status = active? -3. 🟢 Credential type ที่จะออก อยู่ในที่อนุมัติไว้? -4. 🟢 สถานะ/ช่วงเวลา attestation (iat/exp) ยังใช้ได้? -5. 🔵 Verify `wallet_attestation` signature - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - S(["▶ Issuer"]) --> P["🟢 Parse wallet_attestation\nextract WP id (iss)"] - P --> L{"🟢 WP active\nin Trustlist?"} - L -->|yes| T{"🟢 Credential type\napproved?"} - T -->|yes| St{"🟢 Attestation window\n(iat/exp) valid?"} - St -->|yes| Sig{"🔵 Verify attestation\nsignature (crypto)"} - Sig -->|ok| OK(["✅ Issue VC"]) - L -->|no| Rej(["❌ Reject"]) - T -->|no| Rej - St -->|no| Rej - Sig -->|fail| Rej - - style S fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style P fill:#dcfce7,stroke:#16a34a,color:#14532d - style L fill:#dcfce7,stroke:#16a34a,color:#14532d - style T fill:#dcfce7,stroke:#16a34a,color:#14532d - style St fill:#dcfce7,stroke:#16a34a,color:#14532d - style Sig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style OK fill:#16a34a,stroke:#15803d,color:#ffffff - style Rej fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -### ก.2.2 Wallet / Holder — 2 บริบท - -**A) หลังรับ VC — ตรวจ Issuer** - -1. 🟢 Lookup Issuer host name ใน `issuer_entries`? -2. 🟢 Parse SD-JWT → ดึง Issuer DID -3. 🟢 Lookup: Issuer active + credential type อนุญาต ใน `issuer_entries`? -4. 🟠 Resolve DID → PK (network) -5. 🔵 Verify VC signature - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - subgraph VCI["A) หลังรับ VC — ตรวจ Issuer"] - direction LR - S2(["▶ wallet"]) --> P1{"🟢 Issuer host name in Trustlist?"} - P1 --> |yes| P2["🟢 Parse SD-JWT\nextract Issuer DID"] - P1 --> |no| R2 - P2 --> L2{"🟢 Issuer active + type\nin Trustlist?"} - L2 -->|yes| Re2["🟠 Resolve DID → PK\n(network)"] - Re2 --> V2{"🔵 Verify VC sig"} - V2 -->|ok| OK2(["✅ Store"]) - L2 -->|no| R2(["❌ Reject\nไม่ต้อง resolve"]) - V2 -->|fail| R2 - end - - style S2 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style P1 fill:#dcfce7,stroke:#16a34a,color:#14532d - style P2 fill:#dcfce7,stroke:#16a34a,color:#14532d - style L2 fill:#dcfce7,stroke:#16a34a,color:#14532d - style Re2 fill:#ffedd5,stroke:#ea580c,color:#7c2d12 - style V2 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style OK2 fill:#16a34a,stroke:#15803d,color:#ffffff - style R2 fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -**B) ก่อนแสดง VP — ตรวจ Verifier** - -1. 🟢 Parse Request Object → ดึง PK_v -2. 🟢 Lookup: Verifier active ใน `verifier_entries`? -3. 🟢 Scope: claims ที่ขอ ⊆ allowed_attributes, purpose ∈ allowed_purposes? -4. 🔵 Verify RO signature + aud/exp/nonce - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - subgraph VP["B) ก่อนแสดง VP — ตรวจ Verifier"] - direction LR - S1(["▶ wallet"]) --> P1["🟢 Parse RO\nextract PK_v"] - P1 --> L1{"🟢 Verifier active\nin Trustlist?"} - L1 -->|yes| Sc1{"🟢 Scope: claims ⊆ allowed\npurpose allowed?"} - Sc1 -->|yes| V1{"🔵 Verify RO sig"} - V1 -->|ok| OK1(["✅ Present"]) - L1 -->|no| R1(["❌ Reject"]) - Sc1 -->|no| R1 - V1 -->|fail| R1 - end - - style S1 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style P1 fill:#dcfce7,stroke:#16a34a,color:#14532d - style L1 fill:#dcfce7,stroke:#16a34a,color:#14532d - style Sc1 fill:#dcfce7,stroke:#16a34a,color:#14532d - style V1 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style OK1 fill:#16a34a,stroke:#15803d,color:#ffffff - style R1 fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -### ก.2.3 Verifier — ตรวจ VP - -1. 🟢 Parse WIA → WP iss + PK_wp -2. 🟢 Lookup L3: WP active ใน Trustlist? -3. 🔵 Verify WIA + WIA-PoP signature -4. 🟢 Parse issuer_jwt -5. 🟢 **Check temporal: VC `nbf ≤ now ≤ exp` + VP(KB-JWT) `iat` ใหม่ / `exp` ยังไม่หมด** — ถ้าหมดอายุ/ยังไม่ถึงกำหนด → **ปฏิเสธ** -6. 🔵 Verify KB-JWT (holder binding) -7. 🟢 Lookup L4: Issuer DID active + credential type อนุญาต? -8. 🟠 Resolve DID → PK_iss -9. 🔵 Verify issuer_jwt signature + disclosures -10. 🟢 DCQL match -11. 🟠 Fetch + decode Status List -12. 🟢 Credential status = valid? (ไม่ revoked / suspended) — ถ้าไม่ valid → **ปฏิเสธ** - -```mermaid -%%{init: {"theme":"base","themeVariables":{"fontSize":"15px"},"flowchart":{"useMaxWidth":true,"htmlLabels":true,"curve":"linear"}}}%% -flowchart LR - S(["▶ verifier — received VP"]) --> P1["🟢 Parse WIA → WP iss + PK_wp"] - P1 --> L3{"🟢 WP active in TL?\n(allowed_aal)"} - L3 -->|no| R(["❌ Reject"]) - L3 -->|yes| V1{"🔵 Verify WIA + WIA-PoP sig"} - V1 -->|fail| R - V1 -->|ok| P2["🟢 Parse issuer_jwt + KB-JWT\n→ Issuer DID"] - P2 --> T{"🟢 VC nbf/exp + VP(KB-JWT) iat/exp\nยังอยู่ในช่วงใช้งาน?"} - T -->|expired / not yet valid| R - T -->|valid| V2{"🔵 Verify KB-JWT\nholder binding"} - V2 -->|fail| R - V2 -->|ok| L4{"🟢 Issuer DID active + type\nin TL?"} - L4 -->|no| R - L4 -->|yes| Re["🟠 Resolve DID → PK_iss (network)"] - Re --> V3{"🔵 Verify issuer_jwt sig\n+ disclosures"} - V3 -->|fail| R - V3 -->|ok| DQ{"🟢 DCQL match"} - DQ -->|no| R - DQ -->|yes| St["🟠 Fetch + decode Status List (network)"] - St --> Stat{"🟢 Status = valid?\n(not revoked/suspended)"} - Stat -->|revoked/suspended| R - Stat -->|valid| OK(["✅ Grant service"]) - - style S fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style P1 fill:#dcfce7,stroke:#16a34a,color:#14532d - style L3 fill:#dcfce7,stroke:#16a34a,color:#14532d - style V1 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style P2 fill:#dcfce7,stroke:#16a34a,color:#14532d - style T fill:#dcfce7,stroke:#16a34a,color:#14532d - style V2 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style L4 fill:#dcfce7,stroke:#16a34a,color:#14532d - style Re fill:#ffedd5,stroke:#ea580c,color:#7c2d12 - style V3 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - style DQ fill:#dcfce7,stroke:#16a34a,color:#14532d - style St fill:#ffedd5,stroke:#ea580c,color:#7c2d12 - style Stat fill:#dcfce7,stroke:#16a34a,color:#14532d - style OK fill:#16a34a,stroke:#15803d,color:#ffffff - style R fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -## ก.3 หลักการนี้ถูกใช้จริงที่ไหน - -| Flow | จุดที่ใช้หลักการนี้ | อ่านรายละเอียดที่ | -|------|---------------------|--------------------| -| การออกเอกสาร (OID4VCI) | Issuer ตรวจ Wallet Provider ก่อนออกบัตร | [3.2.5 การตรวจสอบ Trustlist](../03-issuance-flow/02-full-flow/06-verification-details.md) | -| การแสดงเอกสาร (OID4VP) | Wallet ตรวจ Verifier + Verifier ตรวจ Wallet Provider/Issuer | [4.1 ภาพรวมการแสดงเอกสาร](../04-presentation-flow/01-overview/index.md) | -| ภาพรวม "ใครตรวจใคร" | ตารางสรุปทุกฝ่าย | [1.4 หลักการตรวจสอบ](../01-concepts-and-roles/03-common-verification-pattern.md) | diff --git a/docs/trust-guide/09-appendix/_category_.json b/docs/trust-guide/09-appendix/_category_.json deleted file mode 100644 index 6b17c80..0000000 --- a/docs/trust-guide/09-appendix/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "ภาคผนวก", - "position": 9 -} diff --git a/docs/trust-guide/_category_.json b/docs/trust-guide/_category_.json deleted file mode 100644 index 117f916..0000000 --- a/docs/trust-guide/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "คู่มือ Trust Framework — VC Ecosystem ประเทศไทย", - "position": 0, - "link": { - "type": "generated-index", - "slug": "/", - "title": "คู่มือ Trust Framework — VC Ecosystem ประเทศไทย", - "description": "คู่มือนี้อธิบายว่า Trust Framework ของ ETDA ทำให้ทุกฝ่ายในระบบ VC (Verifiable Credential) ของไทยเชื่อใจกันได้อย่างไร ตั้งแต่การออกบัตรดิจิทัลไปจนถึงการแสดงบัตรให้ผู้ตรวจสอบดู เนื้อหาหลักแบ่งเป็น 8 บท เรียงตามลำดับที่ควรอ่าน และมีภาคผนวกท้ายเล่มไว้เป็นแผ่นสรุป เลือกอ่านแต่ละบทได้จากการ์ดด้านล่าง โดยผู้เริ่มต้นควรอ่านบท 1–2 ก่อน, นักพัฒนาฝั่งผู้ออกบัตรเริ่มที่บท 3, ฝั่งผู้ตรวจสอบเริ่มที่บท 4, และองค์กรที่อยากเข้าร่วมระบบดูบท 7" - } -} diff --git a/docusaurus.config.ts b/docusaurus.config.ts index e9dac5b..95f0880 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -6,7 +6,7 @@ import type * as Preset from '@docusaurus/preset-classic'; const config: Config = { title: 'VC Document', - tagline: 'เอกสารและมาตรฐาน Verifiable Credential โดย ETDA', + tagline: 'Thai VC ARF — 2.0 DRAFT 0', favicon: 'img/favicon.ico', // Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future @@ -26,8 +26,7 @@ const config: Config = { projectName: 'vcdoc', // Usually your repo name. deploymentBranch: 'gh-pages', - // TEMP: set to 'warn' while 3-02 & 4-02 full-flow are excluded from build - // (other chapters still link into them). Revert to 'throw' when re-enabled. + // ARF source files contain references to material outside this site. onBrokenLinks: 'warn', markdown: { @@ -71,20 +70,18 @@ const config: Config = { sidebarPath: './sidebars.ts', // Serve docs at the site root so the homepage IS the content routeBasePath: '/', + lastVersion: 'current', + versions: { + current: { + label: '2.0 DRAFT 0', + path: '/', + banner: 'none', + }, + }, // Please change this to your repo. // Remove this to remove the "edit this page" links. editUrl: 'https://github.com/ETDA/vcdoc/tree/main/', - // Temporarily excluded from build (3-02 & 4-02 full-flow). - // Keep Docusaurus default excludes, then add the two folders. - exclude: [ - '**/_*.{js,jsx,ts,tsx,md,mdx}', - '**/_*/**', - '**/*.test.{js,jsx,ts,tsx}', - '**/__tests__/**', - '**/03-issuance-flow/02-full-flow/**', - '**/04-presentation-flow/02-full-flow/**', - ], }, blog: false, theme: { @@ -111,7 +108,21 @@ const config: Config = { type: 'docSidebar', sidebarId: 'tutorialSidebar', position: 'left', - label: 'เอกสาร', + label: 'Thai VC ARF', + }, + { + to: '/en/thai-vc-arf/minimal-interoperability-reference', + label: 'VC stack (EN)', + position: 'left', + }, + { + type: 'docsVersionDropdown', + position: 'right', + }, + { + href: 'https://github.com/ETDA/vcdoc/commits/main/', + label: 'History', + position: 'right', }, { href: 'https://github.com/ETDA/vcdoc/issues', @@ -132,7 +143,7 @@ const config: Config = { title: 'Docs', items: [ { - label: 'เอกสาร', + label: 'Thai VC ARF', to: '/', }, ], diff --git a/package-lock.json b/package-lock.json index 88c8dab..523bb78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "vcdoc-scaffold", "version": "0.0.0", + "hasInstallScript": true, "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/faster": "3.10.1", @@ -23,7 +24,10 @@ "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/tsconfig": "3.10.1", "@docusaurus/types": "3.10.1", + "@playwright/test": "^1.63.0", "@types/react": "^19.0.0", + "jsdom": "^29.1.1", + "mermaid": "^11.17.2", "patch-package": "^8.0.1", "typescript": "~6.0.2" }, @@ -277,6 +281,193 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/@csstools/color-helpers": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.2.tgz", + "integrity": "sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/@csstools/css-calc": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.3.tgz", + "integrity": "sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.2", + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/@csstools/css-color-parser": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.6.tgz", + "integrity": "sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.2", + "@csstools/css-calc": "^3.4.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.2", + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.2.tgz", + "integrity": "sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/@csstools/css-tokenizer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.2.tgz", + "integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -2001,6 +2192,40 @@ "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", "license": "MIT" }, + "node_modules/@bramus/specificity": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.3.tgz", + "integrity": "sha512-0GEz8qJWokkUVYQYRukmkSHIFNoBH8QTreeHrAFbvz1XFYc3Y+puhMs8PQ5x8GNugiC2l5jvDBXKLbBIByeSBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.2.1" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@bramus/specificity/node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@bramus/specificity/node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/@chevrotain/types": { "version": "11.1.2", "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", @@ -4926,9 +5151,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.0.tgz", - "integrity": "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz", + "integrity": "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==", "license": "MIT", "dependencies": { "@chevrotain/types": "~11.1.2" @@ -5143,9 +5368,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5162,9 +5384,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5181,9 +5400,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5200,9 +5416,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5480,6 +5693,22 @@ "node": ">=20.0.0" } }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -5578,9 +5807,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5594,9 +5820,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5610,9 +5833,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5626,9 +5846,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6117,9 +6334,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6136,9 +6350,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6155,9 +6366,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6174,9 +6382,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6193,9 +6398,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6212,9 +6414,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6359,9 +6558,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6378,9 +6574,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6397,9 +6590,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6416,9 +6606,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6435,9 +6622,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -6454,9 +6638,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -7817,6 +7998,16 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "license": "MIT" }, + "node_modules/bidi-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -9780,6 +9971,30 @@ "lodash-es": "^4.17.21" } }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/data-urls/node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/dayjs": { "version": "1.11.21", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", @@ -9809,6 +10024,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/decode-named-character-reference": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", @@ -10803,6 +11025,15 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fastdom": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastdom/-/fastdom-1.0.12.tgz", + "integrity": "sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==", + "license": "MIT", + "dependencies": { + "strictdom": "^1.0.1" + } + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -11670,6 +11901,52 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/html-encoding-sniffer/node_modules/@exodus/bytes": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.16.0.tgz", + "integrity": "sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/html-encoding-sniffer/node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -12356,6 +12633,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", @@ -12522,6 +12806,172 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz", + "integrity": "sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@exodus/bytes": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.16.0.tgz", + "integrity": "sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/jsdom/node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/jsdom/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/jsdom/node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/jsdom/node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -12855,9 +13305,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -12878,9 +13325,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -12901,9 +13345,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -12924,9 +13365,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -13654,26 +14092,27 @@ } }, "node_modules/mermaid": { - "version": "11.16.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.16.0.tgz", - "integrity": "sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==", + "version": "11.17.2", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.17.2.tgz", + "integrity": "sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", - "@mermaid-js/parser": "^1.2.0", + "@mermaid-js/parser": "^1.2.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", - "cytoscape": "^3.33.3", + "cytoscape": "^3.34.0", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", - "dayjs": "^1.11.20", + "dayjs": "^1.11.21", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", - "katex": "^0.16.45", + "fastdom": "1.0.12", + "katex": "^0.16.47", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", @@ -16471,6 +16910,35 @@ "node": ">=16.0.0" } }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", @@ -19021,6 +19489,19 @@ "node": ">=11.0.0" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -19661,6 +20142,12 @@ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "license": "MIT" }, + "node_modules/strictdom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strictdom/-/strictdom-1.0.1.tgz", + "integrity": "sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==", + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -19901,6 +20388,13 @@ "webpack": ">=2" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tapable": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", @@ -20079,6 +20573,26 @@ "node": "^18.0.0 || >=20.0.0" } }, + "node_modules/tldts": { + "version": "7.4.16", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.16.tgz", + "integrity": "sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.16" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.16", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.16.tgz", + "integrity": "sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", @@ -20119,6 +20633,32 @@ "node": ">=6" } }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/tree-dump": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", @@ -20761,6 +21301,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/watchpack": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", @@ -20792,6 +21345,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, "node_modules/webpack": { "version": "5.108.4", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.4.tgz", @@ -21163,6 +21726,54 @@ "node": ">=18" } }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/whatwg-url/node_modules/@exodus/bytes": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.16.0.tgz", + "integrity": "sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/whatwg-url/node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -21342,6 +21953,23 @@ "xml-js": "bin/cli.js" } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index b6d46fe..b0fdd3e 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,10 @@ "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" + "typecheck": "tsc", + "validate:build": "node scripts/validate-build.mjs", + "test": "npm run typecheck && npm run build && npm run validate:build", + "test:e2e": "playwright test" }, "dependencies": { "@docusaurus/core": "3.10.1", @@ -31,7 +34,10 @@ "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/tsconfig": "3.10.1", "@docusaurus/types": "3.10.1", + "@playwright/test": "^1.63.0", "@types/react": "^19.0.0", + "jsdom": "^29.1.1", + "mermaid": "^11.17.2", "patch-package": "^8.0.1", "typescript": "~6.0.2" }, diff --git a/playwright.config.mjs b/playwright.config.mjs new file mode 100644 index 0000000..5d7a787 --- /dev/null +++ b/playwright.config.mjs @@ -0,0 +1,26 @@ +import { defineConfig } from '@playwright/test'; + +// Browser-based validation of the built site (run `npm run build` first). +// Mermaid diagrams and locale rendering happen client-side (JS hydration), +// so they are verified here in a real browser rather than in the static HTML. +export default defineConfig({ + testDir: './tests/e2e', + timeout: 30_000, + expect: { timeout: 15_000 }, + workers: 1, + use: { + // Origin only. Tests use full /vcdoc/... paths (the site's baseUrl) so URL + // resolution is unambiguous (a leading "/" resolves against the origin, + // NOT against a pathful baseURL). + baseURL: 'http://localhost:4173', + trace: 'retain-on-failure', + }, + reporter: [['list']], + webServer: { + // Requires a prior `npm run build` (serves the build/ directory). + command: 'npm run serve -- --port 4173', + url: 'http://localhost:4173/vcdoc/', + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, +}); diff --git a/scripts/validate-build.mjs b/scripts/validate-build.mjs new file mode 100644 index 0000000..2acd82b --- /dev/null +++ b/scripts/validate-build.mjs @@ -0,0 +1,259 @@ +#!/usr/bin/env node +/** + * Static validation of the Docusaurus build output (build/). + * + * Run AFTER `npm run build`. Verifies: + * 1. Every source doc produced a generated page (no doc dropped). + * 2. Locale layout is correct: Thai at the root + /thai-vc-arf/..., English at /en/... . + * 3. No raw HTML comments leaked into the source (format-conversion regression check). + * 4. Mermaid diagrams: every top-level mermaid block parses without syntax errors + * (using the mermaid parser under jsdom; client-side rendering is covered by E2E). + * 5. Image integrity: every referenced image exists on disk and no image file is + * orphaned (referenced-but-missing or present-but-unused images are both errors). + * + * Exits non-zero on the first failure. + */ +import { existsSync, readFileSync, readdirSync } from 'node:fs'; +import { join, dirname, resolve, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { JSDOM } from 'jsdom'; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); +const BUILD = join(ROOT, 'build'); +const DOCS = join(ROOT, 'docs'); + +const failures = []; +const checks = []; +const ok = (msg) => checks.push(` PASS ${msg}`); +const fail = (msg) => { + failures.push(msg); + checks.push(` FAIL ${msg}`); +}; + +// --- helpers --------------------------------------------------------------- + +// Docusaurus default slug: filename without .md, with a leading integer prefix +// ("00-", "01-", ... "16-") stripped. Dot-numbered files ("06.5-", "08.1-", +// "12.1-") keep their prefix. Verified against build/sitemap.xml. +const slugFromFilename = (filename) => filename.replace(/\.md$/, '').replace(/^\d+-/, ''); + +function mdFiles(dir) { + return readdirSync(dir) + .filter((f) => f.endsWith('.md')) + .sort(); +} + +// --- 1. all docs generated ------------------------------------------------- + +console.log('1. all docs generated'); +if (!existsSync(BUILD)) { + fail(`build/ directory not found — run "npm run build" first`); +} else { + const thaiDir = join(DOCS, 'thai-vc-arf'); + const enDir = join(DOCS, 'en', 'thai-vc-arf'); + + const thaiMd = mdFiles(thaiDir).filter((f) => f !== 'README.md'); + for (const f of thaiMd) { + const slug = slugFromFilename(f); + const html = join(BUILD, 'thai-vc-arf', slug, 'index.html'); + if (existsSync(html)) ok(`docs/thai-vc-arf/${f} -> /thai-vc-arf/${slug}/`); + else fail(`missing page for docs/thai-vc-arf/${f} (expected ${html})`); + } + + // README.md becomes the Thai category landing page at /thai-vc-arf/ + if (existsSync(join(BUILD, 'thai-vc-arf', 'index.html'))) { + ok('docs/thai-vc-arf/README.md -> /thai-vc-arf/'); + } else { + fail('missing Thai category landing page /thai-vc-arf/ (from README.md)'); + } + + for (const f of mdFiles(enDir)) { + const slug = slugFromFilename(f); + const html = join(BUILD, 'en', 'thai-vc-arf', slug, 'index.html'); + if (existsSync(html)) ok(`docs/en/thai-vc-arf/${f} -> /en/thai-vc-arf/${slug}/`); + else fail(`missing page for docs/en/thai-vc-arf/${f}`); + } +} + +// --- 2. locale support ----------------------------------------------------- + +console.log('2. locale support'); +if (existsSync(BUILD)) { + const readHtml = (p) => (existsSync(p) ? readFileSync(p, 'utf8') : ''); + + const home = readHtml(join(BUILD, 'index.html')); + if (home.includes('Thai VC ARF')) ok('homepage / serves the Thai index'); + else fail('homepage / does not contain the Thai index title'); + + const en = readHtml(join(BUILD, 'en', 'thai-vc-arf', 'minimal-interoperability-reference', 'index.html')); + if (en.includes("Thailand's VC stack")) ok('English summary served at /en/thai-vc-arf/...'); + else fail('English summary page missing or does not contain its title'); + + const thaiPage = readHtml(join(BUILD, 'thai-vc-arf', 'scope', 'index.html')); + if (thaiPage.includes('ขอบข่าย')) ok('Thai chapter content present (scope page)'); + else fail('Thai chapter page missing expected content'); +} + +// --- 3. no raw HTML comments (format regression) --------------------------- + +console.log('3. format conversion (no raw HTML comments)'); +{ + let leaked = 0; + // A raw "" + // inside an already-converted {/* ... */} comment is literal text and is fine. + const topLevelHtmlLines = (text) => { + const hits = []; + let depth = 0; + let i = 0; + let line = 1; + while (i < text.length) { + const ch = text[i]; + if (text.startsWith('{/*', i)) { + depth += 1; + i += 3; + } else if (text.startsWith('*/}', i)) { + depth = Math.max(0, depth - 1); + i += 3; + } else if (text.startsWith('