From e03249168e6edc87d5e364086abaf58cecb4ecb4 Mon Sep 17 00:00:00 2001 From: Chiping Yeh Date: Thu, 4 Jun 2026 16:29:09 -0400 Subject: [PATCH 1/7] Add more details on wallet structure to README --- README.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c0e138b..6fd8325 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,126 @@ -# CM Wallet +# CMWallet - Android Digital Credentials Sample Wallet -Sample wallet app +CMWallet is a developer-focused Android sample application demonstrating integration with Android's **Credential Manager API** to support **Verifiable Digital Credentials**. + +This project demonstrates a referenceable Holder (Wallet), supporting **issuance** of credentials from issuers through the **OpenID4VCI** standard and **presentation** of credentials to Verifiers (apps) through the **OpenID4VP** standard. ## Try it out * Navigate to https://github.com/digitalcredentialsdev/CMWallet/actions. * Select the latest build and download the `app-debug.apk` file. * Deploy the app on your Android device. * Open the `CMWallet` app once to register the metadata with Credential Manager. -* Now the wallet is ready to handle a verifier Digital Credentials request. +* Now the wallet is ready to handle a verifier Digital Credentials request. * You can navigate to the demo verifier site https://digital-credentials.dev/ and try it out. + + +--- + +## Architectural Design & System Flow + +CMWallet is built on Jetpack Compose, using Room for storage and Ktor for networking. + +To learn about handling requests for credentials from other applications, or **Verifiers**, see [Credential Presentation](#1-credential-presentation). + +To learn about handling requests to store credentials from credential **issuers**, see [Credential Issuance](#2-credential-issuance). + +### 1. Credential Presentation +Presents user-selected credential claims to verifiers. Holders must first register their credentials' metadata with Credential Manager to displayed to the user. When verifier websites or apps request digital credential claims, Credential Manager will display relevant options for the user to select. After the user selects a credential, the corresponding holder application will be invoked, and then the credential will be presented to the verifier: +``` +[Holder(wallet) app] ──► Registers metadata with Credential Manager + +[Verifier app] ──► Requests digital credential claim(s) ──► Credential Manager matches claims and displays options to user ──► User selects credential ──► Holder is invoked ──► Holder returns signed presentation to verifier +``` +* **Matchers (WASM matching)**: In order to match a verifier's requested claims with registered metadata, Credential Manager runs the wallet's compiled WebAssembly (WASM) matching module (e.g., `openid4vp1_0.wasm`) in an offline, secure system sandbox. The matcher evaluates the verifier's query against the stored credentials without revealing any private user details to the calling app. Credential Manager comes with a default matcher if none are specified. +* **Holder invocation**: If a matching credential is found, Android displays the card to the user. Clicking the card invokes the holder and launches `GetCredentialActivity`. CMWallet launches an additional `BiometricPrompt` during invocation for additional user consent. +* **Presentation Assembly**: The wallet extracts the requested claims, builds a cryptographically secure **Session Transcript** to bind the response to the specific connection, signs the response using the private key stored in secure hardware, packages the payload, and returns it to the calling verifier. + +### 2. Credential Issuance +Handles credential issuance requests from issuers. When a user initiates getting a credential from an issuer by scanning a QR code or opening a link, the issuer can trigger a system intent that launches the credential creation and storage process: +``` +[Issuer app] ──► Triggers system intent: CREATE_CREDENTIAL ──► calls Credential Manater's CreateCredentialActivity + │ +[Room DB] ◄── Save issued credential ◄── Hardware-backed attestation process ◄── Request credential from Issuer +``` +* **Issuer triggers intent**: The issuer triggers Android's `CREATE_CREDENTIAL` system intent, launching `CreateCredentialActivity`. +* **Progress bottom sheet**: The wallet displays a Compose bottom sheet showing a progress bar while it parses the Credential Offer. +* **Key Generation and Attestation**: The wallet generates a secure cryptographic EC P-256 key pair in the phone's hardware-backed **Android KeyStore**. It creates a signed **DPoP Proof** and an **Android Keystore Key Attestation** to prove the key is tied to a genuine physical device. +* **Hardware-backed attestation process**: The wallet sends the key proofs to the issuer's endpoint and receives the signed credential payload (in mDoc or sd-jwt format) +* **Saving the credential**: The wallet decrypts the signed credential payload if it is encrypted, and persists it in the Room Database. + +--- + +## Directory & Module Guide + +``` +CMWallet/ +├── app/ +│ └── src/main/java/com/credman/cmwallet/ +│ ├── CmWalletApplication.kt # App startup, registry synchronization +│ ├── MainActivity.kt # Main launcher dashboard UI +│ ├── ui/ # Compose views and home ViewModel +│ ├── getcred/ # Presentation flow (GET_CREDENTIAL intent) +│ ├── createcred/ # Issuance flow (CREATE_CREDENTIAL intent) +│ ├── data/ # Repository and Room SQLite persistence +│ ├── openid4vci/ # Issuance client implementation +│ ├── openid4vp/ # Presentation parser and matching engine +│ ├── mdoc/ # ISO 18013-5 mdoc formatting and signatures +│ ├── sdjwt/ # sd-jwt VC format presentation and verification +│ └── cbor/ # CBOR encoding helpers +├── matcher/ # C implementation of wasm matcher +└── matcher-rs/ # Rust implementation of wasm matcher +``` + +### Detailed Directory Mapping + +#### 1. App Configuration and Lifecycle +* **`CmWalletApplication.kt`** + * **Role**: The application initialization class. + * **Usage**: On startup, it configures the database and loads the compiled WASM matcher files (`openid4vp1_0.wasm`, `pnv.wasm`) from the app's `assets/`. It establishes a reactive Kotlin Flow that collects active credentials from the repository, transforms them into a `OpenId4VpRegistry` model, and updates Credential Manager's `RegistryManager` dynamically in the background. + +#### 2. UI, Credential Presentation, and Handling Credential Issuance +* **`HomeScreen.kt` and `HomeViewModel.kt` (`/ui`)** + * **Role**: Renders the wallet's main dashboard. + * **Usage**: Displays stored digital cards in a swipeable wallet visual. Selecting a card opens the `CredentialDialog` for more detailed viewing. +* **`GetCredentialActivity.kt` (`/getcred`)** + * **Role**: Entry point for Android's `GET_CREDENTIAL` flow. + * **Usage**: This activity extracts the request JSON. It calls `processDigitalCredentialOption()` to filter matched claims, triggers the system's biometric lock, compiles the cryptographic response, and calls `PendingIntentHandler.setGetCredentialResponse()` to deliver it to the calling verifier app. +* **`CreateCredentialActivity.kt` and `CreateCredentialViewModel.kt` (`/createcred`)** + * **Role**: Entry point for Android's `CREATE_CREDENTIAL` system provider flow. + * **Usage**: Initiated during an issuance request. Displays a sheet presenting the issuer authentication flow (`AuthWebView`), manages network state steps, triggers key generation, requests the credential, and updates the Room DB upon approval. + +#### 3. Protocol Implementations +* **`/openid4vci`** + * **`OpenId4VCI.kt`**: Orchestrates HTTP communication with the Issuer. Uses **Ktor** to fetch OAuth2 authorization endpoints, post **PAR** (Pushed Authorization Requests), and retrieve issued credentials. + * **`class DeviceKey`**: Contains `SoftwareKey` and `HardwareKey` classes representing device keys. +* **`/openid4vp`** + * **`OpenId4VP.kt`**: Parses verifier presentation requests. Evaluates incoming nonces, extracts client metadata, parses query formats, and encrypts final payloads using JWE (JSON Web Encryption). + * **`DCQL.kt`**: A pure Kotlin parser for the **Digital Credential Query Language** specification, allowing the app to perform query evaluations inside its own execution thread. + +#### 4. Digital Credential Formats +* **`/mdoc`** + * **`MDoc.kt`**: A parser for **ISO 18013-5** mobile driving license documents, converting binary CBOR namespaces and elements into structured Kotlin maps (`issuerSignedNamespaces`). + * **`mdoc/Utils.kt`**: Cryptographic helper functions: + * `generateDeviceResponse()`: Generates a standard-compliant COSE Sign1 signature utilizing the device private key, binding it to the verified claims. + * `createSessionTranscript()`: Binds handover parameters (like domains and session nonces) into a SHA-256 byte array prevent replay attacks. + * `filterIssuerSigned()`: Filters out the raw, verified issuer-signed claims to reveal only the elements the user chose to share. +* **`/sdjwt`** + * **`SdJwt.kt`**: Integrates Selective Disclosure JSON Web Tokens. + * **Key Function - `present()`**: Collects the verifier-selected claims, gathers their corresponding salt disclosures (formatted as base64 hashes), builds a **Key-Bound JWT** signed by the holder's private key, and joins them into a tilde (`~`) delimited string format for submission. + +#### 5. Database and Storage (`/data`) +* **`repository/CredentialRepository.kt`** + * **Role**: Synchronizes persistence layers. + * **Usage**: Combines dynamic sqlite database records (`CredentialDatabaseDataSource`) and test credentials (`TestCredentialsDataSource`) into unified flows. It is responsible for transforming standard database items into Android System-level `DigitalCredentialEntry` structures (like `MdocEntry` and `SdJwtEntry`) including display properties and titles. + +--- + +## The WASM Query Matchers (`/matcher` and `/matcher-rs`) + +Credential matching does not happen directly in the main wallet app code. Instead: +1. Matcher code is written in C/C++ (`matcher/`) or Rust (`matcher-rs/`). +2. It implements DCQL parser rules and format-specific validation. +3. It compiles directly to a WebAssembly binary target in `build.sh` +4. The resulting `.wasm` files are placed in `app/src/main/assets/`. +5. When a request arrives, Credential Manager loads these WASM binaries into an isolated system sandbox for query matching. + +Note that matches are not required to be separately defined; Credential Manager already includes a default matcher. \ No newline at end of file From e758072595a8d52c871a81b585ee9f82796553ee Mon Sep 17 00:00:00 2001 From: Chiping Yeh Date: Thu, 25 Jun 2026 14:06:19 -0400 Subject: [PATCH 2/7] Format document --- README.md | 144 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 6fd8325..6f3870f 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ # CMWallet - Android Digital Credentials Sample Wallet -CMWallet is a developer-focused Android sample application demonstrating integration with Android's **Credential Manager API** to support **Verifiable Digital Credentials**. +CMWallet is a developer-focused Android sample application demonstrating integration with Android's +**Credential Manager API** to support **Verifiable Digital Credentials**. -This project demonstrates a referenceable Holder (Wallet), supporting **issuance** of credentials from issuers through the **OpenID4VCI** standard and **presentation** of credentials to Verifiers (apps) through the **OpenID4VP** standard. +This project demonstrates a referenceable Holder (Wallet), supporting **issuance** of credentials +from issuers through the **OpenID4VCI** standard and **presentation** of credentials to Verifiers ( +apps) through the **OpenID4VP** standard. ## Try it out + * Navigate to https://github.com/digitalcredentialsdev/CMWallet/actions. * Select the latest build and download the `app-debug.apk` file. * Deploy the app on your Android device. * Open the `CMWallet` app once to register the metadata with Credential Manager. * Now the wallet is ready to handle a verifier Digital Credentials request. - * You can navigate to the demo verifier site https://digital-credentials.dev/ and try it out. - + * You can navigate to the demo verifier site https://digital-credentials.dev/ and try it out. --- @@ -19,33 +22,62 @@ This project demonstrates a referenceable Holder (Wallet), supporting **issuance CMWallet is built on Jetpack Compose, using Room for storage and Ktor for networking. -To learn about handling requests for credentials from other applications, or **Verifiers**, see [Credential Presentation](#1-credential-presentation). +To learn about handling requests for credentials from other applications, or **Verifiers**, +see [Credential Presentation](#1-credential-presentation). -To learn about handling requests to store credentials from credential **issuers**, see [Credential Issuance](#2-credential-issuance). +To learn about handling requests to store credentials from credential **issuers**, +see [Credential Issuance](#2-credential-issuance). ### 1. Credential Presentation -Presents user-selected credential claims to verifiers. Holders must first register their credentials' metadata with Credential Manager to displayed to the user. When verifier websites or apps request digital credential claims, Credential Manager will display relevant options for the user to select. After the user selects a credential, the corresponding holder application will be invoked, and then the credential will be presented to the verifier: + +Presents user-selected credential claims to verifiers. Holders must first register their +credentials' metadata with Credential Manager to displayed to the user. When verifier websites or +apps request digital credential claims, Credential Manager will display relevant options for the +user to select. After the user selects a credential, the corresponding holder application will be +invoked, and then the credential will be presented to the verifier: + ``` [Holder(wallet) app] ──► Registers metadata with Credential Manager [Verifier app] ──► Requests digital credential claim(s) ──► Credential Manager matches claims and displays options to user ──► User selects credential ──► Holder is invoked ──► Holder returns signed presentation to verifier ``` -* **Matchers (WASM matching)**: In order to match a verifier's requested claims with registered metadata, Credential Manager runs the wallet's compiled WebAssembly (WASM) matching module (e.g., `openid4vp1_0.wasm`) in an offline, secure system sandbox. The matcher evaluates the verifier's query against the stored credentials without revealing any private user details to the calling app. Credential Manager comes with a default matcher if none are specified. -* **Holder invocation**: If a matching credential is found, Android displays the card to the user. Clicking the card invokes the holder and launches `GetCredentialActivity`. CMWallet launches an additional `BiometricPrompt` during invocation for additional user consent. -* **Presentation Assembly**: The wallet extracts the requested claims, builds a cryptographically secure **Session Transcript** to bind the response to the specific connection, signs the response using the private key stored in secure hardware, packages the payload, and returns it to the calling verifier. + +* **Matchers (WASM matching)**: In order to match a verifier's requested claims with registered + metadata, Credential Manager runs the wallet's compiled WebAssembly (WASM) matching module (e.g., + `openid4vp1_0.wasm`) in an offline, secure system sandbox. The matcher evaluates the verifier's + query against the stored credentials without revealing any private user details to the calling + app. Credential Manager comes with a default matcher if none are specified. +* **Holder invocation**: If a matching credential is found, Android displays the card to the user. + Clicking the card invokes the holder and launches `GetCredentialActivity`. CMWallet launches an + additional `BiometricPrompt` during invocation for additional user consent. +* **Presentation Assembly**: The wallet extracts the requested claims, builds a cryptographically + secure **Session Transcript** to bind the response to the specific connection, signs the response + using the private key stored in secure hardware, packages the payload, and returns it to the + calling verifier. ### 2. Credential Issuance -Handles credential issuance requests from issuers. When a user initiates getting a credential from an issuer by scanning a QR code or opening a link, the issuer can trigger a system intent that launches the credential creation and storage process: + +Handles credential issuance requests from issuers. When a user initiates getting a credential from +an issuer by scanning a QR code or opening a link, the issuer can trigger a system intent that +launches the credential creation and storage process: + ``` [Issuer app] ──► Triggers system intent: CREATE_CREDENTIAL ──► calls Credential Manater's CreateCredentialActivity │ [Room DB] ◄── Save issued credential ◄── Hardware-backed attestation process ◄── Request credential from Issuer ``` -* **Issuer triggers intent**: The issuer triggers Android's `CREATE_CREDENTIAL` system intent, launching `CreateCredentialActivity`. -* **Progress bottom sheet**: The wallet displays a Compose bottom sheet showing a progress bar while it parses the Credential Offer. -* **Key Generation and Attestation**: The wallet generates a secure cryptographic EC P-256 key pair in the phone's hardware-backed **Android KeyStore**. It creates a signed **DPoP Proof** and an **Android Keystore Key Attestation** to prove the key is tied to a genuine physical device. -* **Hardware-backed attestation process**: The wallet sends the key proofs to the issuer's endpoint and receives the signed credential payload (in mDoc or sd-jwt format) -* **Saving the credential**: The wallet decrypts the signed credential payload if it is encrypted, and persists it in the Room Database. + +* **Issuer triggers intent**: The issuer triggers Android's `CREATE_CREDENTIAL` system intent, + launching `CreateCredentialActivity`. +* **Progress bottom sheet**: The wallet displays a Compose bottom sheet showing a progress bar while + it parses the Credential Offer. +* **Key Generation and Attestation**: The wallet generates a secure cryptographic EC P-256 key pair + in the phone's hardware-backed **Android KeyStore**. It creates a signed **DPoP Proof** and an * + *Android Keystore Key Attestation** to prove the key is tied to a genuine physical device. +* **Hardware-backed attestation process**: The wallet sends the key proofs to the issuer's endpoint + and receives the signed credential payload (in mDoc or sd-jwt format) +* **Saving the credential**: The wallet decrypts the signed credential payload if it is encrypted, + and persists it in the Room Database. --- @@ -73,54 +105,88 @@ CMWallet/ ### Detailed Directory Mapping #### 1. App Configuration and Lifecycle + * **`CmWalletApplication.kt`** - * **Role**: The application initialization class. - * **Usage**: On startup, it configures the database and loads the compiled WASM matcher files (`openid4vp1_0.wasm`, `pnv.wasm`) from the app's `assets/`. It establishes a reactive Kotlin Flow that collects active credentials from the repository, transforms them into a `OpenId4VpRegistry` model, and updates Credential Manager's `RegistryManager` dynamically in the background. + * **Role**: The application initialization class. + * **Usage**: On startup, it configures the database and loads the compiled WASM matcher files ( + `openid4vp1_0.wasm`, `pnv.wasm`) from the app's `assets/`. It establishes a reactive Kotlin + Flow that collects active credentials from the repository, transforms them into a + `OpenId4VpRegistry` model, and updates Credential Manager's `RegistryManager` dynamically in + the background. #### 2. UI, Credential Presentation, and Handling Credential Issuance + * **`HomeScreen.kt` and `HomeViewModel.kt` (`/ui`)** - * **Role**: Renders the wallet's main dashboard. - * **Usage**: Displays stored digital cards in a swipeable wallet visual. Selecting a card opens the `CredentialDialog` for more detailed viewing. + * **Role**: Renders the wallet's main dashboard. + * **Usage**: Displays stored digital cards in a swipeable wallet visual. Selecting a card opens + the `CredentialDialog` for more detailed viewing. * **`GetCredentialActivity.kt` (`/getcred`)** - * **Role**: Entry point for Android's `GET_CREDENTIAL` flow. - * **Usage**: This activity extracts the request JSON. It calls `processDigitalCredentialOption()` to filter matched claims, triggers the system's biometric lock, compiles the cryptographic response, and calls `PendingIntentHandler.setGetCredentialResponse()` to deliver it to the calling verifier app. + * **Role**: Entry point for Android's `GET_CREDENTIAL` flow. + * **Usage**: This activity extracts the request JSON. It calls + `processDigitalCredentialOption()` to filter matched claims, triggers the system's biometric + lock, compiles the cryptographic response, and calls + `PendingIntentHandler.setGetCredentialResponse()` to deliver it to the calling verifier app. * **`CreateCredentialActivity.kt` and `CreateCredentialViewModel.kt` (`/createcred`)** - * **Role**: Entry point for Android's `CREATE_CREDENTIAL` system provider flow. - * **Usage**: Initiated during an issuance request. Displays a sheet presenting the issuer authentication flow (`AuthWebView`), manages network state steps, triggers key generation, requests the credential, and updates the Room DB upon approval. + * **Role**: Entry point for Android's `CREATE_CREDENTIAL` system provider flow. + * **Usage**: Initiated during an issuance request. Displays a sheet presenting the issuer + authentication flow (`AuthWebView`), manages network state steps, triggers key generation, + requests the credential, and updates the Room DB upon approval. #### 3. Protocol Implementations + * **`/openid4vci`** - * **`OpenId4VCI.kt`**: Orchestrates HTTP communication with the Issuer. Uses **Ktor** to fetch OAuth2 authorization endpoints, post **PAR** (Pushed Authorization Requests), and retrieve issued credentials. - * **`class DeviceKey`**: Contains `SoftwareKey` and `HardwareKey` classes representing device keys. + * **`OpenId4VCI.kt`**: Orchestrates HTTP communication with the Issuer. Uses **Ktor** to fetch + OAuth2 authorization endpoints, post **PAR** (Pushed Authorization Requests), and retrieve + issued credentials. + * **`class DeviceKey`**: Contains `SoftwareKey` and `HardwareKey` classes representing device + keys. * **`/openid4vp`** - * **`OpenId4VP.kt`**: Parses verifier presentation requests. Evaluates incoming nonces, extracts client metadata, parses query formats, and encrypts final payloads using JWE (JSON Web Encryption). - * **`DCQL.kt`**: A pure Kotlin parser for the **Digital Credential Query Language** specification, allowing the app to perform query evaluations inside its own execution thread. + * **`OpenId4VP.kt`**: Parses verifier presentation requests. Evaluates incoming nonces, extracts + client metadata, parses query formats, and encrypts final payloads using JWE (JSON Web + Encryption). + * **`DCQL.kt`**: A pure Kotlin parser for the **Digital Credential Query Language** + specification, allowing the app to perform query evaluations inside its own execution thread. #### 4. Digital Credential Formats + * **`/mdoc`** - * **`MDoc.kt`**: A parser for **ISO 18013-5** mobile driving license documents, converting binary CBOR namespaces and elements into structured Kotlin maps (`issuerSignedNamespaces`). - * **`mdoc/Utils.kt`**: Cryptographic helper functions: - * `generateDeviceResponse()`: Generates a standard-compliant COSE Sign1 signature utilizing the device private key, binding it to the verified claims. - * `createSessionTranscript()`: Binds handover parameters (like domains and session nonces) into a SHA-256 byte array prevent replay attacks. - * `filterIssuerSigned()`: Filters out the raw, verified issuer-signed claims to reveal only the elements the user chose to share. + * **`MDoc.kt`**: A parser for **ISO 18013-5** mobile driving license documents, converting + binary CBOR namespaces and elements into structured Kotlin maps (`issuerSignedNamespaces`). + * **`mdoc/Utils.kt`**: Cryptographic helper functions: + * `generateDeviceResponse()`: Generates a standard-compliant COSE Sign1 signature utilizing + the device private key, binding it to the verified claims. + * `createSessionTranscript()`: Binds handover parameters (like domains and session nonces) + into a SHA-256 byte array prevent replay attacks. + * `filterIssuerSigned()`: Filters out the raw, verified issuer-signed claims to reveal only + the elements the user chose to share. * **`/sdjwt`** - * **`SdJwt.kt`**: Integrates Selective Disclosure JSON Web Tokens. - * **Key Function - `present()`**: Collects the verifier-selected claims, gathers their corresponding salt disclosures (formatted as base64 hashes), builds a **Key-Bound JWT** signed by the holder's private key, and joins them into a tilde (`~`) delimited string format for submission. + * **`SdJwt.kt`**: Integrates Selective Disclosure JSON Web Tokens. + * **Key Function - `present()`**: Collects the verifier-selected claims, gathers their + corresponding salt disclosures (formatted as base64 hashes), builds a **Key-Bound JWT** signed + by the holder's private key, and joins them into a tilde (`~`) delimited string format for + submission. #### 5. Database and Storage (`/data`) + * **`repository/CredentialRepository.kt`** - * **Role**: Synchronizes persistence layers. - * **Usage**: Combines dynamic sqlite database records (`CredentialDatabaseDataSource`) and test credentials (`TestCredentialsDataSource`) into unified flows. It is responsible for transforming standard database items into Android System-level `DigitalCredentialEntry` structures (like `MdocEntry` and `SdJwtEntry`) including display properties and titles. + * **Role**: Synchronizes persistence layers. + * **Usage**: Combines dynamic sqlite database records (`CredentialDatabaseDataSource`) and test + credentials (`TestCredentialsDataSource`) into unified flows. It is responsible for + transforming standard database items into Android System-level `DigitalCredentialEntry` + structures (like `MdocEntry` and `SdJwtEntry`) including display properties and titles. --- ## The WASM Query Matchers (`/matcher` and `/matcher-rs`) Credential matching does not happen directly in the main wallet app code. Instead: + 1. Matcher code is written in C/C++ (`matcher/`) or Rust (`matcher-rs/`). 2. It implements DCQL parser rules and format-specific validation. 3. It compiles directly to a WebAssembly binary target in `build.sh` 4. The resulting `.wasm` files are placed in `app/src/main/assets/`. -5. When a request arrives, Credential Manager loads these WASM binaries into an isolated system sandbox for query matching. +5. When a request arrives, Credential Manager loads these WASM binaries into an isolated system + sandbox for query matching. -Note that matches are not required to be separately defined; Credential Manager already includes a default matcher. \ No newline at end of file +Note that matches are not required to be separately defined; Credential Manager already includes a +default matcher. \ No newline at end of file From 1456b6f459a62ac3b7516f2cc7b3c4ab55f1d9c7 Mon Sep 17 00:00:00 2001 From: Chiping Yeh Date: Thu, 25 Jun 2026 14:36:18 -0400 Subject: [PATCH 3/7] Apply reviewer feedback --- README.md | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 6f3870f..4b27180 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,14 @@ apps) through the **OpenID4VP** standard. CMWallet is built on Jetpack Compose, using Room for storage and Ktor for networking. To learn about handling requests for credentials from other applications, or **Verifiers**, -see [Credential Presentation](#1-credential-presentation). +see [Credential Presentation](#1-credential-presentation). Learn more on +the [Android developer +website on displaying credentials](https://developer.android.com/identity/digital-credentials/credential-holder/credential-holder). To learn about handling requests to store credentials from credential **issuers**, -see [Credential Issuance](#2-credential-issuance). +see [Credential Issuance](#2-credential-issuance). Learn more on +the [Android developer +website on handling issuance](https://developer.android.com/identity/digital-credentials/credential-holder/issue-credential). ### 1. Credential Presentation @@ -46,25 +50,26 @@ invoked, and then the credential will be presented to the verifier: metadata, Credential Manager runs the wallet's compiled WebAssembly (WASM) matching module (e.g., `openid4vp1_0.wasm`) in an offline, secure system sandbox. The matcher evaluates the verifier's query against the stored credentials without revealing any private user details to the calling - app. Credential Manager comes with a default matcher if none are specified. -* **Holder invocation**: If a matching credential is found, Android displays the card to the user. - Clicking the card invokes the holder and launches `GetCredentialActivity`. CMWallet launches an - additional `BiometricPrompt` during invocation for additional user consent. -* **Presentation Assembly**: The wallet extracts the requested claims, builds a cryptographically - secure **Session Transcript** to bind the response to the specific connection, signs the response - using the private key stored in secure hardware, packages the payload, and returns it to the - calling verifier. + app. Credential Manager comes with a robust matcher that supports OpenID4VP, including the sd-jwt + VC and the mdoc credential types, by default. +* **Holder invocation**: If matching credential(s) are found, Android displays a credential selector + with the matched credentials to the user. The user selects the credential they want, which then + invokes the holder and launches the holder activity. CMWallet launches an additional + `BiometricPrompt` during invocation for additional user consent. +* **Presentation Assembly**: The wallet extracts the requested claims, signs the response using the + private key stored in secure hardware, packages the payload, and returns it to the calling + verifier. ### 2. Credential Issuance Handles credential issuance requests from issuers. When a user initiates getting a credential from -an issuer by scanning a QR code or opening a link, the issuer can trigger a system intent that -launches the credential creation and storage process: +an issuer by scanning a QR code or opening a link, the issuer calls the Android Credential Manager +API that launches the credential creation and storage process: ``` [Issuer app] ──► Triggers system intent: CREATE_CREDENTIAL ──► calls Credential Manater's CreateCredentialActivity │ -[Room DB] ◄── Save issued credential ◄── Hardware-backed attestation process ◄── Request credential from Issuer + Save issued credential ◄── Hardware-backed attestation process ◄── Request credential from Issuer ``` * **Issuer triggers intent**: The issuer triggers Android's `CREATE_CREDENTIAL` system intent, From bfce41186addb120ca26204d7ad705bf479868d8 Mon Sep 17 00:00:00 2001 From: Chiping Yeh Date: Tue, 7 Jul 2026 17:13:45 -0400 Subject: [PATCH 4/7] Made updates per feedback --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4b27180..e4fb09b 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,7 @@ the [Android developer website on displaying credentials](https://developer.android.com/identity/digital-credentials/credential-holder/credential-holder). To learn about handling requests to store credentials from credential **issuers**, -see [Credential Issuance](#2-credential-issuance). Learn more on -the [Android developer +see [Credential Issuance](#2-credential-issuance). Learn more on the [Android developer website on handling issuance](https://developer.android.com/identity/digital-credentials/credential-holder/issue-credential). ### 1. Credential Presentation @@ -43,7 +42,7 @@ invoked, and then the credential will be presented to the verifier: ``` [Holder(wallet) app] ──► Registers metadata with Credential Manager -[Verifier app] ──► Requests digital credential claim(s) ──► Credential Manager matches claims and displays options to user ──► User selects credential ──► Holder is invoked ──► Holder returns signed presentation to verifier +[Verifier app] ──► Requests digital credential(s) ──► Credential Manager matches credentials and displays options to user ──► User selects credential(s) ──► Holder is invoked ──► Holder returns signed presentation to verifier ``` * **Matchers (WASM matching)**: In order to match a verifier's requested claims with registered @@ -59,26 +58,37 @@ invoked, and then the credential will be presented to the verifier: * **Presentation Assembly**: The wallet extracts the requested claims, signs the response using the private key stored in secure hardware, packages the payload, and returns it to the calling verifier. +Additional features: + +* **Multi-credential presentation**: Credential Manager supports requesting multiple credentials (e.g. age + payment) in a single request. +* **User-friendly UI**: The credential selector UI automatically adapts to different use cases, such as verification or payment confirmation, displaying the most appropriate layout for the request. ### 2. Credential Issuance -Handles credential issuance requests from issuers. When a user initiates getting a credential from -an issuer by scanning a QR code or opening a link, the issuer calls the Android Credential Manager -API that launches the credential creation and storage process: +Normally a user can trigger credential issuance in two ways: -``` -[Issuer app] ──► Triggers system intent: CREATE_CREDENTIAL ──► calls Credential Manater's CreateCredentialActivity - │ - Save issued credential ◄── Hardware-backed attestation process ◄── Request credential from Issuer -``` +* Issuer-initiated flow: the user triggers a request to issue a VDC from an issuer application or + website. For example, a website may offer its users an option to "Add your passport to your + wallet". The issuer calls the [Credential Manager issuance API](https://developer.android.com/identity/digital-credentials/credential-issuer/issue-credentials) + to make an OpenID4VCI Credential Offer request. To handle such requests, a wallet must first + integrate with Credential Manager to register its metadata. Credential Manager will display + relevant options for a user to select. After the user selects a wallet option, the wallet + application will be invoked and can proceed with the steps needed to complete the issuance. + +* Wallet-initiated flow: the user requests to add a VDC from their wallet application. For example, + a wallet may offer a button to "Add your passport". In this case, the wallet maintains its + supported issuer list and metadata. It does not need to integrate with the Android Credential + Manager to complete this function. + +CMWallet supports the issuer initiated flow, allowing arbitrary types of credentials in the SD-JWT +VC or mdoc format. * **Issuer triggers intent**: The issuer triggers Android's `CREATE_CREDENTIAL` system intent, launching `CreateCredentialActivity`. * **Progress bottom sheet**: The wallet displays a Compose bottom sheet showing a progress bar while it parses the Credential Offer. -* **Key Generation and Attestation**: The wallet generates a secure cryptographic EC P-256 key pair - in the phone's hardware-backed **Android KeyStore**. It creates a signed **DPoP Proof** and an * - *Android Keystore Key Attestation** to prove the key is tied to a genuine physical device. +* **Key Generation and Attestation**: CMWallet supports [Android key attestation](https://developer.android.com/identity/digital-credentials/credential-issuer/keystore-attestation) for credential +binding keys. It provides a direct Android hardware backed key attestation and is the recommended key proof approach on Android. * **Hardware-backed attestation process**: The wallet sends the key proofs to the issuer's endpoint and receives the signed credential payload (in mDoc or sd-jwt format) * **Saving the credential**: The wallet decrypts the signed credential payload if it is encrypted, @@ -104,7 +114,23 @@ CMWallet/ │ ├── sdjwt/ # sd-jwt VC format presentation and verification │ └── cbor/ # CBOR encoding helpers ├── matcher/ # C implementation of wasm matcher -└── matcher-rs/ # Rust implementation of wasm matcher +├── matcher-rs/ # Rust implementation of wasm matcher +├── testdata/ # Offline test credential generator & key material +│ ├── create_database.py # Python script to sign & generate mdoc and SD-JWT test credentials +│ ├── helpers.py # Utilities for parsing SD-JWT claim paths and display metadata +│ ├── database_in.json # Raw unsigned input credentials (claims, namespaces, and display info) +│ ├── database.json # Output database containing signed test credentials & device keys +│ ├── ds_cert_mdoc.pem # Document Signer (DS) certificate for mdoc test credentials +│ ├── ds_private_key_mdoc.pem # Private key for signing mdoc test credentials +│ ├── ds_cert_sdjwt.pem # Document Signer (DS) certificate for SD-JWT test credentials +│ ├── ds_private_key_sdjwt.json # JWK private key for signing SD-JWT test credentials +│ ├── issuer_cert_mdoc.pem # Issuer Root CA certificate for mdoc +│ ├── issuer_cert_sdjwt.pem # Issuer Root CA certificate for SD-JWT +│ ├── issuer_private_key_mdoc.pem # Issuer root private key for mdoc +│ ├── issuer_private_key_sdjwt.json # Issuer root private key for SD-JWT +│ └── issuance_wallet_attestation_cert.pem # Certificate used for wallet client attestation +│ ├── README.md # Instructions for building and deploying test database + ``` ### Detailed Directory Mapping From f9de2656bcae26952a59794ae0d4b2d92b7c5e91 Mon Sep 17 00:00:00 2001 From: Chiping Yeh Date: Tue, 7 Jul 2026 17:15:44 -0400 Subject: [PATCH 5/7] Minor formatting --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4fb09b..218a622 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ invoked, and then the credential will be presented to the verifier: * **Presentation Assembly**: The wallet extracts the requested claims, signs the response using the private key stored in secure hardware, packages the payload, and returns it to the calling verifier. + Additional features: * **Multi-credential presentation**: Credential Manager supports requesting multiple credentials (e.g. age + payment) in a single request. From 8ab8ea504328536038bccda7541b8c3a8c17cb69 Mon Sep 17 00:00:00 2001 From: Helen Qin Date: Wed, 8 Jul 2026 06:08:48 +0000 Subject: [PATCH 6/7] Clarify presentation and issuance flows in README.md --- README.md | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 218a622..1144b74 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,11 @@ apps) through the **OpenID4VP** standard. CMWallet is built on Jetpack Compose, using Room for storage and Ktor for networking. -To learn about handling requests for credentials from other applications, or **Verifiers**, -see [Credential Presentation](#1-credential-presentation). Learn more on +CMWallet handles requests for credential presentation from **Verifiers** following the [Android developer website on displaying credentials](https://developer.android.com/identity/digital-credentials/credential-holder/credential-holder). -To learn about handling requests to store credentials from credential **issuers**, -see [Credential Issuance](#2-credential-issuance). Learn more on the [Android developer +CMWallet handles requests to store credentials from **issuers** following the [Android developer website on handling issuance](https://developer.android.com/identity/digital-credentials/credential-holder/issue-credential). ### 1. Credential Presentation @@ -40,9 +38,9 @@ user to select. After the user selects a credential, the corresponding holder ap invoked, and then the credential will be presented to the verifier: ``` -[Holder(wallet) app] ──► Registers metadata with Credential Manager +[Holder(wallet) app] ──► Registers presentation metadata with Credential Manager -[Verifier app] ──► Requests digital credential(s) ──► Credential Manager matches credentials and displays options to user ──► User selects credential(s) ──► Holder is invoked ──► Holder returns signed presentation to verifier +[Verifier app / website] ──► Requests digital credential(s) ──► Credential Manager matches credentials and displays options to user ──► User selects credential(s) ──► Holder is invoked ──► Holder returns signed presentation to verifier ``` * **Matchers (WASM matching)**: In order to match a verifier's requested claims with registered @@ -84,16 +82,17 @@ Normally a user can trigger credential issuance in two ways: CMWallet supports the issuer initiated flow, allowing arbitrary types of credentials in the SD-JWT VC or mdoc format. -* **Issuer triggers intent**: The issuer triggers Android's `CREATE_CREDENTIAL` system intent, - launching `CreateCredentialActivity`. -* **Progress bottom sheet**: The wallet displays a Compose bottom sheet showing a progress bar while - it parses the Credential Offer. +The credential issuance flow works similar to the presentation flow: + +``` +[Holder(wallet) app] ──► Registers issuance metadata with Credential Manager + +[Issuer app / website] ──► Requests credential issuance ──► Credential Manager matches and displays holder options to user ──► User selects holder ──► Holder is invoked ──► Holder completes the issuance operation +``` + * **Key Generation and Attestation**: CMWallet supports [Android key attestation](https://developer.android.com/identity/digital-credentials/credential-issuer/keystore-attestation) for credential binding keys. It provides a direct Android hardware backed key attestation and is the recommended key proof approach on Android. -* **Hardware-backed attestation process**: The wallet sends the key proofs to the issuer's endpoint - and receives the signed credential payload (in mDoc or sd-jwt format) -* **Saving the credential**: The wallet decrypts the signed credential payload if it is encrypted, - and persists it in the Room Database. +* **Asynchronous issuance fulfillment**: Unlike presentation fulfillment, where the holder always returns a completed presentation result to the caller, issuance fulfillment involves multiple server interactions and sometimes a user verification process that can take hours to complete. Therefore, the holder may return a default response indicating the successful receipt of the issuance request. This does not necessarily mean that the issuance has completed; instead, it signals to the calling issuer that they can transition the user to an appropriate next step in the UI. --- @@ -211,14 +210,6 @@ CMWallet/ ## The WASM Query Matchers (`/matcher` and `/matcher-rs`) -Credential matching does not happen directly in the main wallet app code. Instead: - -1. Matcher code is written in C/C++ (`matcher/`) or Rust (`matcher-rs/`). -2. It implements DCQL parser rules and format-specific validation. -3. It compiles directly to a WebAssembly binary target in `build.sh` -4. The resulting `.wasm` files are placed in `app/src/main/assets/`. -5. When a request arrives, Credential Manager loads these WASM binaries into an isolated system - sandbox for query matching. +These folders contain the source code for the default WASM matchers, which support all OpenID4VP and OpenID4VCI use cases. -Note that matches are not required to be separately defined; Credential Manager already includes a -default matcher. \ No newline at end of file +Note that the Credential Manager API already includes these default matchers, so you will most likely not need to create any custom matchers yourself. \ No newline at end of file From 0011d9d4c55d9644c73215a5e153b6724aa35dea Mon Sep 17 00:00:00 2001 From: Chiping Yeh Date: Wed, 8 Jul 2026 14:19:28 -0400 Subject: [PATCH 7/7] minor text update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1144b74..79d2c4b 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ invoked, and then the credential will be presented to the verifier: * **Holder invocation**: If matching credential(s) are found, Android displays a credential selector with the matched credentials to the user. The user selects the credential they want, which then invokes the holder and launches the holder activity. CMWallet launches an additional - `BiometricPrompt` during invocation for additional user consent. + `BiometricPrompt` during invocation for additional user authentication. * **Presentation Assembly**: The wallet extracts the requested claims, signs the response using the private key stored in secure hardware, packages the payload, and returns it to the calling verifier.