diff --git a/apps/funke/constants.ts b/apps/funke/constants.ts index 98904f23..7d2b3f7c 100644 --- a/apps/funke/constants.ts +++ b/apps/funke/constants.ts @@ -11,3 +11,14 @@ const animoFunkeRelyingPartyCertificate = 'MIIBAzCBq6ADAgECAhArxq0w60RTDK4WY9HzgcvBMAoGCCqGSM49BAMCMAAwIBcNNzAwMTAxMDAwMDAwWhgPMjI4NjExMjAxNzQ2NDBaMAAwOTATBgcqhkjOPQIBBggqhkjOPQMBBwMiAALcD1XzKepFxWMAOqV+ln1fybBt7DRO5CV0f9A6mRp2xaMlMCMwIQYDVR0RBBowGIYWaHR0cHM6Ly9mdW5rZS5hbmltby5pZDAKBggqhkjOPQQDAgNHADBEAiAfvGG6sqrvzIMWYpJB5VLloo9f51loYXSkKxJIOztlNwIgLLSvEl0Dmp5vtj2buZ2nXQ2RBKxiLbc5eYGeMeoUnjk=' export const trustedX509Certificates = [bdrPidIssuerCertificate, animoFunkeRelyingPartyCertificate] + +// https://gitlab.opencode.de/bmi/eudi-wallet/eidas-2.0-architekturkonzept/-/blob/main/architecture-proposal.md#pid-contents +const sdJwtVcVcts = ['https://example.bmi.bund.de/credential/pid/1.0', 'urn:eu.europa.ec.eudi:pid:1'] + +// TODO +const msoMdocNamespaces = ['org.iso.18013.5.1.mDL'] + +export const pidSchemes = { + sdJwtVcVcts, + msoMdocNamespaces, +} diff --git a/apps/funke/package.json b/apps/funke/package.json index 57bd043a..b32dab9f 100644 --- a/apps/funke/package.json +++ b/apps/funke/package.json @@ -10,7 +10,7 @@ "prebuild": "APP_VARIANT=development expo prebuild --no-install" }, "dependencies": { - "@animo-id/expo-ausweis-sdk": "0.0.1-alpha.5", + "@animo-id/expo-ausweis-sdk": "0.0.1-alpha.6", "@animo-id/expo-secure-environment": "0.1.0-alpha.1", "@credo-ts/core": "*", "@expo-google-fonts/open-sans": "^0.2.3", diff --git a/apps/funke/use-cases/ReceivePidUseCase.ts b/apps/funke/use-cases/ReceivePidUseCase.ts index d1384932..baf22551 100644 --- a/apps/funke/use-cases/ReceivePidUseCase.ts +++ b/apps/funke/use-cases/ReceivePidUseCase.ts @@ -1,4 +1,5 @@ import type { AppAgent } from '@/agent' +import { pidSchemes } from '@/constants' import { AusweisAuthFlow } from '@animo-id/expo-ausweis-sdk' import { type OpenId4VciRequestTokenResponse, @@ -114,6 +115,7 @@ export class ReceivePidUseCase { resolvedCredentialOffer: this.resolvedCredentialOffer, credentialConfigurationIdToRequest, clientId: ReceivePidUseCase.CLIENT_ID, + pidSchemes, }) // TODO: add error handling everywhere to set state to error @@ -129,7 +131,10 @@ export class ReceivePidUseCase { } private async acquireAccessToken(refreshUrl: string) { - this.assertState({ expectedState: 'id-card-auth', newState: 'acquire-access-token' }) + this.assertState({ + expectedState: 'id-card-auth', + newState: 'acquire-access-token', + }) try { const authorizationCodeResponse = await fetch(refreshUrl) @@ -154,7 +159,10 @@ export class ReceivePidUseCase { agent: this.agent, }) - this.assertState({ expectedState: 'acquire-access-token', newState: 'retrieve-credential' }) + this.assertState({ + expectedState: 'acquire-access-token', + newState: 'retrieve-credential', + }) } catch (error) { this.handleError() } @@ -163,7 +171,10 @@ export class ReceivePidUseCase { private assertState({ expectedState, newState, - }: { expectedState: ReceivePidUseCase['currentState']; newState?: ReceivePidUseCase['currentState'] }) { + }: { + expectedState: ReceivePidUseCase['currentState'] + newState?: ReceivePidUseCase['currentState'] + }) { if (this.currentState !== expectedState) { throw new Error(`Expected state to be ${expectedState}. Found ${this.currentState}`) } diff --git a/package.json b/package.json index 73b83a9e..21786982 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,9 @@ "@credo-ts/openid4vc": "0.5.10-alpha-20240805102402", "@credo-ts/question-answer": "0.5.10-alpha-20240805102402", "@credo-ts/react-hooks": "0.6.1", - "@credo-ts/react-native": "0.5.10-alpha-20240805102402" + "@credo-ts/react-native": "0.5.10-alpha-20240805102402", + + "@animo-id/expo-secure-environment": "0.1.0-alpha.1" }, "patchedDependencies": { "@credo-ts/openid4vc@0.5.10-alpha-20240805102402": "patches/@credo-ts__openid4vc@0.5.9.patch", diff --git a/packages/agent/src/invitation/handler.ts b/packages/agent/src/invitation/handler.ts index f6905507..50122490 100644 --- a/packages/agent/src/invitation/handler.ts +++ b/packages/agent/src/invitation/handler.ts @@ -142,11 +142,13 @@ export const receiveCredentialFromOpenId4VciOffer = async ({ credentialConfigurationIdToRequest, accessToken, clientId, + pidSchemes, }: { agent: EitherAgent resolvedCredentialOffer: OpenId4VciResolvedCredentialOffer credentialConfigurationIdToRequest?: string clientId?: string + pidSchemes?: { sdJwtVcVcts: Array; msoMdocNamespaces: Array } // TODO: cNonce should maybe be provided separately (multiple calls can have different c_nonce values) accessToken: OpenId4VciRequestTokenResponse @@ -182,6 +184,7 @@ export const receiveCredentialFromOpenId4VciOffer = async ({ supportsAllDidMethods, supportsJwk, credentialFormat, + supportedCredentialId, }) => { // First, we try to pick a did method // Prefer did:jwk, otherwise use did:key, otherwise use undefined @@ -199,26 +202,26 @@ export const receiveCredentialFromOpenId4VciOffer = async ({ didMethod = 'key' } - let key: Key | undefined = undefined - - // For P-256 we first try secure enclave - if (keyType === KeyType.P256) { - key = await agent.wallet - .createKey({ - keyType, - keyBackend: KeyBackend.SecureElement, - }) - .catch((e) => { - agent.config.logger.warn('Could not create a key in the secure element', e as Record) - return agent.wallet.createKey({ - keyType, - }) - }) - } else { - key = await agent.wallet.createKey({ - keyType, - }) - } + const offeredCredentialConfiguration = supportedCredentialId + ? resolvedCredentialOffer.offeredCredentialConfigurations[supportedCredentialId] + : undefined + + const shouldKeyBeHardwareBackedForMsoMdoc = false + // offeredCredentialConfiguration?.format === "mso_mdoc" && + // pidSchemes?.msoMdocNamespaces.includes( + // offeredCredentialConfiguration.namespace + // ); + const shouldKeyBeHardwareBackedForSdJwtVc = + offeredCredentialConfiguration?.format === 'vc+sd-jwt' && + pidSchemes?.sdJwtVcVcts.includes(offeredCredentialConfiguration.vct) + + // TODO: add mso-mdoc config from above + const shouldKeyBeHardwareBacked = shouldKeyBeHardwareBackedForSdJwtVc ?? shouldKeyBeHardwareBackedForMsoMdoc + + const key = await agent.wallet.createKey({ + keyType, + keyBackend: shouldKeyBeHardwareBacked ? KeyBackend.SecureElement : KeyBackend.Software, + }) if (didMethod) { const didResult = await agent.dids.create({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55b122c7..640a8d5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,7 @@ overrides: '@credo-ts/question-answer': 0.5.10-alpha-20240805102402 '@credo-ts/react-hooks': 0.6.1 '@credo-ts/react-native': 0.5.10-alpha-20240805102402 + '@animo-id/expo-secure-environment': 0.1.0-alpha.1 patchedDependencies: '@credo-ts/openid4vc@0.5.10-alpha-20240805102402': @@ -52,8 +53,8 @@ importers: apps/funke: dependencies: '@animo-id/expo-ausweis-sdk': - specifier: 0.0.1-alpha.5 - version: 0.0.1-alpha.5(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + specifier: 0.0.1-alpha.6 + version: 0.0.1-alpha.6(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) '@animo-id/expo-secure-environment': specifier: 0.1.0-alpha.1 version: 0.1.0-alpha.1(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) @@ -506,7 +507,7 @@ importers: version: 0.5.10-alpha-20240805102402(@hyperledger/anoncreds-shared@0.2.2)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(web-streams-polyfill@3.3.3) '@credo-ts/askar': specifier: 0.5.10-alpha-20240805102402 - version: 0.5.10-alpha-20240805102402(@animo-id/expo-secure-environment@0.0.1-alpha.0(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(@hyperledger/aries-askar-shared@0.2.3)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(web-streams-polyfill@3.3.3) + version: 0.5.10-alpha-20240805102402(@animo-id/expo-secure-environment@0.1.0-alpha.1(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(@hyperledger/aries-askar-shared@0.2.3)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(web-streams-polyfill@3.3.3) '@credo-ts/cheqd': specifier: 0.5.10-alpha-20240805102402 version: 0.5.10-alpha-20240805102402(@hyperledger/anoncreds-shared@0.2.2)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(web-streams-polyfill@3.3.3) @@ -701,15 +702,8 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@animo-id/expo-ausweis-sdk@0.0.1-alpha.5': - resolution: {integrity: sha512-4m/D/fJFlKIUNY1mFf+xxxDz5EmDx5Cob514vKzcMgQkMItEtGp21/JqDxBfVKyM4SDFMG3Pxx2zjaxdtZt3uw==} - peerDependencies: - expo: '*' - react: 18.2.0 - react-native: '*' - - '@animo-id/expo-secure-environment@0.0.1-alpha.0': - resolution: {integrity: sha512-5ldT6osZjTxJgxi9Qr5hm8VN1FTsXkNhwBdEcu7qMUvnd2LhKSOyKlUCUAOfDNuhFqXDU1K6Cmy3P7kJ6dEK/Q==} + '@animo-id/expo-ausweis-sdk@0.0.1-alpha.6': + resolution: {integrity: sha512-XkijcBkXHbu2jBKMol3Vy61ABP9qwY7EGu+vHPwSp130820I3LcpKneF92RAJVfIQIMMnN+v3DYjhRXtg6fQzQ==} peerDependencies: expo: '*' react: 18.2.0 @@ -1606,7 +1600,7 @@ packages: '@credo-ts/askar@0.5.10-alpha-20240805102402': resolution: {integrity: sha512-3Y0TcGaG7Ook0Ej36tjxBzYzsRNGiZU1hE5WNUa22TSk+pU1t2VgeoLdIdMk0CHkn7G1aXBluDTgLcr4+lWCaw==} peerDependencies: - '@animo-id/expo-secure-environment': ^0.0.1-alpha.0 + '@animo-id/expo-secure-environment': 0.1.0-alpha.1 '@hyperledger/aries-askar-shared': ^0.2.3 peerDependenciesMeta: '@animo-id/expo-secure-environment': @@ -9563,7 +9557,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@animo-id/expo-ausweis-sdk@0.0.1-alpha.5(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': + '@animo-id/expo-ausweis-sdk@0.0.1-alpha.6(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': dependencies: '@expo/config-plugins': 8.0.8 expo: 51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)) @@ -9572,15 +9566,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@animo-id/expo-secure-environment@0.0.1-alpha.0(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': - dependencies: - '@peculiar/asn1-ecc': 2.3.8 - '@peculiar/asn1-schema': 2.3.8 - '@peculiar/asn1-x509': 2.3.8 - react: 18.2.0 - react-native: 0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0) - optional: true - '@animo-id/expo-secure-environment@0.1.0-alpha.1(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)': dependencies: '@peculiar/asn1-ecc': 2.3.8 @@ -10769,7 +10754,7 @@ snapshots: - supports-color - web-streams-polyfill - '@credo-ts/askar@0.5.10-alpha-20240805102402(@animo-id/expo-secure-environment@0.0.1-alpha.0(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(@hyperledger/aries-askar-shared@0.2.3)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(web-streams-polyfill@3.3.3)': + '@credo-ts/askar@0.5.10-alpha-20240805102402(@animo-id/expo-secure-environment@0.1.0-alpha.1(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(@hyperledger/aries-askar-shared@0.2.3)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(web-streams-polyfill@3.3.3)': dependencies: '@credo-ts/core': 0.5.10-alpha-20240805102402(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(web-streams-polyfill@3.3.3) '@hyperledger/aries-askar-shared': 0.2.3 @@ -10779,7 +10764,7 @@ snapshots: rxjs: 7.8.1 tsyringe: 4.8.0 optionalDependencies: - '@animo-id/expo-secure-environment': 0.0.1-alpha.0(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + '@animo-id/expo-secure-environment': 0.1.0-alpha.1(expo@51.0.23(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2)))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.2(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - domexception - encoding