diff --git a/.changeset/brave-cars-deny.md b/.changeset/brave-cars-deny.md new file mode 100644 index 0000000000..d97a9624c4 --- /dev/null +++ b/.changeset/brave-cars-deny.md @@ -0,0 +1,5 @@ +--- +'@credo-ts/core': minor +--- + +`createDeviceResponse` now returns bytes and not base64 encoded bytes diff --git a/.changeset/cool-pets-grab.md b/.changeset/cool-pets-grab.md new file mode 100644 index 0000000000..ceb9566f62 --- /dev/null +++ b/.changeset/cool-pets-grab.md @@ -0,0 +1,9 @@ +--- +'@credo-ts/anoncreds': minor +'@credo-ts/askar': minor +'@credo-ts/core': minor +--- + +- Rely on Uint8Array instead of Buffer for internal key bytes representation +- Remove dependency on external Big Number libraries +- Default to use of uncompressed keys for Secp256k1, Secp256r1, Secp384r1 and Secp521r1 diff --git a/.changeset/cuddly-jobs-learn.md b/.changeset/cuddly-jobs-learn.md new file mode 100644 index 0000000000..bfdb13b957 --- /dev/null +++ b/.changeset/cuddly-jobs-learn.md @@ -0,0 +1,5 @@ +--- +'@credo-ts/openid4vc': minor +--- + +fix: typo statefull -> stateful in configuration of OpenID4VCI module diff --git a/.changeset/cyan-parents-relax.md b/.changeset/cyan-parents-relax.md new file mode 100644 index 0000000000..0b97d5b06e --- /dev/null +++ b/.changeset/cyan-parents-relax.md @@ -0,0 +1,9 @@ +--- +'@credo-ts/anoncreds': patch +'@credo-ts/askar': patch +'@credo-ts/core': patch +--- + +- Remove usage of Big Number libraries and rely on native implementations +- By default rely on uncompressed keys instead of compressed (for P256, P384, P521 and K256) +- Utilze Uint8Array more instead of Buffer (i.e. for internally representing a key) diff --git a/.changeset/eleven-deers-rush.md b/.changeset/eleven-deers-rush.md new file mode 100644 index 0000000000..d035ee95d5 --- /dev/null +++ b/.changeset/eleven-deers-rush.md @@ -0,0 +1,5 @@ +--- +'@credo-ts/core': minor +--- + +refactor: split async `getData` method on x509 certificate to sync `.data` getter and async `getThumbprint` method diff --git a/.changeset/serious-rivers-clap.md b/.changeset/serious-rivers-clap.md new file mode 100644 index 0000000000..45c887c3b4 --- /dev/null +++ b/.changeset/serious-rivers-clap.md @@ -0,0 +1,6 @@ +--- +'@credo-ts/askar': patch +'@credo-ts/core': patch +--- + +feat: support A128CBC-HS256 encryption algorithm for JWE diff --git a/.changeset/smooth-balloons-breathe.md b/.changeset/smooth-balloons-breathe.md new file mode 100644 index 0000000000..bb35834b62 --- /dev/null +++ b/.changeset/smooth-balloons-breathe.md @@ -0,0 +1,6 @@ +--- +'@credo-ts/indy-sdk-to-askar-migration': minor +'@credo-ts/askar': minor +--- + +- depend on @openwallet-foundation/askar instead of @hyperledger/aries-askar diff --git a/.changeset/wet-pianos-walk.md b/.changeset/wet-pianos-walk.md new file mode 100644 index 0000000000..bb9924a4b5 --- /dev/null +++ b/.changeset/wet-pianos-walk.md @@ -0,0 +1,7 @@ +--- +"@credo-ts/cheqd": patch +--- + +fix(cheqd): cheqd revocationRegistryDefinition resource name + +Creating two revocation registries with same name would lead to updating the resource. Adding credential definition tag in the resource name fixes this issue diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 8d1430d9a7..26b1de4061 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -17,7 +17,7 @@ env: # "When concurrency is specified at the job level, order is not guaranteed for jobs or runs that queue within 5 minutes of each other." concurrency: # Cancel previous runs that are not completed yet - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} cancel-in-progress: true jobs: diff --git a/demo-openid/package.json b/demo-openid/package.json index de88ea3f9f..d3f90dae52 100644 --- a/demo-openid/package.json +++ b/demo-openid/package.json @@ -17,9 +17,9 @@ }, "dependencies": { "@hyperledger/anoncreds-nodejs": "^0.2.2", - "@hyperledger/aries-askar-nodejs": "^0.2.3", "@hyperledger/indy-vdr-nodejs": "^0.2.2", "@koa/bodyparser": "^5.1.1", + "@openwallet-foundation/askar-nodejs": "^0.3.1", "express": "^4.18.1", "inquirer": "^8.2.5", "jose": "^5.3.0", diff --git a/demo-openid/src/Holder.ts b/demo-openid/src/Holder.ts index c846e0d928..7f78f8c2dc 100644 --- a/demo-openid/src/Holder.ts +++ b/demo-openid/src/Holder.ts @@ -20,14 +20,14 @@ import { OpenId4VciAuthorizationFlow, preAuthorizedCodeGrantIdentifier, } from '@credo-ts/openid4vc' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import { BaseAgent } from './BaseAgent' import { greenText, Output } from './OutputClass' function getOpenIdHolderModules() { return { - askar: new AskarModule({ ariesAskar }), + askar: new AskarModule({ askar }), openId4VcHolder: new OpenId4VcHolderModule(), x509: new X509Module({ getTrustedCertificatesForVerification: (agentContext, { certificateChain, verification }) => { diff --git a/demo-openid/src/Issuer.ts b/demo-openid/src/Issuer.ts index 37750dfb1b..dee9959051 100644 --- a/demo-openid/src/Issuer.ts +++ b/demo-openid/src/Issuer.ts @@ -33,7 +33,7 @@ import { OpenId4VcVerifierModule, OpenId4VciCredentialFormatProfile, } from '@credo-ts/openid4vc' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import { Router } from 'express' import { BaseAgent } from './BaseAgent' @@ -206,7 +206,7 @@ export class Issuer extends BaseAgent<{ port, name, modules: { - askar: new AskarModule({ ariesAskar }), + askar: new AskarModule({ askar }), openId4VcVerifier: new OpenId4VcVerifierModule({ baseUrl: `${url}/oid4vp`, router: openId4VpRouter, diff --git a/demo-openid/src/Verifier.ts b/demo-openid/src/Verifier.ts index c60ce008a9..730c48d168 100644 --- a/demo-openid/src/Verifier.ts +++ b/demo-openid/src/Verifier.ts @@ -3,7 +3,7 @@ import type { OpenId4VcVerifierRecord } from '@credo-ts/openid4vc' import { AskarModule } from '@credo-ts/askar' import { OpenId4VcVerifierModule } from '@credo-ts/openid4vc' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import { Router } from 'express' import { BaseAgent } from './BaseAgent' @@ -159,7 +159,7 @@ export class Verifier extends BaseAgent<{ askar: AskarModule; openId4VcVerifier: port, name, modules: { - askar: new AskarModule({ ariesAskar }), + askar: new AskarModule({ askar }), openId4VcVerifier: new OpenId4VcVerifierModule({ baseUrl: `${url}/siop`, router: openId4VcSiopRouter, diff --git a/demo/package.json b/demo/package.json index dcb637d1fb..585c263a8b 100644 --- a/demo/package.json +++ b/demo/package.json @@ -15,7 +15,7 @@ "dependencies": { "@hyperledger/indy-vdr-nodejs": "^0.2.2", "@hyperledger/anoncreds-nodejs": "^0.2.2", - "@hyperledger/aries-askar-nodejs": "^0.2.3", + "@openwallet-foundation/askar-nodejs": "^0.3.1", "inquirer": "^8.2.5" }, "devDependencies": { diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 9617ed2641..06d2c69faa 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -34,8 +34,8 @@ import { import { IndyVdrIndyDidResolver, IndyVdrAnonCredsRegistry, IndyVdrModule } from '@credo-ts/indy-vdr' import { agentDependencies, HttpInboundTransport } from '@credo-ts/node' import { anoncreds } from '@hyperledger/anoncreds-nodejs' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import { greenText } from './OutputClass' @@ -144,7 +144,7 @@ function getAskarAnonCredsIndyModules(didcommConfig: DidCommModuleConfigOptions) registrars: [new CheqdDidRegistrar()], }), askar: new AskarModule({ - ariesAskar, + askar, }), } as const } diff --git a/package.json b/package.json index f1d2ebef5b..a67a298162 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,8 @@ "@babel/core": "^7.25.8", "@babel/preset-env": "^7.25.8", "@changesets/cli": "^2.27.5", - "@hyperledger/aries-askar-nodejs": "^0.2.3", + "@openwallet-foundation/askar-nodejs": "^0.3.1", "@jest/types": "^29.6.3", - "@types/bn.js": "^5.1.5", "@types/cors": "^2.8.10", "@types/eslint": "^8.21.2", "@types/express": "^4.17.13", @@ -49,7 +48,6 @@ "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.14.1", - "bn.js": "^5.2.1", "cors": "^2.8.5", "eslint": "^8.36.0", "eslint-config-prettier": "^8.3.0", @@ -70,6 +68,14 @@ "undici": "^6.20.1", "ws": "^8.13.0" }, + "pnpm": { + "overrides": { + "@openid4vc/oid4vci": "file:./local/openid4vc-oid4vci-0.2.0.tgz", + "@openid4vc/oid4vp": "file:./local/openid4vc-oid4vp-0.1.4.tgz", + "@openid4vc/oauth2": "file:./local/openid4vc-oauth2-0.2.0.tgz", + "@openid4vc/utils": "file:./local/openid4vc-utils-0.2.0.tgz" + } + }, "resolutions": { "@types/node": "18.18.8", "undici": "^6.20.1" diff --git a/packages/anoncreds/package.json b/packages/anoncreds/package.json index 7bee830493..f5aa4ef404 100644 --- a/packages/anoncreds/package.json +++ b/packages/anoncreds/package.json @@ -30,8 +30,6 @@ "@credo-ts/core": "workspace:*", "@credo-ts/didcomm": "workspace:*", "@sphereon/pex-models": "^2.3.1", - "big-integer": "^1.6.51", - "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", "reflect-metadata": "^0.1.13" diff --git a/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts b/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts index 9894f41702..9348438bcd 100644 --- a/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts +++ b/packages/anoncreds/src/anoncreds-rs/AnonCredsDataIntegrityService.ts @@ -24,10 +24,10 @@ import { injectable, ClaimFormat, } from '@credo-ts/core' -import BigNumber from 'bn.js' import { AnonCredsHolderServiceSymbol, AnonCredsVerifierServiceSymbol } from '../services' import { fetchCredentialDefinitions, fetchSchemas } from '../utils/anonCredsObjects' +import { bytesToBigint } from '../utils/bytesToBigint' import { assertLinkSecretsMatch } from '../utils/linkSecret' import { getAnonCredsTagsFromRecord } from '../utils/w3cAnonCredsUtils' @@ -163,7 +163,7 @@ export class AnonCredsDataIntegrityService implements IAnonCredsDataIntegritySer const credentialsWithMetadata: CredentialWithRevocationMetadata[] = [] const hash = Hasher.hash(TypedArrayEncoder.fromString(challenge), 'sha-256') - const nonce = new BigNumber(hash).toString().slice(0, 20) + const nonce = bytesToBigint(hash).toString().slice(0, 20) const anonCredsProofRequest: AnonCredsProofRequest = { version: '1.0', diff --git a/packages/anoncreds/src/utils/bytesToBigint.ts b/packages/anoncreds/src/utils/bytesToBigint.ts new file mode 100644 index 0000000000..ab843a7c32 --- /dev/null +++ b/packages/anoncreds/src/utils/bytesToBigint.ts @@ -0,0 +1,12 @@ +export function bytesToBigint(b: Uint8Array): bigint { + if (b.length === 0) { + throw new Error('Empty byte array is not supported') + } + + let value = 0n + for (let i = 0; i < b.length; i++) { + value = (value << 8n) | BigInt(b[i]) + } + + return value +} diff --git a/packages/anoncreds/src/utils/credential.ts b/packages/anoncreds/src/utils/credential.ts index e015ec26aa..886621bd5a 100644 --- a/packages/anoncreds/src/utils/credential.ts +++ b/packages/anoncreds/src/utils/credential.ts @@ -1,9 +1,10 @@ import type { AnonCredsSchema, AnonCredsCredentialValues } from '../models' import type { CredentialPreviewAttributeOptions, LinkedAttachment } from '@credo-ts/didcomm' -import { Buffer, CredoError, Hasher, TypedArrayEncoder } from '@credo-ts/core' +import { CredoError, Hasher, TypedArrayEncoder } from '@credo-ts/core' import { encodeAttachment } from '@credo-ts/didcomm' -import bigInt from 'big-integer' + +import { bytesToBigint } from './bytesToBigint' export type AnonCredsClaimRecord = Record @@ -66,9 +67,8 @@ export function encodeCredentialValue(value: unknown) { const buffer = TypedArrayEncoder.fromString(String(value)) const hash = Hasher.hash(buffer, 'sha-256') - const hex = Buffer.from(hash).toString('hex') - return bigInt(hex, 16).toString() + return bytesToBigint(hash).toString() } export const mapAttributeRawValuesToAnonCredsCredentialValues = ( diff --git a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts index fa397be680..c943f46132 100644 --- a/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts +++ b/packages/anoncreds/tests/InMemoryAnonCredsRegistry.ts @@ -20,13 +20,13 @@ import type { import type { AgentContext } from '@credo-ts/core' import { Hasher, utils } from '@credo-ts/core' -import BigNumber from 'bn.js' import { getDidIndyCredentialDefinitionId, getDidIndyRevocationRegistryDefinitionId, getDidIndySchemaId, } from '../../indy-vdr/src/anoncreds/utils/identifiers' +import { bytesToBigint } from '../src/utils/bytesToBigint' import { getUnQualifiedDidIndyDid, getUnqualifiedRevocationRegistryDefinitionId, @@ -377,7 +377,6 @@ export class InMemoryAnonCredsRegistry implements AnonCredsRegistry { * Does this by hashing the schema id, transforming the hash to a number and taking the first 6 digits. */ function getSeqNoFromSchemaId(schemaId: string) { - const seqNo = Number(new BigNumber(Hasher.hash(schemaId, 'sha-256')).toString().slice(0, 5)) - - return seqNo + const hash = Hasher.hash(schemaId, 'sha-256') + return bytesToBigint(hash).toString().slice(0, 5) } diff --git a/packages/askar/package.json b/packages/askar/package.json index 48730d5638..3e47b8dbdc 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -27,17 +27,15 @@ }, "dependencies": { "@credo-ts/core": "workspace:*", - "bn.js": "^5.2.1", "class-transformer": "0.5.1", "class-validator": "0.14.1", "rxjs": "^7.8.0", "tsyringe": "^4.8.0" }, "devDependencies": { - "@animo-id/expo-secure-environment": "^0.1.0-alpha.11", - "@hyperledger/aries-askar-nodejs": "^0.2.3", - "@hyperledger/aries-askar-shared": "^0.2.3", - "@types/bn.js": "^5.1.0", + "@animo-id/expo-secure-environment": "^0.1.0-alpha.12", + "@openwallet-foundation/askar-nodejs": "^0.3.1", + "@openwallet-foundation/askar-shared": "^0.3.1", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", @@ -45,7 +43,7 @@ "typescript": "~5.5.2" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.3", + "@openwallet-foundation/askar-shared": "^0.3.1", "@animo-id/expo-secure-environment": "^0.1.0-alpha.11" }, "peerDependenciesMeta": { diff --git a/packages/askar/src/AskarModule.ts b/packages/askar/src/AskarModule.ts index a1e1762f40..0c0dec6f22 100644 --- a/packages/askar/src/AskarModule.ts +++ b/packages/askar/src/AskarModule.ts @@ -2,7 +2,7 @@ import type { AskarModuleConfigOptions } from './AskarModuleConfig' import type { AgentContext, DependencyManager, Module } from '@credo-ts/core' import { CredoError, InjectionSymbols } from '@credo-ts/core' -import { Store } from '@hyperledger/aries-askar-shared' +import { Store } from '@openwallet-foundation/askar-shared' import { AskarMultiWalletDatabaseScheme, AskarModuleConfig } from './AskarModuleConfig' import { AskarStorageService } from './storage' diff --git a/packages/askar/src/AskarModuleConfig.ts b/packages/askar/src/AskarModuleConfig.ts index 91ec72ed5b..500104ea6b 100644 --- a/packages/askar/src/AskarModuleConfig.ts +++ b/packages/askar/src/AskarModuleConfig.ts @@ -1,4 +1,4 @@ -import type { AriesAskar } from '@hyperledger/aries-askar-shared' +import type { Askar } from '@openwallet-foundation/askar-shared' export enum AskarMultiWalletDatabaseScheme { /** @@ -18,14 +18,14 @@ export interface AskarModuleConfigOptions { * ## Node.JS * * ```ts - * import { ariesAskar } from '@hyperledger/aries-askar-nodejs' + * import { askar } from '@openwallet-foundation/askar-nodejs' * * const agent = new Agent({ * config: {}, * dependencies: agentDependencies, * modules: { - * ariesAskar: new AskarModule({ - * ariesAskar, + * askar: new AskarModule({ + * askar, * }) * } * }) @@ -34,20 +34,20 @@ export interface AskarModuleConfigOptions { * ## React Native * * ```ts - * import { ariesAskar } from '@hyperledger/aries-askar-react-native' + * import { askar } from '@openwallet-foundation/askar-react-native' * * const agent = new Agent({ * config: {}, * dependencies: agentDependencies, * modules: { - * ariesAskar: new AskarModule({ - * ariesAskar, + * askar: new AskarModule({ + * askar, * }) * } * }) * ``` */ - ariesAskar: AriesAskar + askar: Askar /** * Determine the strategy for storing wallets if multiple wallets are used in a single agent. @@ -70,9 +70,9 @@ export class AskarModuleConfig { this.options = options } - /** See {@link AskarModuleConfigOptions.ariesAskar} */ - public get ariesAskar() { - return this.options.ariesAskar + /** See {@link AskarModuleConfigOptions.askar} */ + public get askar() { + return this.options.askar } /** See {@link AskarModuleConfigOptions.multiWalletDatabaseScheme} */ diff --git a/packages/askar/src/secureEnvironment/secureEnvironment.ts b/packages/askar/src/secureEnvironment/secureEnvironment.ts index 5c94febd7a..42bcfa7db3 100644 --- a/packages/askar/src/secureEnvironment/secureEnvironment.ts +++ b/packages/askar/src/secureEnvironment/secureEnvironment.ts @@ -1,7 +1,7 @@ export function importSecureEnvironment(): { sign: (id: string, message: Uint8Array) => Promise - getPublicBytesForKeyId: (id: string) => Uint8Array - generateKeypair: (id: string) => void + getPublicBytesForKeyId: (id: string) => Uint8Array | Promise + generateKeypair: (id: string) => void | Promise } { throw new Error( '@animo-id/expo-secure-environment cannot be imported in Node.js. Currently, there is no hardware key support for node.js' diff --git a/packages/askar/src/storage/AskarStorageService.ts b/packages/askar/src/storage/AskarStorageService.ts index a8ea386f60..82d03ce430 100644 --- a/packages/askar/src/storage/AskarStorageService.ts +++ b/packages/askar/src/storage/AskarStorageService.ts @@ -8,7 +8,7 @@ import type { } from '@credo-ts/core' import { RecordDuplicateError, WalletError, RecordNotFoundError, injectable, JsonTransformer } from '@credo-ts/core' -import { Scan } from '@hyperledger/aries-askar-shared' +import { Scan } from '@openwallet-foundation/askar-shared' import { AskarErrorCode, isAskarError } from '../utils/askarError' import { assertAskarWallet } from '../utils/assertAskarWallet' diff --git a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts index a16777a853..0ee0b67e2b 100644 --- a/packages/askar/src/storage/__tests__/AskarStorageService.test.ts +++ b/packages/askar/src/storage/__tests__/AskarStorageService.test.ts @@ -1,7 +1,7 @@ import type { AgentContext, TagsBase } from '@credo-ts/core' import { TypedArrayEncoder, SigningProviderRegistry, RecordDuplicateError, RecordNotFoundError } from '@credo-ts/core' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import { TestRecord } from '../../../../core/src/storage/__tests__/TestRecord' import { agentDependencies, getAgentConfig, getAgentContext } from '../../../../core/tests/helpers' @@ -60,7 +60,7 @@ describe('AskarStorageService', () => { }) const retrieveRecord = await wallet.withSession((session) => - ariesAskar.sessionFetch({ + askar.sessionFetch({ category: record.type, name: record.id, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -85,7 +85,7 @@ describe('AskarStorageService', () => { it('should correctly transform tag values from string after retrieving', async () => { await wallet.withSession( async (session) => - await ariesAskar.sessionUpdate({ + await askar.sessionUpdate({ category: TestRecord.type, name: 'some-id', // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/askar/src/storage/utils.ts b/packages/askar/src/storage/utils.ts index 0a9cd001a0..484fcb007d 100644 --- a/packages/askar/src/storage/utils.ts +++ b/packages/askar/src/storage/utils.ts @@ -1,5 +1,5 @@ import type { BaseRecord, BaseRecordConstructor, Query, TagsBase } from '@credo-ts/core' -import type { EntryObject } from '@hyperledger/aries-askar-shared' +import type { EntryObject } from '@openwallet-foundation/askar-shared' import { JsonTransformer } from '@credo-ts/core' diff --git a/packages/askar/src/utils/askarError.ts b/packages/askar/src/utils/askarError.ts index 632733413a..f096908784 100644 --- a/packages/askar/src/utils/askarError.ts +++ b/packages/askar/src/utils/askarError.ts @@ -1,4 +1,4 @@ -import { AriesAskarError } from '@hyperledger/aries-askar-shared' +import { AskarError } from '@openwallet-foundation/askar-shared' export enum AskarErrorCode { Success = 0, @@ -13,5 +13,5 @@ export enum AskarErrorCode { Custom = 100, } -export const isAskarError = (error: Error, askarErrorCode?: AskarErrorCode): error is AriesAskarError => - error instanceof AriesAskarError && (askarErrorCode === undefined || error.code === askarErrorCode) +export const isAskarError = (error: Error, askarErrorCode?: AskarErrorCode): error is AskarError => + error instanceof AskarError && (askarErrorCode === undefined || error.code === askarErrorCode) diff --git a/packages/askar/src/utils/askarKeyBackend.ts b/packages/askar/src/utils/askarKeyBackend.ts index 01a421396c..be36aec06a 100644 --- a/packages/askar/src/utils/askarKeyBackend.ts +++ b/packages/askar/src/utils/askarKeyBackend.ts @@ -1,5 +1,5 @@ import { KeyBackend as CredoKeyBackend } from '@credo-ts/core' -import { KeyBackend as AskarKeyBackend } from '@hyperledger/aries-askar-shared' +import { KeyBackend as AskarKeyBackend } from '@openwallet-foundation/askar-shared' export const convertToAskarKeyBackend = (credoKeyBackend: CredoKeyBackend) => { switch (credoKeyBackend) { diff --git a/packages/askar/src/utils/askarKeyTypes.ts b/packages/askar/src/utils/askarKeyTypes.ts index d5005d709b..5155d942ce 100644 --- a/packages/askar/src/utils/askarKeyTypes.ts +++ b/packages/askar/src/utils/askarKeyTypes.ts @@ -1,5 +1,5 @@ import { KeyType } from '@credo-ts/core' -import { KeyAlgs } from '@hyperledger/aries-askar-shared' +import { KeyAlgorithm } from '@openwallet-foundation/askar-shared' export enum AskarKeyTypePurpose { KeyManagement = 'KeyManagement', @@ -9,31 +9,31 @@ export enum AskarKeyTypePurpose { const keyTypeToAskarAlg = { [KeyType.Ed25519]: { - keyAlg: KeyAlgs.Ed25519, + keyAlg: KeyAlgorithm.Ed25519, purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing], }, [KeyType.X25519]: { - keyAlg: KeyAlgs.X25519, + keyAlg: KeyAlgorithm.X25519, purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing], }, [KeyType.Bls12381g1]: { - keyAlg: KeyAlgs.Bls12381G1, + keyAlg: KeyAlgorithm.Bls12381G1, purposes: [AskarKeyTypePurpose.KeyManagement], }, [KeyType.Bls12381g2]: { - keyAlg: KeyAlgs.Bls12381G2, + keyAlg: KeyAlgorithm.Bls12381G2, purposes: [AskarKeyTypePurpose.KeyManagement], }, [KeyType.Bls12381g1g2]: { - keyAlg: KeyAlgs.Bls12381G1, + keyAlg: KeyAlgorithm.Bls12381G1, purposes: [AskarKeyTypePurpose.KeyManagement], }, [KeyType.P256]: { - keyAlg: KeyAlgs.EcSecp256r1, + keyAlg: KeyAlgorithm.EcSecp256r1, purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing, AskarKeyTypePurpose.Encryption], }, [KeyType.K256]: { - keyAlg: KeyAlgs.EcSecp256k1, + keyAlg: KeyAlgorithm.EcSecp256k1, purposes: [AskarKeyTypePurpose.KeyManagement, AskarKeyTypePurpose.Signing], }, } diff --git a/packages/askar/src/utils/askarWalletConfig.ts b/packages/askar/src/utils/askarWalletConfig.ts index 1819222e2a..65e7f76721 100644 --- a/packages/askar/src/utils/askarWalletConfig.ts +++ b/packages/askar/src/utils/askarWalletConfig.ts @@ -1,7 +1,7 @@ import type { WalletConfig } from '@credo-ts/core' import { KeyDerivationMethod, WalletError } from '@credo-ts/core' -import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' +import { KdfMethod, StoreKeyMethod } from '@openwallet-foundation/askar-shared' import { isAskarWalletPostgresStorageConfig, diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 64755bb951..5794b65a93 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -13,7 +13,7 @@ import type { SigningProviderRegistry, WalletDirectEncryptCompactJwtEcdhEsOptions, } from '@credo-ts/core' -import type { Session } from '@hyperledger/aries-askar-shared' +import type { Session } from '@openwallet-foundation/askar-shared' import { WalletKeyExistsError, @@ -33,12 +33,11 @@ import { CryptoBox, Store, Key as AskarKey, - keyAlgFromString, + keyAlgorithmFromString, EcdhEs, - KeyAlgs, + KeyAlgorithm, Jwk, -} from '@hyperledger/aries-askar-shared' -import BigNumber from 'bn.js' +} from '@openwallet-foundation/askar-shared' import { importSecureEnvironment } from '../secureEnvironment' import { @@ -166,7 +165,7 @@ export abstract class AskarBaseWallet implements Wallet { isKeyTypeSupportedByAskarForPurpose(keyType, AskarKeyTypePurpose.KeyManagement) && keyBackend === KeyBackend.Software ) { - const algorithm = keyAlgFromString(keyType) + const algorithm = keyAlgorithmFromString(keyType) // Create key let key: AskarKey | undefined @@ -181,15 +180,17 @@ export abstract class AskarBaseWallet implements Wallet { // This will be fixed once we use the new 'using' syntax key = _key - const keyPublicBytes = key.publicBytes + const keyInstance = new Key(key.publicBytes, keyType) // Store key await this.withSession((session) => - session.insertKey({ key: _key, name: keyId ?? TypedArrayEncoder.toBase58(keyPublicBytes) }) + // NOTE: askar by default uses the compressed variant of EC keys. To not break existing wallets we keep using + // the compressed variant of the public key as the key identifier + session.insertKey({ key: _key, name: keyId ?? TypedArrayEncoder.toBase58(keyInstance.compressedPublicKey) }) ) key.handle.free() - return Key.fromPublicKey(keyPublicBytes, keyType) + return keyInstance } catch (error) { key?.handle.free() // Handle case where key already exists @@ -206,16 +207,17 @@ export abstract class AskarBaseWallet implements Wallet { // Generate a hardware-backed P-256 keypair await secureEnvironment.generateKeypair(kid) - const publicKeyBytes = await secureEnvironment.getPublicBytesForKeyId(kid) - const publicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyBytes) + const compressedPublicKeyBytes = await secureEnvironment.getPublicBytesForKeyId(kid) + + const publicKeyInstance = new Key(compressedPublicKeyBytes, keyType) await this.storeSecureEnvironmentKeyById({ keyType, - publicKeyBase58, + publicKeyBase58: TypedArrayEncoder.toBase58(publicKeyInstance.compressedPublicKey), keyId: kid, }) - return new Key(publicKeyBytes, keyType) + return publicKeyInstance } else { // Check if there is a signing key provider for the specified key type. if (this.signingKeyProviderRegistry.hasProviderForKeyType(keyType)) { @@ -253,7 +255,10 @@ export abstract class AskarBaseWallet implements Wallet { try { if (isKeyTypeSupportedByAskarForPurpose(key.keyType, AskarKeyTypePurpose.KeyManagement)) { askarKey = await this.withSession( - async (session) => (await session.fetchKey({ name: key.publicKeyBase58 }))?.key + async (session) => + ( + await session.fetchKey({ name: TypedArrayEncoder.toBase58(key.compressedPublicKey) }) + )?.key ) } @@ -264,29 +269,29 @@ export abstract class AskarBaseWallet implements Wallet { // where a key wasn't supported at first by the wallet, but now is if (!askarKey) { // TODO: we should probably make retrieveKeyPair + insertKey + deleteKeyPair a transaction - keyPair = await this.retrieveKeyPair(key.publicKeyBase58) + keyPair = await this.retrieveKeyPair(TypedArrayEncoder.toBase58(key.compressedPublicKey)) // If we have the key stored in a custom record, but it is now supported by Askar, // we 'import' the key into askar storage and remove the custom key record if (keyPair && isKeyTypeSupportedByAskarForPurpose(keyPair.keyType, AskarKeyTypePurpose.KeyManagement)) { const _askarKey = AskarKey.fromSecretBytes({ secretKey: TypedArrayEncoder.fromBase58(keyPair.privateKeyBase58), - algorithm: keyAlgFromString(keyPair.keyType), + algorithm: keyAlgorithmFromString(keyPair.keyType), }) askarKey = _askarKey await this.withSession((session) => session.insertKey({ - name: key.publicKeyBase58, + name: TypedArrayEncoder.toBase58(key.compressedPublicKey), key: _askarKey, }) ) // Now we can remove it from the custom record as we have imported it into Askar - await this.deleteKeyPair(key.publicKeyBase58) + await this.deleteKeyPair(TypedArrayEncoder.toBase58(key.compressedPublicKey)) keyPair = undefined } else { - const { keyId } = await this.getSecureEnvironmentKey(key.publicKeyBase58) + const { keyId } = await this.getSecureEnvironmentKey(TypedArrayEncoder.toBase58(key.compressedPublicKey)) if (Array.isArray(data[0])) { throw new WalletError('Multi signature is not supported for the Secure Environment') @@ -311,7 +316,7 @@ export abstract class AskarBaseWallet implements Wallet { (keyPair ? AskarKey.fromSecretBytes({ secretKey: TypedArrayEncoder.fromBase58(keyPair.privateKeyBase58), - algorithm: keyAlgFromString(keyPair.keyType), + algorithm: keyAlgorithmFromString(keyPair.keyType), }) : undefined) @@ -349,7 +354,12 @@ export abstract class AskarBaseWallet implements Wallet { if (!isError(error)) { throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) } - throw new WalletError(`Error signing data with verkey ${key.publicKeyBase58}. ${error.message}`, { cause: error }) + throw new WalletError( + `Error signing data with key associated with publicKeyBase58 ${key.publicKeyBase58}. ${error.message}`, + { + cause: error, + } + ) } finally { askarKey?.handle.free() } @@ -376,7 +386,7 @@ export abstract class AskarBaseWallet implements Wallet { } askarKey = AskarKey.fromPublicBytes({ - algorithm: keyAlgFromString(key.keyType), + algorithm: keyAlgorithmFromString(key.keyType), publicKey: key.publicKey, }) const verified = askarKey.verifySignature({ message: data as Buffer, signature }) @@ -483,21 +493,22 @@ export abstract class AskarBaseWallet implements Wallet { data, header, }: WalletDirectEncryptCompactJwtEcdhEsOptions) { - if (encryptionAlgorithm !== 'A256GCM') { - throw new WalletError(`Encryption algorithm ${encryptionAlgorithm} is not supported. Only A256GCM is supported`) + if (encryptionAlgorithm !== 'A256GCM' && encryptionAlgorithm !== 'A128CBC-HS256') { + throw new WalletError( + `Encryption algorithm ${encryptionAlgorithm} is not supported. Only A256GCM and A128CBC-HS256 are supported` + ) } - // Only one supported for now - const encAlg = KeyAlgs.AesA256Gcm + const encAlg = encryptionAlgorithm === 'A256GCM' ? KeyAlgorithm.AesA256Gcm : KeyAlgorithm.AesA128CbcHs256 // Create ephemeral key - const ephemeralKey = AskarKey.generate(keyAlgFromString(recipientKey.keyType)) + const ephemeralKey = AskarKey.generate(keyAlgorithmFromString(recipientKey.keyType)) const _header = { ...header, apv, apu, - enc: 'A256GCM', + enc: encryptionAlgorithm, alg: 'ECDH-ES', epk: ephemeralKey.jwkPublic, } @@ -511,11 +522,11 @@ export abstract class AskarBaseWallet implements Wallet { }) const { ciphertext, tag, nonce } = ecdh.encryptDirect({ - encAlg, + encryptionAlgorithm: encAlg, ephemeralKey, message: Uint8Array.from(data), recipientKey: AskarKey.fromPublicBytes({ - algorithm: keyAlgFromString(recipientKey.keyType), + algorithm: keyAlgorithmFromString(recipientKey.keyType), publicKey: recipientKey.publicKey, }), // NOTE: aad is bytes of base64url encoded string. It SHOULD NOT be decoded as base64 @@ -548,8 +559,8 @@ export abstract class AskarBaseWallet implements Wallet { if (header.alg !== 'ECDH-ES') { throw new WalletError('Only ECDH-ES alg value is supported') } - if (header.enc !== 'A256GCM') { - throw new WalletError('Only A256GCM enc value is supported') + if (header.enc !== 'A256GCM' && header.enc !== 'A128CBC-HS256') { + throw new WalletError('Only A256GCM and A128CBC-HS256 enc values are supported') } if (!header.epk || typeof header.epk !== 'object') { throw new WalletError('header epk value must contain a JWK') @@ -559,16 +570,17 @@ export abstract class AskarBaseWallet implements Wallet { let askarKey: AskarKey | null | undefined if (isKeyTypeSupportedByAskarForPurpose(recipientKey.keyType, AskarKeyTypePurpose.KeyManagement)) { askarKey = await this.withSession( - async (session) => (await session.fetchKey({ name: recipientKey.publicKeyBase58 }))?.key + async (session) => + ( + await session.fetchKey({ name: TypedArrayEncoder.toBase58(recipientKey.compressedPublicKey) }) + )?.key ) } if (!askarKey) { throw new WalletError('Key entry not found') } - // Only one supported for now - const encAlg = KeyAlgs.AesA256Gcm - + const encAlg = header.enc === 'A256GCM' ? KeyAlgorithm.AesA256Gcm : KeyAlgorithm.AesA128CbcHs256 const ecdh = new EcdhEs({ algId: Uint8Array.from(Buffer.from(header.enc)), apu: header.apu ? Uint8Array.from(TypedArrayEncoder.fromBase64(header.apu)) : Uint8Array.from([]), @@ -578,7 +590,7 @@ export abstract class AskarBaseWallet implements Wallet { const plaintext = ecdh.decryptDirect({ nonce: TypedArrayEncoder.fromBase64(encodedIv), ciphertext: TypedArrayEncoder.fromBase64(encodedCiphertext), - encAlg, + encryptionAlgorithm: encAlg, ephemeralKey: Jwk.fromJson(header.epk), recipientKey: askarKey, tag: TypedArrayEncoder.fromBase64(encodedTag), @@ -593,7 +605,7 @@ export abstract class AskarBaseWallet implements Wallet { try { // generate an 80-bit nonce suitable for AnonCreds proofs const nonce = CryptoBox.randomNonce().slice(0, 10) - return new BigNumber(nonce).toString() + return nonce.reduce((acc, byte) => (acc << 8n) | BigInt(byte), 0n).toString() } catch (error) { if (!isError(error)) { throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error }) @@ -671,7 +683,9 @@ export abstract class AskarBaseWallet implements Wallet { await this.withSession((session) => session.insert({ category: 'KeyPairRecord', - name: `key-${keyPair.publicKeyBase58}`, + name: `key-${TypedArrayEncoder.toBase58( + Key.fromPublicKeyBase58(keyPair.publicKeyBase58, keyPair.keyType).compressedPublicKey + )}`, value: JSON.stringify(keyPair), tags: { keyType: keyPair.keyType, diff --git a/packages/askar/src/wallet/AskarProfileWallet.ts b/packages/askar/src/wallet/AskarProfileWallet.ts index c3586404de..9edefc99a2 100644 --- a/packages/askar/src/wallet/AskarProfileWallet.ts +++ b/packages/askar/src/wallet/AskarProfileWallet.ts @@ -9,7 +9,7 @@ import { SigningProviderRegistry, WalletError, } from '@credo-ts/core' -import { Store } from '@hyperledger/aries-askar-shared' +import { Store } from '@openwallet-foundation/askar-shared' import { inject, injectable } from 'tsyringe' import { AskarErrorCode, isAskarError } from '../utils' @@ -151,12 +151,12 @@ export class AskarProfileWallet extends AskarBaseWallet { } public async export() { - // This PR should help with this: https://github.com/hyperledger/aries-askar/pull/159 + // This PR should help with this: https://github.com/openwallet-foundation/askar/pull/159 throw new WalletExportUnsupportedError('Exporting a profile is not supported.') } public async import() { - // This PR should help with this: https://github.com/hyperledger/aries-askar/pull/159 + // This PR should help with this: https://github.com/openwallet-foundation/askar/pull/159 throw new WalletError('Importing a profile is not supported.') } diff --git a/packages/askar/src/wallet/AskarWallet.ts b/packages/askar/src/wallet/AskarWallet.ts index 34be645d9c..1b581c0a90 100644 --- a/packages/askar/src/wallet/AskarWallet.ts +++ b/packages/askar/src/wallet/AskarWallet.ts @@ -15,7 +15,7 @@ import { WalletImportPathExistsError, WalletExportUnsupportedError, } from '@credo-ts/core' -import { Store } from '@hyperledger/aries-askar-shared' +import { Store } from '@openwallet-foundation/askar-shared' import { inject, injectable } from 'tsyringe' import { AskarErrorCode, isAskarError, keyDerivationMethodToStoreKeyMethod, uriFromWalletConfig } from '../utils' @@ -351,7 +351,7 @@ export class AskarWallet extends AskarBaseWallet { const defaultProfile = await sourceWalletStore.getDefaultProfile() if (defaultProfile !== importWalletConfig.profile) { throw new WalletError( - `Trying to import wallet with walletConfig.id ${importWalletConfig.profile}, however the wallet contains a default profile with id ${defaultProfile}. The walletConfig.id MUST match with the default profile. In the future this behavior may be changed. See https://github.com/hyperledger/aries-askar/issues/221 for more information.` + `Trying to import wallet with walletConfig.id ${importWalletConfig.profile}, however the wallet contains a default profile with id ${defaultProfile}. The walletConfig.id MUST match with the default profile. In the future this behavior may be changed. See https://github.com/openwallet-foundation/askar/issues/221 for more information.` ) } diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 106d371dfc..0ea69a2382 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -6,7 +6,7 @@ import type { SignOptions, VerifyOptions, } from '@credo-ts/core' -import type { JwkProps } from '@hyperledger/aries-askar-shared' +import type { JwkProps } from '@openwallet-foundation/askar-shared' import { WalletKeyExistsError, @@ -22,8 +22,8 @@ import { Buffer, JsonEncoder, } from '@credo-ts/core' -import { Key as AskarKey } from '@hyperledger/aries-askar-nodejs' -import { Jwk, Store } from '@hyperledger/aries-askar-shared' +import { Key as AskarKey } from '@openwallet-foundation/askar-nodejs' +import { Jwk, Store } from '@openwallet-foundation/askar-shared' import { readFileSync } from 'fs' import path from 'path' @@ -176,7 +176,7 @@ describe('AskarWallet basic operations', () => { await expect(askarWallet.verify({ key: k256Key, data: message, signature })).resolves.toStrictEqual(true) }) - test('Encrypt and decrypt using JWE ECDH-ES', async () => { + test('Encrypt and decrypt using JWE ECDH-ES A256GCM', async () => { const recipientKey = await askarWallet.createKey({ keyType: KeyType.P256, }) @@ -216,6 +216,46 @@ describe('AskarWallet basic operations', () => { expect(JsonEncoder.fromBuffer(data)).toEqual({ vp_token: ['something'] }) }) + test('Encrypt and decrypt using JWE ECDH-ES A128CBC-HS256', async () => { + const recipientKey = await askarWallet.createKey({ + keyType: KeyType.P256, + }) + + const apv = TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromString('nonce-from-auth-request')) + const apu = TypedArrayEncoder.toBase64URL(TypedArrayEncoder.fromString(await askarWallet.generateNonce())) + + const compactJwe = await askarWallet.directEncryptCompactJweEcdhEs({ + data: JsonEncoder.toBuffer({ vp_token: ['something'] }), + apu, + apv, + encryptionAlgorithm: 'A128CBC-HS256', + header: { + kid: 'some-kid', + }, + recipientKey, + }) + + const { data, header } = await askarWallet.directDecryptCompactJweEcdhEs({ + compactJwe, + recipientKey, + }) + + expect(header).toEqual({ + kid: 'some-kid', + apv, + apu, + enc: 'A128CBC-HS256', + alg: 'ECDH-ES', + epk: { + kty: 'EC', + crv: 'P-256', + x: expect.any(String), + y: expect.any(String), + }, + }) + expect(JsonEncoder.fromBuffer(data)).toEqual({ vp_token: ['something'] }) + }) + test('decrypt using JWE ECDH-ES based on test vector from OpenID Conformance test', async () => { const { compactJwe, diff --git a/packages/askar/src/wallet/didcommV1.ts b/packages/askar/src/wallet/didcommV1.ts index 6ab9a7f76d..ff8491e6ff 100644 --- a/packages/askar/src/wallet/didcommV1.ts +++ b/packages/askar/src/wallet/didcommV1.ts @@ -1,7 +1,7 @@ import type { EncryptedMessage } from '@credo-ts/core' import { WalletError, JsonEncoder, JsonTransformer, Key, KeyType, TypedArrayEncoder, Buffer } from '@credo-ts/core' -import { CryptoBox, Key as AskarKey, KeyAlgs } from '@hyperledger/aries-askar-shared' +import { CryptoBox, Key as AskarKey, KeyAlgorithm } from '@openwallet-foundation/askar-shared' import { JweEnvelope, JweRecipient } from './JweEnvelope' @@ -10,9 +10,9 @@ export function didcommV1Pack(payload: Record, recipientKeys: s let senderExchangeKey: AskarKey | undefined try { - cek = AskarKey.generate(KeyAlgs.Chacha20C20P) + cek = AskarKey.generate(KeyAlgorithm.Chacha20C20P) - senderExchangeKey = senderKey ? senderKey.convertkey({ algorithm: KeyAlgs.X25519 }) : undefined + senderExchangeKey = senderKey ? senderKey.convertkey({ algorithm: KeyAlgorithm.X25519 }) : undefined const recipients: JweRecipient[] = [] @@ -21,8 +21,8 @@ export function didcommV1Pack(payload: Record, recipientKeys: s try { targetExchangeKey = AskarKey.fromPublicBytes({ publicKey: Key.fromPublicKeyBase58(recipientKey, KeyType.Ed25519).publicKey, - algorithm: KeyAlgs.Ed25519, - }).convertkey({ algorithm: KeyAlgs.X25519 }) + algorithm: KeyAlgorithm.Ed25519, + }).convertkey({ algorithm: KeyAlgorithm.X25519 }) if (senderKey && senderExchangeKey) { const encryptedSender = CryptoBox.seal({ @@ -125,7 +125,7 @@ export function didcommV1Unpack(messagePackage: EncryptedMessage, recipientKey: let recip_x: AskarKey | undefined try { - recip_x = recipientKey.convertkey({ algorithm: KeyAlgs.X25519 }) + recip_x = recipientKey.convertkey({ algorithm: KeyAlgorithm.X25519 }) if (sender && iv) { senderKey = TypedArrayEncoder.toUtf8String( @@ -135,9 +135,9 @@ export function didcommV1Unpack(messagePackage: EncryptedMessage, recipientKey: }) ) sender_x = AskarKey.fromPublicBytes({ - algorithm: KeyAlgs.Ed25519, + algorithm: KeyAlgorithm.Ed25519, publicKey: TypedArrayEncoder.fromBase58(senderKey), - }).convertkey({ algorithm: KeyAlgs.X25519 }) + }).convertkey({ algorithm: KeyAlgorithm.X25519 }) payloadKey = CryptoBox.open({ recipientKey: recip_x, @@ -159,7 +159,7 @@ export function didcommV1Unpack(messagePackage: EncryptedMessage, recipientKey: let cek: AskarKey | undefined try { - cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgs.Chacha20C20P, secretKey: payloadKey }) + cek = AskarKey.fromSecretBytes({ algorithm: KeyAlgorithm.Chacha20C20P, secretKey: payloadKey }) const message = cek.aeadDecrypt({ ciphertext: TypedArrayEncoder.fromBase64(messagePackage.ciphertext), nonce: TypedArrayEncoder.fromBase64(messagePackage.iv), diff --git a/packages/askar/tests/askar-sqlite.test.ts b/packages/askar/tests/askar-sqlite.test.ts index f1630f0725..a5d6fb45ff 100644 --- a/packages/askar/tests/askar-sqlite.test.ts +++ b/packages/askar/tests/askar-sqlite.test.ts @@ -8,7 +8,7 @@ import { WalletInvalidKeyError, WalletNotFoundError, } from '@credo-ts/core' -import { Store } from '@hyperledger/aries-askar-shared' +import { Store } from '@openwallet-foundation/askar-shared' import { tmpdir } from 'os' import path from 'path' @@ -162,7 +162,7 @@ describe('Askar SQLite agents', () => { await expect( bobAgent.wallet.import({ id: backupWalletName, key: backupWalletName }, { path: backupPath, key: backupKey }) ).rejects.toThrow( - `Error importing wallet '${backupWalletName}': Trying to import wallet with walletConfig.id ${backupWalletName}, however the wallet contains a default profile with id ${bobAgent.config.walletConfig.id}. The walletConfig.id MUST match with the default profile. In the future this behavior may be changed. See https://github.com/hyperledger/aries-askar/issues/221 for more information.` + `Error importing wallet '${backupWalletName}': Trying to import wallet with walletConfig.id ${backupWalletName}, however the wallet contains a default profile with id ${bobAgent.config.walletConfig.id}. The walletConfig.id MUST match with the default profile. In the future this behavior may be changed. See https://github.com/openwallet-foundation/askar/issues/221 for more information.` ) }) diff --git a/packages/askar/tests/helpers.ts b/packages/askar/tests/helpers.ts index 845d11067e..aee8067b58 100644 --- a/packages/askar/tests/helpers.ts +++ b/packages/askar/tests/helpers.ts @@ -3,8 +3,8 @@ import type { AskarWalletPostgresStorageConfig } from '../src/wallet' import type { Agent, InitConfig } from '@credo-ts/core' import { LogLevel, utils } from '@credo-ts/core' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' -import { registerAriesAskar } from '@hyperledger/aries-askar-shared' +import { askar } from '@openwallet-foundation/askar-nodejs' +import { registerAskar } from '@openwallet-foundation/askar-shared' import path from 'path' import { waitForBasicMessage } from '../../core/tests/helpers' @@ -16,10 +16,10 @@ import { AskarModule } from '../src/AskarModule' import { AskarModuleConfig } from '../src/AskarModuleConfig' import { AskarWallet } from '../src/wallet' -export const askarModuleConfig = new AskarModuleConfig({ ariesAskar }) -registerAriesAskar({ askar: askarModuleConfig.ariesAskar }) +export const askarModuleConfig = new AskarModuleConfig({ askar }) +registerAskar({ askar: askarModuleConfig.askar }) export const askarModule = new AskarModule(askarModuleConfig) -export { ariesAskar } +export { askar } // When using the AskarWallet directly, the native dependency won't be loaded by default. // So in tests depending on Askar, we import this wallet so we're sure the native dependency is loaded. diff --git a/packages/askar/tests/setup.ts b/packages/askar/tests/setup.ts index 97eeec3b92..6440af8bf7 100644 --- a/packages/askar/tests/setup.ts +++ b/packages/askar/tests/setup.ts @@ -1,4 +1,4 @@ import 'reflect-metadata' -import '@hyperledger/aries-askar-nodejs' +import '@openwallet-foundation/askar-nodejs' jest.setTimeout(180000) diff --git a/packages/bbs-signatures/tests/bbs-signatures.test.ts b/packages/bbs-signatures/tests/bbs-signatures.test.ts index 0a6c098624..f3055816fd 100644 --- a/packages/bbs-signatures/tests/bbs-signatures.test.ts +++ b/packages/bbs-signatures/tests/bbs-signatures.test.ts @@ -130,7 +130,7 @@ describeSkipNode18('BBS W3cCredentialService', () => { beforeAll(async () => { // FIXME: askar doesn't create the same privateKey based on the same seed as when generated used askar BBS library... - // See https://github.com/hyperledger/aries-askar/issues/219 + // See https://github.com/openwallet-foundation/askar/issues/219 const key = await wallet.createKey({ keyType: KeyType.Bls12381g2, privateKey: TypedArrayEncoder.fromBase58('2szQ7zB4tKLJPsGK3YTp9SNQ6hoWYFG5rGhmgfQM4nb7'), diff --git a/packages/bbs-signatures/tests/bbs-signing-provider.test.ts b/packages/bbs-signatures/tests/bbs-signing-provider.test.ts index 21acb9988f..a63d59b84d 100644 --- a/packages/bbs-signatures/tests/bbs-signing-provider.test.ts +++ b/packages/bbs-signatures/tests/bbs-signing-provider.test.ts @@ -52,7 +52,7 @@ describeSkipNode18('BBS Signing Provider', () => { key, }) ).rejects.toThrow( - 'Error signing data with verkey AeAihfn5UFf7y9oesemKE1oLmTwKMRv7fafTepespr3qceF4RUMggAbogkoC8n6rXgtJytq4oGy59DsVHxmNj9WGWwkiRnP3Sz2r924RLVbc2NdP4T7yEPsSFZPsWmLjgnP1vXHpj4bVXNcTmkUmF6mSXinF3HehnQVip14vRFuMzYVxMUh28ofTJzbtUqxMWZQRu. Unsupported keyType: bls12381g1g2' + 'Error signing data with key associated with publicKeyBase58 AeAihfn5UFf7y9oesemKE1oLmTwKMRv7fafTepespr3qceF4RUMggAbogkoC8n6rXgtJytq4oGy59DsVHxmNj9WGWwkiRnP3Sz2r924RLVbc2NdP4T7yEPsSFZPsWmLjgnP1vXHpj4bVXNcTmkUmF6mSXinF3HehnQVip14vRFuMzYVxMUh28ofTJzbtUqxMWZQRu. Unsupported keyType: bls12381g1g2' ) }) diff --git a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts index aae26d659d..431462a5ce 100644 --- a/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts +++ b/packages/bbs-signatures/tests/v2.ldproof.credentials.propose-offerBbs.test.ts @@ -70,7 +70,7 @@ describeSkipNode18('credentials, BBS+ signature', () => { privateKey: TypedArrayEncoder.fromString('testseed000000000000000000000001'), }) // FIXME: askar doesn't create the same privateKey based on the same seed as when generated used askar BBS library... - // See https://github.com/hyperledger/aries-askar/issues/219 + // See https://github.com/openwallet-foundation/askar/issues/219 await faberAgent.context.wallet.createKey({ keyType: KeyType.Bls12381g2, privateKey: TypedArrayEncoder.fromBase58('2szQ7zB4tKLJPsGK3YTp9SNQ6hoWYFG5rGhmgfQM4nb7'), diff --git a/packages/cheqd/package.json b/packages/cheqd/package.json index cfa83cb4a5..d1886b486e 100644 --- a/packages/cheqd/package.json +++ b/packages/cheqd/package.json @@ -26,8 +26,8 @@ "test": "jest" }, "dependencies": { - "@cheqd/sdk": "2.5.1", - "@cheqd/ts-proto": "~2.3.2", + "@cheqd/sdk": "~2.6.0", + "@cheqd/ts-proto": "~2.4.0", "@cosmjs/crypto": "~0.30.0", "@cosmjs/proto-signing": "~0.30.0", "@cosmjs/stargate": "~0.30.0", diff --git a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts index 8de99bf8ea..e8e24b8072 100644 --- a/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts +++ b/packages/cheqd/src/anoncreds/services/CheqdAnonCredsRegistry.ts @@ -19,7 +19,11 @@ import type { AgentContext } from '@credo-ts/core' import { CredoError, Hasher, JsonTransformer, TypedArrayEncoder, utils } from '@credo-ts/core' import { CheqdDidResolver, CheqdDidRegistrar } from '../../dids' -import { cheqdSdkAnonCredsRegistryIdentifierRegex, parseCheqdDid } from '../utils/identifiers' +import { + cheqdAnonCredsResourceTypes, + cheqdSdkAnonCredsRegistryIdentifierRegex, + parseCheqdDid, +} from '../utils/identifiers' import { CheqdCredentialDefinition, CheqdRevocationRegistryDefinition, @@ -87,7 +91,7 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const schemaResource = { id: utils.uuid(), name: `${schema.name}-Schema`, - resourceType: 'anonCredsSchema', + resourceType: cheqdAnonCredsResourceTypes.schema, data: { name: schema.name, version: schema.version, @@ -145,7 +149,7 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const credDefResource = { id: utils.uuid(), name: TypedArrayEncoder.toHex(credDefNameHashBuffer), - resourceType: 'anonCredsCredDef', + resourceType: cheqdAnonCredsResourceTypes.credentialDefinition, data: { type: credentialDefinition.type, tag: credentialDefinition.tag, @@ -250,9 +254,9 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const searchDid = parsedDid.path ? revocationRegistryDefinitionId - : `${revocationRegistryDefinitionId}${ - revocationRegistryDefinitionId.includes('?') ? '&' : '?' - }resourceType=anonCredsRevocRegDef` + : `${revocationRegistryDefinitionId}${revocationRegistryDefinitionId.includes('?') ? '&' : '?'}resourceType=${ + cheqdAnonCredsResourceTypes.revocationRegistryDefinition + }` const response = await cheqdDidResolver.resolveResource(agentContext, searchDid) const revocationRegistryDefinition = JsonTransformer.fromJSON( @@ -306,10 +310,13 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const cheqdDidRegistrar = agentContext.dependencyManager.resolve(CheqdDidRegistrar) + const revocDefName = `${credentialDefinitionName}-${revocationRegistryDefinition.tag}` + const revocDefNameHashedBuffer = Hasher.hash(revocDefName, 'sha-256') + const revocationRegistryDefinitionResource = { id: utils.uuid(), - name: credentialDefinitionName as string, - resourceType: 'anonCredsRevocRegDef', + name: TypedArrayEncoder.toHex(revocDefNameHashedBuffer), + resourceType: cheqdAnonCredsResourceTypes.revocationRegistryDefinition, data: { credDefId: revocationRegistryDefinition.credDefId, revocDefType: revocationRegistryDefinition.revocDefType, @@ -385,7 +392,7 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const response = await cheqdDidResolver.resolveResource( agentContext, - `${parsedDid.did}?resourceType=anonCredsStatusList&resourceVersionTime=${timestamp}&resourceName=${revocationRegistryDefinitionName}` + `${parsedDid.did}?resourceType=${cheqdAnonCredsResourceTypes.revocationStatusList}&resourceVersionTime=${timestamp}&resourceName=${revocationRegistryDefinitionName}` ) const revocationStatusList = JsonTransformer.fromJSON(response.resource, CheqdRevocationStatusList) @@ -444,7 +451,7 @@ export class CheqdAnonCredsRegistry implements AnonCredsRegistry { const revocationStatusListResource = { id: utils.uuid(), name: revocationRegistryDefinitionName as string, - resourceType: 'anonCredsStatusList', + resourceType: cheqdAnonCredsResourceTypes.revocationStatusList, data: { currentAccumulator: revocationStatusList.currentAccumulator, revRegDefId: revocationStatusList.revRegDefId, diff --git a/packages/cheqd/src/anoncreds/utils/identifiers.ts b/packages/cheqd/src/anoncreds/utils/identifiers.ts index ac4b58170c..3d33f99a70 100644 --- a/packages/cheqd/src/anoncreds/utils/identifiers.ts +++ b/packages/cheqd/src/anoncreds/utils/identifiers.ts @@ -63,3 +63,10 @@ export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null { } return null } + +export const cheqdAnonCredsResourceTypes = { + schema: 'anonCredsSchema', + credentialDefinition: 'anonCredsCredDef', + revocationRegistryDefinition: 'anonCredsRevocRegDef', + revocationStatusList: 'anonCredsStatusList', +} diff --git a/packages/core/package.json b/packages/core/package.json index 9411b47a4d..d146b3e2d2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,6 +25,9 @@ "prepublishOnly": "pnpm run build" }, "dependencies": { + "@animo-id/mdoc": "0.4.0", + "@animo-id/pex": "4.1.1-alpha.1", + "@astronautlabs/jsonpath": "^1.1.2", "@digitalcredentials/jsonld": "^6.0.0", "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", @@ -35,7 +38,6 @@ "@peculiar/asn1-schema": "^2.3.13", "@peculiar/asn1-x509": "^2.3.13", "@peculiar/x509": "^1.12.1", - "@animo-id/mdoc": "0.3.0", "@sd-jwt/core": "^0.7.2", "@sd-jwt/decode": "^0.7.2", "@sd-jwt/jwt-status-list": "^0.7.2", @@ -43,19 +45,17 @@ "@sd-jwt/sd-jwt-vc": "^0.7.2", "@sd-jwt/types": "^0.7.2", "@sd-jwt/utils": "^0.7.2", - "@animo-id/pex": "4.1.1-alpha.0", - "dcql": "^0.2.17", + "dcql": "^0.2.20", "@sphereon/pex-models": "^2.3.1", "@sphereon/ssi-types": "0.30.2-next.135", "@stablelib/ed25519": "^1.0.2", "@types/ws": "^8.5.4", - "big-integer": "^1.6.51", "borc": "^3.0.0", "buffer": "^6.0.3", "class-transformer": "0.5.1", "class-validator": "0.14.1", "did-resolver": "^4.1.0", - "@astronautlabs/jsonpath": "^1.1.2", + "ec-compression": "0.0.1-alpha.10", "lru_map": "^0.4.1", "make-error": "^1.3.6", "object-inspect": "^1.10.3", @@ -72,9 +72,9 @@ "@types/object-inspect": "^1.8.0", "@types/uuid": "^9.0.1", "@types/varint": "^6.0.0", + "nock": "^14.0.0-beta.19", "rimraf": "^4.4.0", "tslog": "^4.8.2", - "typescript": "~5.5.2", - "nock": "^14.0.0-beta.19" + "typescript": "~5.5.2" } } diff --git a/packages/core/src/crypto/JwsService.ts b/packages/core/src/crypto/JwsService.ts index 1be463e632..4f1361e4b8 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -1,5 +1,3 @@ -import type { AgentContext } from '../agent' -import type { Buffer } from '../utils' import type { Jws, JwsDetachedFormat, @@ -10,11 +8,12 @@ import type { import type { Key } from './Key' import type { Jwk } from './jose/jwk' import type { JwkJson } from './jose/jwk/Jwk' +import type { AgentContext } from '../agent' import { CredoError } from '../error' import { EncodedX509Certificate, X509ModuleConfig } from '../modules/x509' import { injectable } from '../plugins' -import { isJsonObject, JsonEncoder, TypedArrayEncoder } from '../utils' +import { Buffer, isJsonObject, JsonEncoder, TypedArrayEncoder } from '../utils' import { WalletError } from '../wallet/error' import { X509Service } from './../modules/x509/X509Service' @@ -33,14 +32,18 @@ export class JwsService { const certificate = X509Service.getLeafCertificate(agentContext, { certificateChain: x5c }) if ( certificate.publicKey.keyType !== options.key.keyType || - !certificate.publicKey.publicKey.equals(options.key.publicKey) + !Buffer.from(certificate.publicKey.publicKey).equals(Buffer.from(options.key.publicKey)) ) { throw new CredoError(`Protected header x5c does not match key for signing.`) } } // Make sure the options.key and jwk from protectedHeader are the same. - if (jwk && (jwk.key.keyType !== options.key.keyType || !jwk.key.publicKey.equals(options.key.publicKey))) { + if ( + jwk && + (jwk.key.keyType !== options.key.keyType || + !Buffer.from(jwk.key.publicKey).equals(Buffer.from(options.key.publicKey))) + ) { throw new CredoError(`Protected header JWK does not match key for signing.`) } diff --git a/packages/core/src/crypto/Key.ts b/packages/core/src/crypto/Key.ts index ec44eead24..1661c72138 100644 --- a/packages/core/src/crypto/Key.ts +++ b/packages/core/src/crypto/Key.ts @@ -1,25 +1,31 @@ import type { KeyType } from './KeyType' -import { Buffer, MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' +import { compressPublicKeyIfPossible, decompressPublicKeyIfPossible } from 'ec-compression' + +import { MultiBaseEncoder, TypedArrayEncoder, VarintEncoder } from '../utils' import { isEncryptionSupportedForKeyType, isSigningSupportedForKeyType } from './keyUtils' import { getKeyTypeByMultiCodecPrefix, getMultiCodecPrefixByKeyType } from './multiCodecKey' export class Key { - public readonly publicKey: Buffer + public readonly publicKey: Uint8Array public readonly keyType: KeyType public constructor(publicKey: Uint8Array, keyType: KeyType) { - this.publicKey = Buffer.from(publicKey) + this.publicKey = decompressPublicKeyIfPossible(publicKey, keyType) this.keyType = keyType } + public get compressedPublicKey() { + return compressPublicKeyIfPossible(this.publicKey, this.keyType) + } + public static fromPublicKey(publicKey: Uint8Array, keyType: KeyType) { - return new Key(Buffer.from(publicKey), keyType) + return new Key(publicKey, keyType) } public static fromPublicKeyBase58(publicKey: string, keyType: KeyType) { - const publicKeyBytes = TypedArrayEncoder.fromBase58(publicKey) + const publicKeyBytes = Uint8Array.from(TypedArrayEncoder.fromBase58(publicKey)) return Key.fromPublicKey(publicKeyBytes, keyType) } @@ -28,7 +34,7 @@ export class Key { const { data } = MultiBaseEncoder.decode(fingerprint) const [code, byteLength] = VarintEncoder.decode(data) - const publicKey = Buffer.from(data.slice(byteLength)) + const publicKey = data.slice(byteLength) const keyType = getKeyTypeByMultiCodecPrefix(code) return new Key(publicKey, keyType) @@ -41,7 +47,8 @@ export class Key { const prefixBytes = VarintEncoder.encode(multiCodecPrefix) // Combine prefix with public key - return Buffer.concat([prefixBytes, this.publicKey]) + // Multicodec requires compressable keys to be compressed + return new Uint8Array([...prefixBytes, ...this.compressedPublicKey]) } public get fingerprint() { diff --git a/packages/core/src/crypto/__tests__/JwsService.test.ts b/packages/core/src/crypto/__tests__/JwsService.test.ts index d6654aaa0d..036837f19d 100644 --- a/packages/core/src/crypto/__tests__/JwsService.test.ts +++ b/packages/core/src/crypto/__tests__/JwsService.test.ts @@ -147,7 +147,7 @@ describe('JwsService', () => { jwsService.verifyJws(agentContext, { jws: { signatures: [], payload: '' }, }) - ).rejects.toThrowError('Unable to verify JWS, no signatures present in JWS.') + ).rejects.toThrow('Unable to verify JWS, no signatures present in JWS.') }) }) }) diff --git a/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts b/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts index 30966cea64..56d7cf5ceb 100644 --- a/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/Ed25519Jwk.ts @@ -1,5 +1,4 @@ import type { JwkJson } from './Jwk' -import type { Buffer } from '../../../utils' import type { JwaEncryptionAlgorithm } from '../jwa/alg' import { TypedArrayEncoder } from '../../../utils' @@ -15,12 +14,16 @@ export class Ed25519Jwk extends Jwk { public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.EdDSA] public static readonly keyType = KeyType.Ed25519 - public readonly x: string + private readonly _x: Uint8Array - public constructor({ x }: { x: string }) { + public constructor({ x }: { x: string | Uint8Array }) { super() - this.x = x + this._x = typeof x === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(x)) : x + } + + public get x() { + return TypedArrayEncoder.toBase64URL(this._x) } public get kty() { @@ -32,7 +35,7 @@ export class Ed25519Jwk extends Jwk { } public get publicKey() { - return TypedArrayEncoder.fromBase64(this.x) + return this._x } public get keyType() { @@ -65,10 +68,8 @@ export class Ed25519Jwk extends Jwk { }) } - public static fromPublicKey(publicKey: Buffer) { - return new Ed25519Jwk({ - x: TypedArrayEncoder.toBase64URL(publicKey), - }) + public static fromPublicKey(publicKey: Uint8Array) { + return new Ed25519Jwk({ x: publicKey }) } } diff --git a/packages/core/src/crypto/jose/jwk/Jwk.ts b/packages/core/src/crypto/jose/jwk/Jwk.ts index a38b384ba9..b9cc10ccc5 100644 --- a/packages/core/src/crypto/jose/jwk/Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/Jwk.ts @@ -1,4 +1,3 @@ -import type { Buffer } from '../../../utils' import type { KeyType } from '../../KeyType' import type { JwaKeyType, JwaEncryptionAlgorithm, JwaSignatureAlgorithm } from '../jwa' @@ -11,7 +10,7 @@ export interface JwkJson { } export abstract class Jwk { - public abstract publicKey: Buffer + public abstract publicKey: Uint8Array public abstract supportedSignatureAlgorithms: JwaSignatureAlgorithm[] public abstract supportedEncryptionAlgorithms: JwaEncryptionAlgorithm[] diff --git a/packages/core/src/crypto/jose/jwk/K256Jwk.ts b/packages/core/src/crypto/jose/jwk/K256Jwk.ts index 914b940d86..ac0db2af9c 100644 --- a/packages/core/src/crypto/jose/jwk/K256Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/K256Jwk.ts @@ -1,13 +1,20 @@ import type { JwkJson } from './Jwk' import type { JwaEncryptionAlgorithm } from '../jwa/alg' -import { TypedArrayEncoder, Buffer } from '../../../utils' +import { + AffinePoint, + isValidCompressedPublicKeyFormat, + isValidDecompressedPublicKeyFormat, + Secp256k1, +} from 'ec-compression' + +import { CredoError } from '../../../error' +import { TypedArrayEncoder } from '../../../utils' import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' import { JwaSignatureAlgorithm } from '../jwa/alg' import { Jwk } from './Jwk' -import { compress, expand } from './ecCompression' import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' export class K256Jwk extends Jwk { @@ -15,14 +22,15 @@ export class K256Jwk extends Jwk { public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES256K] public static readonly keyType = KeyType.K256 - public readonly x: string - public readonly y: string + private readonly affinePoint: AffinePoint - public constructor({ x, y }: { x: string; y: string }) { + public constructor({ x, y }: { x: string | Uint8Array; y: string | Uint8Array }) { super() - this.x = x - this.y = y + const xAsBytes = typeof x === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(x)) : x + const yAsBytes = typeof y === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(y)) : y + + this.affinePoint = new AffinePoint(xAsBytes, yAsBytes) } public get kty() { @@ -33,17 +41,26 @@ export class K256Jwk extends Jwk { return JwaCurve.Secp256k1 as const } + public get x() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.xBytes) + } + + public get y() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.yBytes) + } + /** - * Returns the public key of the K-256 JWK. - * - * NOTE: this is the compressed variant. We still need to add support for the - * uncompressed variant. + * Returns the uncompressed public key of the P-256 JWK. */ public get publicKey() { - const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) - const compressedPublicKey = compress(publicKeyBuffer) + return this.affinePoint.decompressedForm + } - return Buffer.from(compressedPublicKey) + /** + * Returns the compressed public key of the K-256 JWK. + */ + public get publicKeyCompressed() { + return this.affinePoint.compressedForm } public get keyType() { @@ -78,15 +95,20 @@ export class K256Jwk extends Jwk { }) } - public static fromPublicKey(publicKey: Buffer) { - const expanded = expand(publicKey, JwaCurve.Secp256k1) - const x = expanded.slice(0, expanded.length / 2) - const y = expanded.slice(expanded.length / 2) + public static fromPublicKey(publicKey: Uint8Array) { + if (isValidCompressedPublicKeyFormat(publicKey, Secp256k1)) { + const affinePoint = AffinePoint.fromCompressedPoint(publicKey, Secp256k1) + return new K256Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } - return new K256Jwk({ - x: TypedArrayEncoder.toBase64URL(x), - y: TypedArrayEncoder.toBase64URL(y), - }) + if (isValidDecompressedPublicKeyFormat(publicKey, Secp256k1)) { + const affinePoint = AffinePoint.fromDecompressedPoint(publicKey, Secp256k1) + return new K256Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } + + throw new CredoError( + `${this.keyType} public key is neither a valid compressed or uncompressed key. Key prefix '${publicKey[0]}', key length '${publicKey.length}'` + ) } } diff --git a/packages/core/src/crypto/jose/jwk/P256Jwk.ts b/packages/core/src/crypto/jose/jwk/P256Jwk.ts index 68427ad9d7..f10a37d4ef 100644 --- a/packages/core/src/crypto/jose/jwk/P256Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/P256Jwk.ts @@ -1,13 +1,20 @@ import type { JwkJson } from './Jwk' import type { JwaEncryptionAlgorithm } from '../jwa/alg' -import { TypedArrayEncoder, Buffer } from '../../../utils' +import { + AffinePoint, + isValidCompressedPublicKeyFormat, + isValidDecompressedPublicKeyFormat, + Secp256r1, +} from 'ec-compression' + +import { CredoError } from '../../../error' +import { TypedArrayEncoder } from '../../../utils' import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' import { JwaSignatureAlgorithm } from '../jwa/alg' import { Jwk } from './Jwk' -import { compress, expand } from './ecCompression' import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' export class P256Jwk extends Jwk { @@ -15,14 +22,15 @@ export class P256Jwk extends Jwk { public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES256] public static readonly keyType = KeyType.P256 - public readonly x: string - public readonly y: string + private readonly affinePoint: AffinePoint - public constructor({ x, y }: { x: string; y: string }) { + public constructor({ x, y }: { x: string | Uint8Array; y: string | Uint8Array }) { super() - this.x = x - this.y = y + const xAsBytes = typeof x === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(x)) : x + const yAsBytes = typeof y === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(y)) : y + + this.affinePoint = new AffinePoint(xAsBytes, yAsBytes) } public get kty() { @@ -33,17 +41,26 @@ export class P256Jwk extends Jwk { return JwaCurve.P256 as const } + public get x() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.xBytes) + } + + public get y() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.yBytes) + } + /** - * Returns the public key of the P-256 JWK. - * - * NOTE: this is the compressed variant. We still need to add support for the - * uncompressed variant. + * Returns the uncompressed public key of the P-256 JWK. */ public get publicKey() { - const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) - const compressedPublicKey = compress(publicKeyBuffer) + return this.affinePoint.decompressedForm + } - return Buffer.from(compressedPublicKey) + /** + * Returns the compressed public key of the P-256 JWK. + */ + public get publicKeyCompressed() { + return this.affinePoint.compressedForm } public get keyType() { @@ -78,15 +95,20 @@ export class P256Jwk extends Jwk { }) } - public static fromPublicKey(publicKey: Buffer) { - const expanded = expand(publicKey, JwaCurve.P256) - const x = expanded.slice(0, expanded.length / 2) - const y = expanded.slice(expanded.length / 2) + public static fromPublicKey(publicKey: Uint8Array) { + if (isValidCompressedPublicKeyFormat(publicKey, Secp256r1)) { + const affinePoint = AffinePoint.fromCompressedPoint(publicKey, Secp256r1) + return new P256Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } - return new P256Jwk({ - x: TypedArrayEncoder.toBase64URL(x), - y: TypedArrayEncoder.toBase64URL(y), - }) + if (isValidDecompressedPublicKeyFormat(publicKey, Secp256r1)) { + const affinePoint = AffinePoint.fromDecompressedPoint(publicKey, Secp256r1) + return new P256Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } + + throw new CredoError( + `${this.keyType} public key is neither a valid compressed or uncompressed key. Key prefix '${publicKey[0]}', key length '${publicKey.length}'` + ) } } diff --git a/packages/core/src/crypto/jose/jwk/P384Jwk.ts b/packages/core/src/crypto/jose/jwk/P384Jwk.ts index b6f30c15c5..3b34273682 100644 --- a/packages/core/src/crypto/jose/jwk/P384Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/P384Jwk.ts @@ -1,13 +1,20 @@ import type { JwkJson } from './Jwk' import type { JwaEncryptionAlgorithm } from '../jwa/alg' -import { TypedArrayEncoder, Buffer } from '../../../utils' +import { + AffinePoint, + isValidCompressedPublicKeyFormat, + isValidDecompressedPublicKeyFormat, + Secp384r1, +} from 'ec-compression' + +import { CredoError } from '../../../error' +import { TypedArrayEncoder } from '../../../utils' import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' import { JwaSignatureAlgorithm } from '../jwa/alg' import { Jwk } from './Jwk' -import { compress, expand } from './ecCompression' import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' export class P384Jwk extends Jwk { @@ -15,14 +22,15 @@ export class P384Jwk extends Jwk { public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES384] public static readonly keyType = KeyType.P384 - public readonly x: string - public readonly y: string + private readonly affinePoint: AffinePoint - public constructor({ x, y }: { x: string; y: string }) { + public constructor({ x, y }: { x: string | Uint8Array; y: string | Uint8Array }) { super() - this.x = x - this.y = y + const xAsBytes = typeof x === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(x)) : x + const yAsBytes = typeof y === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(y)) : y + + this.affinePoint = new AffinePoint(xAsBytes, yAsBytes) } public get kty() { @@ -45,17 +53,26 @@ export class P384Jwk extends Jwk { return P384Jwk.supportedSignatureAlgorithms } + public get x() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.xBytes) + } + + public get y() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.yBytes) + } + /** - * Returns the public key of the P-384 JWK. - * - * NOTE: this is the compressed variant. We still need to add support for the - * uncompressed variant. + * Returns the uncompressed public key of the P-384 JWK. */ public get publicKey() { - const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) - const compressedPublicKey = compress(publicKeyBuffer) + return this.affinePoint.decompressedForm + } - return Buffer.from(compressedPublicKey) + /** + * Returns the compressed public key of the P-384 JWK. + */ + public get publicKeyCompressed() { + return this.affinePoint.compressedForm } public toJson() { @@ -78,15 +95,20 @@ export class P384Jwk extends Jwk { }) } - public static fromPublicKey(publicKey: Buffer) { - const expanded = expand(publicKey, JwaCurve.P384) - const x = expanded.slice(0, expanded.length / 2) - const y = expanded.slice(expanded.length / 2) + public static fromPublicKey(publicKey: Uint8Array) { + if (isValidCompressedPublicKeyFormat(publicKey, Secp384r1)) { + const affinePoint = AffinePoint.fromCompressedPoint(publicKey, Secp384r1) + return new P384Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } - return new P384Jwk({ - x: TypedArrayEncoder.toBase64URL(x), - y: TypedArrayEncoder.toBase64URL(y), - }) + if (isValidDecompressedPublicKeyFormat(publicKey, Secp384r1)) { + const affinePoint = AffinePoint.fromDecompressedPoint(publicKey, Secp384r1) + return new P384Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } + + throw new CredoError( + `${this.keyType} public key is neither a valid compressed or uncompressed key. Key prefix '${publicKey[0]}', key length '${publicKey.length}'` + ) } } diff --git a/packages/core/src/crypto/jose/jwk/P521Jwk.ts b/packages/core/src/crypto/jose/jwk/P521Jwk.ts index 5b7998eff7..bd54ff2604 100644 --- a/packages/core/src/crypto/jose/jwk/P521Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/P521Jwk.ts @@ -1,13 +1,20 @@ import type { JwkJson } from './Jwk' import type { JwaEncryptionAlgorithm } from '../jwa/alg' -import { TypedArrayEncoder, Buffer } from '../../../utils' +import { + AffinePoint, + isValidCompressedPublicKeyFormat, + isValidDecompressedPublicKeyFormat, + Secp521r1, +} from 'ec-compression' + +import { CredoError } from '../../../error' +import { TypedArrayEncoder } from '../../../utils' import { KeyType } from '../../KeyType' import { JwaCurve, JwaKeyType } from '../jwa' import { JwaSignatureAlgorithm } from '../jwa/alg' import { Jwk } from './Jwk' -import { compress, expand } from './ecCompression' import { hasKty, hasCrv, hasX, hasY, hasValidUse } from './validate' export class P521Jwk extends Jwk { @@ -15,14 +22,15 @@ export class P521Jwk extends Jwk { public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [JwaSignatureAlgorithm.ES512] public static readonly keyType = KeyType.P521 - public readonly x: string - public readonly y: string + private readonly affinePoint: AffinePoint - public constructor({ x, y }: { x: string; y: string }) { + public constructor({ x, y }: { x: string | Uint8Array; y: string | Uint8Array }) { super() - this.x = x - this.y = y + const xAsBytes = typeof x === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(x)) : x + const yAsBytes = typeof y === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(y)) : y + + this.affinePoint = new AffinePoint(xAsBytes, yAsBytes) } public get kty() { @@ -45,17 +53,26 @@ export class P521Jwk extends Jwk { return P521Jwk.supportedSignatureAlgorithms } + public get x() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.xBytes) + } + + public get y() { + return TypedArrayEncoder.toBase64URL(this.affinePoint.yBytes) + } + /** - * Returns the public key of the P-521 JWK. - * - * NOTE: this is the compressed variant. We still need to add support for the - * uncompressed variant. + * Returns the uncompressed public key of the P-521 JWK. */ public get publicKey() { - const publicKeyBuffer = Buffer.concat([TypedArrayEncoder.fromBase64(this.x), TypedArrayEncoder.fromBase64(this.y)]) - const compressedPublicKey = compress(publicKeyBuffer) + return this.affinePoint.decompressedForm + } - return Buffer.from(compressedPublicKey) + /** + * Returns the compressed public key of the P-521 JWK. + */ + public get publicKeyCompressed() { + return this.affinePoint.compressedForm } public toJson() { @@ -78,15 +95,20 @@ export class P521Jwk extends Jwk { }) } - public static fromPublicKey(publicKey: Buffer) { - const expanded = expand(publicKey, JwaCurve.P521) - const x = expanded.slice(0, expanded.length / 2) - const y = expanded.slice(expanded.length / 2) + public static fromPublicKey(publicKey: Uint8Array) { + if (isValidCompressedPublicKeyFormat(publicKey, Secp521r1)) { + const affinePoint = AffinePoint.fromCompressedPoint(publicKey, Secp521r1) + return new P521Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } - return new P521Jwk({ - x: TypedArrayEncoder.toBase64URL(x), - y: TypedArrayEncoder.toBase64URL(y), - }) + if (isValidDecompressedPublicKeyFormat(publicKey, Secp521r1)) { + const affinePoint = AffinePoint.fromDecompressedPoint(publicKey, Secp521r1) + return new P521Jwk({ x: affinePoint.xBytes, y: affinePoint.yBytes }) + } + + throw new CredoError( + `${this.keyType} public key is neither a valid compressed or uncompressed key. Key prefix '${publicKey[0]}', key length '${publicKey.length}'` + ) } } diff --git a/packages/core/src/crypto/jose/jwk/X25519Jwk.ts b/packages/core/src/crypto/jose/jwk/X25519Jwk.ts index 6d1ada04ce..78c22a1a72 100644 --- a/packages/core/src/crypto/jose/jwk/X25519Jwk.ts +++ b/packages/core/src/crypto/jose/jwk/X25519Jwk.ts @@ -1,5 +1,4 @@ import type { JwkJson } from './Jwk' -import type { Buffer } from '../../../utils' import type { JwaSignatureAlgorithm } from '../jwa' import { TypedArrayEncoder } from '../../../utils' @@ -19,12 +18,16 @@ export class X25519Jwk extends Jwk { public static readonly supportedSignatureAlgorithms: JwaSignatureAlgorithm[] = [] public static readonly keyType = KeyType.X25519 - public readonly x: string + private readonly _x: Uint8Array - public constructor({ x }: { x: string }) { + public constructor({ x }: { x: string | Uint8Array }) { super() - this.x = x + this._x = typeof x === 'string' ? Uint8Array.from(TypedArrayEncoder.fromBase64(x)) : x + } + + public get x() { + return TypedArrayEncoder.toBase64URL(this._x) } public get kty() { @@ -48,7 +51,7 @@ export class X25519Jwk extends Jwk { } public get publicKey() { - return TypedArrayEncoder.fromBase64(this.x) + return this._x } public toJson() { @@ -69,10 +72,8 @@ export class X25519Jwk extends Jwk { }) } - public static fromPublicKey(publicKey: Buffer) { - return new X25519Jwk({ - x: TypedArrayEncoder.toBase64URL(publicKey), - }) + public static fromPublicKey(publicKey: Uint8Array) { + return new X25519Jwk({ x: publicKey }) } } diff --git a/packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts index a2f07ecc7f..e5903063a6 100644 --- a/packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts +++ b/packages/core/src/crypto/jose/jwk/__tests__/Ed25519Jwk.test.ts @@ -15,7 +15,7 @@ describe('Ed25519JWk', () => { expect(jwk.kty).toEqual('OKP') expect(jwk.crv).toEqual('Ed25519') expect(jwk.keyType).toEqual(KeyType.Ed25519) - expect(jwk.publicKey).toEqual(TypedArrayEncoder.fromBase64(jwkJson.x)) + expect(jwk.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase64(jwkJson.x))) expect(jwk.supportedEncryptionAlgorithms).toEqual([]) expect(jwk.supportedSignatureAlgorithms).toEqual(['EdDSA']) expect(jwk.key.keyType).toEqual(KeyType.Ed25519) @@ -26,7 +26,7 @@ describe('Ed25519JWk', () => { const jwk = Ed25519Jwk.fromJson(jwkJson) expect(jwk.x).toEqual(jwkJson.x) - expect(() => Ed25519Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'Ed25519' JWK.") + expect(() => Ed25519Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrow("Invalid 'Ed25519' JWK.") }) test('fromPublicKey', () => { diff --git a/packages/core/src/crypto/jose/jwk/__tests__/K_256Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/K_256Jwk.test.ts new file mode 100644 index 0000000000..87206c877d --- /dev/null +++ b/packages/core/src/crypto/jose/jwk/__tests__/K_256Jwk.test.ts @@ -0,0 +1,58 @@ +import { compressPublicKeyIfPossible } from 'ec-compression' + +import { TypedArrayEncoder } from '../../../../utils' +import { KeyType } from '../../../KeyType' +import { K256Jwk } from '../K256Jwk' + +// Generated with https://mkjwk.org +const jwkJson = { + kty: 'EC', + crv: 'secp256k1', + x: '0CtFvFuEzkEhPOTKHi3k2OvEgJmQ1dH-IXXme3JBzVY', + y: 'vIr8423MqTswmAebHhCaOoiYdp1kyOiduZinD3JBXxU', +} + +const uncompressedPublicKey = new Uint8Array([ + 0x04, + ...TypedArrayEncoder.fromBase64(jwkJson.x), + ...TypedArrayEncoder.fromBase64(jwkJson.y), +]) +const compressedPublicKey = compressPublicKeyIfPossible(uncompressedPublicKey, 'k-256') + +describe('K_256JWk', () => { + test('has correct properties', () => { + const jwk = new K256Jwk({ x: jwkJson.x, y: jwkJson.y }) + + expect(jwk.kty).toEqual('EC') + expect(jwk.crv).toEqual('secp256k1') + expect(jwk.keyType).toEqual(KeyType.K256) + expect(jwk.supportedEncryptionAlgorithms).toEqual([]) + expect(jwk.supportedSignatureAlgorithms).toEqual(['ES256K']) + expect(jwk.key.keyType).toEqual(KeyType.K256) + expect(jwk.toJson()).toEqual(jwkJson) + + expect(jwk.publicKey).toEqual(uncompressedPublicKey) + expect(jwk.publicKey.length).toEqual(65) + expect(jwk.publicKeyCompressed.length).toEqual(33) + }) + + test('fromJson', () => { + const jwk = K256Jwk.fromJson(jwkJson) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + + expect(() => K256Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrow("Invalid 'K-256' JWK.") + }) + + test('fromUncompressedPublicKey', () => { + const jwk = K256Jwk.fromPublicKey(uncompressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) + + test('fromCompressedPublicKey', () => { + const jwk = K256Jwk.fromPublicKey(compressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) +}) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts index 1250d031d9..e65bae02e6 100644 --- a/packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts +++ b/packages/core/src/crypto/jose/jwk/__tests__/P_256Jwk.test.ts @@ -1,15 +1,24 @@ -import { TypedArrayEncoder, Buffer } from '../../../../utils' +import { compressPublicKeyIfPossible } from 'ec-compression' + +import { TypedArrayEncoder } from '../../../../utils' import { KeyType } from '../../../KeyType' import { P256Jwk } from '../P256Jwk' -import { compress } from '../ecCompression' +// Generated with https://mkjwk.org const jwkJson = { kty: 'EC', crv: 'P-256', - x: 'igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns', - y: 'efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM', + x: 'YKIJKqnGI22osL86OZUIGmwW7Bh0ZsUpTVBLVRNyThQ', + y: 'booCsoNXVs1W8GBt9V7DvEktjyWPUV2NFvDrW2aqMfI', } +const uncompressedPublicKey = new Uint8Array([ + 0x04, + ...TypedArrayEncoder.fromBase64(jwkJson.x), + ...TypedArrayEncoder.fromBase64(jwkJson.y), +]) +const compressedPublicKey = compressPublicKeyIfPossible(uncompressedPublicKey, 'p-256') + describe('P_256JWk', () => { test('has correct properties', () => { const jwk = new P256Jwk({ x: jwkJson.x, y: jwkJson.y }) @@ -17,17 +26,14 @@ describe('P_256JWk', () => { expect(jwk.kty).toEqual('EC') expect(jwk.crv).toEqual('P-256') expect(jwk.keyType).toEqual(KeyType.P256) - - const publicKeyBuffer = Buffer.concat([ - TypedArrayEncoder.fromBase64(jwkJson.x), - TypedArrayEncoder.fromBase64(jwkJson.y), - ]) - const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) - expect(jwk.publicKey).toEqual(compressedPublicKey) expect(jwk.supportedEncryptionAlgorithms).toEqual([]) expect(jwk.supportedSignatureAlgorithms).toEqual(['ES256']) expect(jwk.key.keyType).toEqual(KeyType.P256) expect(jwk.toJson()).toEqual(jwkJson) + + expect(jwk.publicKey).toEqual(uncompressedPublicKey) + expect(jwk.publicKey.length).toEqual(65) + expect(jwk.publicKeyCompressed.length).toEqual(33) }) test('fromJson', () => { @@ -35,16 +41,16 @@ describe('P_256JWk', () => { expect(jwk.x).toEqual(jwkJson.x) expect(jwk.y).toEqual(jwkJson.y) - expect(() => P256Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'P-256' JWK.") + expect(() => P256Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrow("Invalid 'P-256' JWK.") }) - test('fromPublicKey', () => { - const publicKeyBuffer = Buffer.concat([ - TypedArrayEncoder.fromBase64(jwkJson.x), - TypedArrayEncoder.fromBase64(jwkJson.y), - ]) - const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + test('fromUncompressedPublicKey', () => { + const jwk = P256Jwk.fromPublicKey(uncompressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) + test('fromCompressedPublicKey', () => { const jwk = P256Jwk.fromPublicKey(compressedPublicKey) expect(jwk.x).toEqual(jwkJson.x) expect(jwk.y).toEqual(jwkJson.y) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts index 0f409ed878..5028070cca 100644 --- a/packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts +++ b/packages/core/src/crypto/jose/jwk/__tests__/P_384Jwk.test.ts @@ -1,15 +1,24 @@ -import { TypedArrayEncoder, Buffer } from '../../../../utils' +import { compressPublicKeyIfPossible } from 'ec-compression' + +import { TypedArrayEncoder } from '../../../../utils' import { KeyType } from '../../../KeyType' import { P384Jwk } from '../P384Jwk' -import { compress } from '../ecCompression' +// Generated with https://mkjwk.org const jwkJson = { kty: 'EC', crv: 'P-384', - x: 'lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc', - y: 'y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv', + x: 'Rl0BbVOvE0zcytPVSGgM39tihXnlYjuaLin3SjhD6cLRL_IK-3tHTCljCiJBbSX9', + y: '282rUQMBuCkLb0t9PbReApadoP7Jo-sVcZDNGglYg4iMsqNPvyq-WIzxSUb1USpc', } +const uncompressedPublicKey = new Uint8Array([ + 0x04, + ...TypedArrayEncoder.fromBase64(jwkJson.x), + ...TypedArrayEncoder.fromBase64(jwkJson.y), +]) +const compressedPublicKey = compressPublicKeyIfPossible(uncompressedPublicKey, 'p-384') + describe('P_384JWk', () => { test('has correct properties', () => { const jwk = new P384Jwk({ x: jwkJson.x, y: jwkJson.y }) @@ -17,16 +26,14 @@ describe('P_384JWk', () => { expect(jwk.kty).toEqual('EC') expect(jwk.crv).toEqual('P-384') expect(jwk.keyType).toEqual(KeyType.P384) - const publicKeyBuffer = Buffer.concat([ - TypedArrayEncoder.fromBase64(jwkJson.x), - TypedArrayEncoder.fromBase64(jwkJson.y), - ]) - const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) - expect(jwk.publicKey).toEqual(compressedPublicKey) expect(jwk.supportedEncryptionAlgorithms).toEqual([]) expect(jwk.supportedSignatureAlgorithms).toEqual(['ES384']) expect(jwk.key.keyType).toEqual(KeyType.P384) expect(jwk.toJson()).toEqual(jwkJson) + + expect(jwk.publicKey).toEqual(uncompressedPublicKey) + expect(jwk.publicKey.length).toEqual(97) + expect(jwk.publicKeyCompressed.length).toEqual(49) }) test('fromJson', () => { @@ -34,16 +41,16 @@ describe('P_384JWk', () => { expect(jwk.x).toEqual(jwkJson.x) expect(jwk.y).toEqual(jwkJson.y) - expect(() => P384Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'P-384' JWK.") + expect(() => P384Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrow("Invalid 'P-384' JWK.") }) - test('fromPublicKey', () => { - const publicKeyBuffer = Buffer.concat([ - TypedArrayEncoder.fromBase64(jwkJson.x), - TypedArrayEncoder.fromBase64(jwkJson.y), - ]) - const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + test('fromUncompressedPublicKey', () => { + const jwk = P384Jwk.fromPublicKey(uncompressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) + test('fromCompressedPublicKey', () => { const jwk = P384Jwk.fromPublicKey(compressedPublicKey) expect(jwk.x).toEqual(jwkJson.x) expect(jwk.y).toEqual(jwkJson.y) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts index 662fb5ee7b..9f8a473820 100644 --- a/packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts +++ b/packages/core/src/crypto/jose/jwk/__tests__/P_521Jwk.test.ts @@ -1,15 +1,24 @@ -import { TypedArrayEncoder, Buffer } from '../../../../utils' +import { compressPublicKeyIfPossible } from 'ec-compression' + +import { TypedArrayEncoder } from '../../../../utils' import { KeyType } from '../../../KeyType' import { P521Jwk } from '../P521Jwk' -import { compress } from '../ecCompression' +// Generated with https://mkjwk.org const jwkJson = { kty: 'EC', crv: 'P-521', - x: 'ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS', - y: 'AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC', + x: 'AAyV8qWafv5UPexMB3ohAPSFuz_zFdaHAjb-XlzO8qBkx-lZtN1PN1E9AHipP6esSNBPilGOAkiZYnQ48hPJgJQG', + y: 'AccbmJnVXJhxJ8vFS4GcG1eM27XtSOjKz1dX52wbJ0YN6U5KEOPQ-3krxvLAqlFG2BCbZkpnrfateEdervmp3Q3G', } +const uncompressedPublicKey = new Uint8Array([ + 0x04, + ...TypedArrayEncoder.fromBase64(jwkJson.x), + ...TypedArrayEncoder.fromBase64(jwkJson.y), +]) +const compressedPublicKey = compressPublicKeyIfPossible(uncompressedPublicKey, 'p-521') + describe('P_521JWk', () => { test('has correct properties', () => { const jwk = new P521Jwk({ x: jwkJson.x, y: jwkJson.y }) @@ -17,16 +26,14 @@ describe('P_521JWk', () => { expect(jwk.kty).toEqual('EC') expect(jwk.crv).toEqual('P-521') expect(jwk.keyType).toEqual(KeyType.P521) - const publicKeyBuffer = Buffer.concat([ - TypedArrayEncoder.fromBase64(jwkJson.x), - TypedArrayEncoder.fromBase64(jwkJson.y), - ]) - const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) - expect(jwk.publicKey).toEqual(compressedPublicKey) expect(jwk.supportedEncryptionAlgorithms).toEqual([]) expect(jwk.supportedSignatureAlgorithms).toEqual(['ES512']) expect(jwk.key.keyType).toEqual(KeyType.P521) expect(jwk.toJson()).toEqual(jwkJson) + + expect(jwk.publicKey).toEqual(uncompressedPublicKey) + expect(jwk.publicKey.length).toEqual(133) + expect(jwk.publicKeyCompressed.length).toEqual(67) }) test('fromJson', () => { @@ -34,16 +41,16 @@ describe('P_521JWk', () => { expect(jwk.x).toEqual(jwkJson.x) expect(jwk.y).toEqual(jwkJson.y) - expect(() => P521Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'P-521' JWK.") + expect(() => P521Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrow("Invalid 'P-521' JWK.") }) - test('fromPublicKey', () => { - const publicKeyBuffer = Buffer.concat([ - TypedArrayEncoder.fromBase64(jwkJson.x), - TypedArrayEncoder.fromBase64(jwkJson.y), - ]) - const compressedPublicKey = Buffer.from(compress(publicKeyBuffer)) + test('fromUncompressedPublicKey', () => { + const jwk = P521Jwk.fromPublicKey(uncompressedPublicKey) + expect(jwk.x).toEqual(jwkJson.x) + expect(jwk.y).toEqual(jwkJson.y) + }) + test('fromCompressedPublicKey', () => { const jwk = P521Jwk.fromPublicKey(compressedPublicKey) expect(jwk.x).toEqual(jwkJson.x) expect(jwk.y).toEqual(jwkJson.y) diff --git a/packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts b/packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts index 138e3f59b4..c582a63ca2 100644 --- a/packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts +++ b/packages/core/src/crypto/jose/jwk/__tests__/X25519Jwk.test.ts @@ -15,7 +15,7 @@ describe('X25519JWk', () => { expect(jwk.kty).toEqual('OKP') expect(jwk.crv).toEqual('X25519') expect(jwk.keyType).toEqual(KeyType.X25519) - expect(jwk.publicKey).toEqual(TypedArrayEncoder.fromBase64(jwkJson.x)) + expect(jwk.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase64(jwkJson.x))) expect(jwk.supportedEncryptionAlgorithms).toEqual(['ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'ECDH-ES']) expect(jwk.supportedSignatureAlgorithms).toEqual([]) expect(jwk.key.keyType).toEqual(KeyType.X25519) @@ -26,7 +26,7 @@ describe('X25519JWk', () => { const jwk = X25519Jwk.fromJson(jwkJson) expect(jwk.x).toEqual(jwkJson.x) - expect(() => X25519Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrowError("Invalid 'X25519' JWK.") + expect(() => X25519Jwk.fromJson({ ...jwkJson, kty: 'test' })).toThrow("Invalid 'X25519' JWK.") }) test('fromPublicKey', () => { diff --git a/packages/core/src/crypto/jose/jwk/ecCompression.ts b/packages/core/src/crypto/jose/jwk/ecCompression.ts deleted file mode 100644 index f602191e8d..0000000000 --- a/packages/core/src/crypto/jose/jwk/ecCompression.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Based on https://github.com/transmute-industries/verifiable-data/blob/main/packages/web-crypto-key-pair/src/compression/ec-compression.ts - */ - -// native BigInteger is only supported in React Native 0.70+, so we use big-integer for now. -import bigInt from 'big-integer' - -import { Buffer } from '../../../utils/buffer' -import { JwaCurve } from '../jwa' - -const curveToPointLength = { - [JwaCurve.P256]: 64, - [JwaCurve.P384]: 96, - [JwaCurve.P521]: 132, - [JwaCurve.Secp256k1]: 64, -} - -function getConstantsForCurve(curve: 'P-256' | 'P-384' | 'P-521' | 'secp256k1') { - let two, prime, b, pIdent - - if (curve === 'P-256') { - two = bigInt(2) - prime = two.pow(256).subtract(two.pow(224)).add(two.pow(192)).add(two.pow(96)).subtract(1) - - pIdent = prime.add(1).divide(4) - - b = bigInt('5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16) - } - - if (curve === 'P-384') { - two = bigInt(2) - prime = two.pow(384).subtract(two.pow(128)).subtract(two.pow(96)).add(two.pow(32)).subtract(1) - - pIdent = prime.add(1).divide(4) - b = bigInt('b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16) - } - - if (curve === 'P-521') { - two = bigInt(2) - prime = two.pow(521).subtract(1) - b = bigInt( - '00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', - 16 - ) - pIdent = prime.add(1).divide(4) - } - - // https://en.bitcoin.it/wiki/Secp256k1 - // p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F - // P = 2256 - 232 - 29 - 28 - 27 - 26 - 24 - 1 - if (curve === JwaCurve.Secp256k1) { - two = bigInt(2) - prime = two - .pow(256) - .subtract(two.pow(32)) - .subtract(two.pow(9)) - .subtract(two.pow(8)) - .subtract(two.pow(7)) - .subtract(two.pow(6)) - .subtract(two.pow(4)) - .subtract(1) - b = bigInt(7) - pIdent = prime.add(1).divide(4) - } - - if (!prime || !b || !pIdent) { - throw new Error(`Unsupported curve ${curve}`) - } - - return { prime, b, pIdent } -} - -// see https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression -// https://github.com/w3c-ccg/did-method-key/pull/36 -/** - * Point compress elliptic curve key - * @return Compressed representation - */ -function compressECPoint(x: Uint8Array, y: Uint8Array): Uint8Array { - const out = new Uint8Array(x.length + 1) - out[0] = 2 + (y[y.length - 1] & 1) - out.set(x, 1) - return out -} - -function padWithZeroes(number: number | string, length: number) { - let value = '' + number - while (value.length < length) { - value = '0' + value - } - return value -} - -export function compress(publicKey: Uint8Array): Uint8Array { - const publicKeyHex = Buffer.from(publicKey).toString('hex') - const xHex = publicKeyHex.slice(0, publicKeyHex.length / 2) - const yHex = publicKeyHex.slice(publicKeyHex.length / 2, publicKeyHex.length) - const xOctet = Uint8Array.from(Buffer.from(xHex, 'hex')) - const yOctet = Uint8Array.from(Buffer.from(yHex, 'hex')) - return compressECPoint(xOctet, yOctet) -} - -export function expand(publicKey: Uint8Array, curve: 'P-256' | 'P-384' | 'P-521' | 'secp256k1'): Uint8Array { - const publicKeyComponent = Buffer.from(publicKey).toString('hex') - const { prime, b, pIdent } = getConstantsForCurve(curve) - const signY = new Number(publicKeyComponent[1]).valueOf() - 2 - const x = bigInt(publicKeyComponent.substring(2), 16) - - // y^2 = x^3 - 3x + b - let y = x.pow(3).subtract(x.multiply(3)).add(b).modPow(pIdent, prime) - - if (curve === 'secp256k1') { - // y^2 = x^3 + 7 - y = x.pow(3).add(7).modPow(pIdent, prime) - } - - // If the parity doesn't match it's the *other* root - if (y.mod(2).toJSNumber() !== signY) { - // y = prime - y - y = prime.subtract(y) - } - - return Buffer.from( - padWithZeroes(x.toString(16), curveToPointLength[curve]) + padWithZeroes(y.toString(16), curveToPointLength[curve]), - 'hex' - ) -} diff --git a/packages/core/src/modules/dcql/DcqlService.ts b/packages/core/src/modules/dcql/DcqlService.ts index e595d9fa72..100937c02e 100644 --- a/packages/core/src/modules/dcql/DcqlService.ts +++ b/packages/core/src/modules/dcql/DcqlService.ts @@ -1,10 +1,19 @@ import type { AgentContext } from '../../agent' +import type { VerifiablePresentation } from '../dif-presentation-exchange/index' +import type { TransactionDataAuthorization } from '../dif-presentation-exchange/models/TransactionData' import { DcqlCredential, DcqlMdocCredential, DcqlPresentationResult, DcqlQuery, DcqlSdJwtVcCredential } from 'dcql' - import { injectable } from 'tsyringe' -import { Mdoc, MdocApi, MdocDeviceResponse, MdocOpenId4VpSessionTranscriptOptions, MdocRecord } from '../mdoc' +import { TypedArrayEncoder } from '../../utils' +import { + Mdoc, + MdocApi, + MdocDeviceResponse, + MdocOpenId4VpDcApiSessionTranscriptOptions, + MdocOpenId4VpSessionTranscriptOptions, + MdocRecord, +} from '../mdoc' import { SdJwtVcApi, SdJwtVcRecord, SdJwtVcService } from '../sd-jwt-vc' import { buildDisclosureFrameForPayload } from '../sd-jwt-vc/disclosureFrame' import { ClaimFormat, W3cCredentialRecord, W3cCredentialRepository } from '../vc' @@ -12,8 +21,6 @@ import { ClaimFormat, W3cCredentialRecord, W3cCredentialRepository } from '../vc import { DcqlError } from './DcqlError' import { DcqlCredentialsForRequest, DcqlEncodedPresentations, DcqlPresentation, DcqlQueryResult } from './models' import { dcqlGetPresentationsToCreate as getDcqlVcPresentationsToCreate } from './utils' -import { TransactionDataAuthorization } from '../dif-presentation-exchange/models/TransactionData' -import { VerifiablePresentation } from '../dif-presentation-exchange/index' @injectable() export class DcqlService { @@ -30,7 +37,13 @@ export class DcqlService { const formats = new Set(dcqlQuery.credentials.map((c) => c.format)) for (const format of formats) { - if (format !== 'vc+sd-jwt' && format !== 'jwt_vc_json' && format !== 'jwt_vc_json-ld' && format !== 'mso_mdoc') { + if ( + format !== 'vc+sd-jwt' && + format !== 'dc+sd-jwt' && + format !== 'jwt_vc_json' && + format !== 'jwt_vc_json-ld' && + format !== 'mso_mdoc' + ) { throw new DcqlError(`Unsupported credential format ${format}.`) } } @@ -63,7 +76,8 @@ export class DcqlService { const sdJwtVctValues = dcqlQuery.credentials .filter( - (credentialQuery): credentialQuery is DcqlSdJwtVcCredential.Model => credentialQuery.format === 'vc+sd-jwt' + (credentialQuery): credentialQuery is DcqlSdJwtVcCredential.Model => + credentialQuery.format === 'vc+sd-jwt' || credentialQuery.format === 'dc+sd-jwt' ) .flatMap((c) => c.meta?.vct_values) @@ -135,26 +149,51 @@ export class DcqlService { const queryResult = DcqlQuery.query(DcqlQuery.parse(dcqlQuery), dcqlCredentials) const matchesWithRecord = Object.fromEntries( Object.entries(queryResult.credential_matches).map(([credential_query_id, result]) => { + const all = result.all.map((entry) => + entry.map((inner) => { + if (!inner || !inner.success) return inner + + const record = credentialRecords[inner.input_credential_index] + + return { + ...inner, + output: + record.type === 'SdJwtVcRecord' && + (inner.output.credential_format === 'dc+sd-jwt' || inner.output.credential_format === 'vc+sd-jwt') + ? { + ...inner.output, + claims: agentContext.dependencyManager + .resolve(SdJwtVcService) + .applyDisclosuresForPayload(record.compactSdJwtVc, inner.output.claims).prettyClaims, + } + : inner.output, + record: credentialRecords[inner.input_credential_index], + } + }) + ) + if (result.success) { - if (result.output.credential_format === 'vc+sd-jwt') { + if (result.output.credential_format === 'vc+sd-jwt' || result.output.credential_format === 'dc+sd-jwt') { const sdJwtVcRecord = credentialRecords[result.input_credential_index] as SdJwtVcRecord const claims = agentContext.dependencyManager .resolve(SdJwtVcService) .applyDisclosuresForPayload(sdJwtVcRecord.compactSdJwtVc, result.output.claims).prettyClaims + return [ credential_query_id, { ...result, + all, output: { ...result.output, claims }, record: credentialRecords[result.input_credential_index], }, ] } - return [credential_query_id, { ...result, record: credentialRecords[result.input_credential_index] }] - } else { - return [credential_query_id, result] + return [credential_query_id, { ...result, record: credentialRecords[result.input_credential_index], all }] } + + return [credential_query_id, { ...result, all }] }) ) @@ -256,7 +295,9 @@ export class DcqlService { credentialQueryToCredential: DcqlCredentialsForRequest challenge: string domain?: string - openid4vp?: Omit + openid4vp?: + | Omit + | Omit transactionDataAuthorization?: TransactionDataAuthorization } ): Promise<{ @@ -279,30 +320,25 @@ export class DcqlService { throw new DcqlError('Missing openid4vp options for creating MDOC presentation.') } - if (!domain) { - throw new DcqlError('Missing domain property for creating MDOC presentation.') - } - - const { deviceResponseBase64Url } = await MdocDeviceResponse.createOpenId4VpDcqlDeviceResponse(agentContext, { + const deviceResponse = await MdocDeviceResponse.createDeviceResponse(agentContext, { mdocs: [Mdoc.fromBase64Url(mdocRecord.base64Url)], - docRequests: [ + documentRequests: [ { - itemsRequestData: { - docType: mdocRecord.getTags().docType, - nameSpaces: Object.fromEntries( - Object.entries(presentationToCreate.disclosedPayload).map(([key, value]) => { - return [key, Object.fromEntries(Object.entries(value).map(([key]) => [key, true]))] - }) - ), - }, + docType: mdocRecord.getTags().docType, + nameSpaces: Object.fromEntries( + Object.entries(presentationToCreate.disclosedPayload).map(([key, value]) => { + // FIXME: we need the DCQL query here to get the intent_to_retain from query (currnetly hardcoded to false) + return [key, Object.fromEntries(Object.entries(value).map(([key]) => [key, false]))] + }) + ), }, ], sessionTranscriptOptions: { ...openid4vp, - clientId: domain, verifierGeneratedNonce: challenge, }, }) + const deviceResponseBase64Url = TypedArrayEncoder.toBase64URL(deviceResponse) encodedDcqlPresentation[credentialQueryId] = deviceResponseBase64Url dcqlPresentation[credentialQueryId] = MdocDeviceResponse.fromBase64Url(deviceResponseBase64Url) diff --git a/packages/core/src/modules/dcql/models/index.ts b/packages/core/src/modules/dcql/models/index.ts index 88c736bed6..2848c03d78 100644 --- a/packages/core/src/modules/dcql/models/index.ts +++ b/packages/core/src/modules/dcql/models/index.ts @@ -20,12 +20,23 @@ export type DcqlMdocCredential = _DcqlMdocCredential.Model['Input'] export type DcqlSdJwtVcCredential = _DcqlSdJwtVcCredential.Model['Input'] export type DcqlW3cVcCredential = _DcqlW3cVcCredential.Model['Input'] +type DcqlEntrySuccess = _DcqlQueryResult['credential_matches'][string]['all'][number][number] & { + success: true + record: MdocRecord | SdJwtVcRecord | W3cCredentialRecord +} + +type DcqlEntryNoSuccess = _DcqlQueryResult['credential_matches'][string]['all'][number][number] & { + success: false + record?: undefined +} + export type DcqlMatchWithRecord = - | (_DcqlQueryResult['credential_matches'][number] & { + | (_DcqlQueryResult['credential_matches'][string] & { success: true record: MdocRecord | SdJwtVcRecord | W3cCredentialRecord + all: Array> }) - | (_DcqlQueryResult['credential_matches'][number] & { + | (_DcqlQueryResult['credential_matches'][string] & { success: false }) diff --git a/packages/core/src/modules/dcql/utils/DcqlPresentationsToCreate.ts b/packages/core/src/modules/dcql/utils/DcqlPresentationsToCreate.ts index dc54a9e678..aef4a16851 100644 --- a/packages/core/src/modules/dcql/utils/DcqlPresentationsToCreate.ts +++ b/packages/core/src/modules/dcql/utils/DcqlPresentationsToCreate.ts @@ -1,12 +1,12 @@ +import type { TransactionData } from '../../dif-presentation-exchange/index' +import type { TransactionDataAuthorization } from '../../dif-presentation-exchange/models/TransactionData' import type { SdJwtVcRecord } from '../../sd-jwt-vc' import type { DcqlCredentialsForRequest } from '../models' import type { DcqlSdJwtVcCredential, DcqlMdocCredential, DcqlW3cVcCredential } from 'dcql' +import { CredoError } from '../../../error/CredoError' import { MdocRecord } from '../../mdoc' import { W3cCredentialRecord, ClaimFormat } from '../../vc' -import { TransactionData } from '../../dif-presentation-exchange/index' -import { TransactionDataAuthorization } from '../../dif-presentation-exchange/models/TransactionData' -import { CredoError } from '../../../error/CredoError' // - the credentials included in the presentation export interface DcqlSdJwtVcPresentationToCreate { diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts index fe2b3f0c03..fc05105baa 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1.test.ts @@ -39,9 +39,9 @@ describe('bls12381g1', () => { expect(key.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) expect(key.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) - expect(key.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(key.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY))) expect(key.keyType).toBe(KeyType.Bls12381g1) - expect(key.prefixedPublicKey.equals(TEST_BLS12381G1_PREFIX_BYTES)).toBe(true) + expect(Buffer.from(key.prefixedPublicKey).equals(TEST_BLS12381G1_PREFIX_BYTES)).toBe(true) }) it('should return a valid verification method', async () => { @@ -68,7 +68,7 @@ describe('bls12381g1', () => { verificationMethod.type = 'SomeRandomType' - expect(() => keyDidBls12381g1.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + expect(() => keyDidBls12381g1.getKeyFromVerificationMethod(verificationMethod)).toThrow( "Verification method with type 'SomeRandomType' not supported for key type 'bls12381g1'" ) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts index 442422f2cb..85ac115900 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g1g2.test.ts @@ -50,9 +50,9 @@ describe('bls12381g1g2', () => { expect(key.fingerprint).toBe(TEST_BLS12381G1G2_FINGERPRINT) expect(key.publicKeyBase58).toBe(TEST_BLS12381G1G2_BASE58_KEY) - expect(key.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY)) + expect(key.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase58(TEST_BLS12381G1G2_BASE58_KEY))) expect(key.keyType).toBe(KeyType.Bls12381g1g2) - expect(key.prefixedPublicKey.equals(TEST_BLS12381G1G2_PREFIX_BYTES)).toBe(true) + expect(Buffer.from(key.prefixedPublicKey).equals(TEST_BLS12381G1G2_PREFIX_BYTES)).toBe(true) }) it('should return a valid verification method', async () => { @@ -73,7 +73,7 @@ describe('bls12381g1g2', () => { VerificationMethod ) - expect(() => keyDidBls12381g1g2.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + expect(() => keyDidBls12381g1g2.getKeyFromVerificationMethod(verificationMethod)).toThrow( 'Not supported for bls12381g1g2 key' ) }) @@ -86,7 +86,7 @@ describe('bls12381g1g2', () => { expect(g1DidKey.fingerprint).toBe(TEST_BLS12381G1_FINGERPRINT) expect(g1DidKey.publicKeyBase58).toBe(TEST_BLS12381G1_BASE58_KEY) - expect(g1DidKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY)) + expect(g1DidKey.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase58(TEST_BLS12381G1_BASE58_KEY))) expect(g1DidKey.keyType).toBe(KeyType.Bls12381g1) }) @@ -98,7 +98,7 @@ describe('bls12381g1g2', () => { expect(g2DidKey.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) expect(g2DidKey.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) - expect(g2DidKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(g2DidKey.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY))) expect(g2DidKey.keyType).toBe(KeyType.Bls12381g2) }) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts index 14ab8d9fbd..254455e1ae 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/bls12381g2.test.ts @@ -41,9 +41,9 @@ describe('bls12381g2', () => { expect(key.fingerprint).toBe(TEST_BLS12381G2_FINGERPRINT) expect(key.publicKeyBase58).toBe(TEST_BLS12381G2_BASE58_KEY) - expect(key.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY)) + expect(key.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase58(TEST_BLS12381G2_BASE58_KEY))) expect(key.keyType).toBe(KeyType.Bls12381g2) - expect(key.prefixedPublicKey.equals(TEST_BLS12381G2_PREFIX_BYTES)).toBe(true) + expect(Buffer.from(key.prefixedPublicKey).equals(TEST_BLS12381G2_PREFIX_BYTES)).toBe(true) }) it('should return a valid verification method', async () => { diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts index f57600a3c0..cf86cc533d 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/ed25519.test.ts @@ -39,9 +39,9 @@ describe('ed25519', () => { expect(didKey.fingerprint).toBe(TEST_ED25519_FINGERPRINT) expect(didKey.publicKeyBase58).toBe(TEST_ED25519_BASE58_KEY) - expect(didKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_ED25519_BASE58_KEY)) + expect(didKey.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase58(TEST_ED25519_BASE58_KEY))) expect(didKey.keyType).toBe(KeyType.Ed25519) - expect(didKey.prefixedPublicKey.equals(TEST_ED25519_PREFIX_BYTES)).toBe(true) + expect(Buffer.from(didKey.prefixedPublicKey).equals(TEST_ED25519_PREFIX_BYTES)).toBe(true) }) it('should return a valid verification method', async () => { @@ -89,7 +89,7 @@ describe('ed25519', () => { verificationMethod.type = 'SomeRandomType' - expect(() => keyDidEd25519.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + expect(() => keyDidEd25519.getKeyFromVerificationMethod(verificationMethod)).toThrow( "Verification method with type 'SomeRandomType' not supported for key type 'ed25519'" ) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts index aa186aef56..9e529c26b8 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/jwk.test.ts @@ -35,7 +35,7 @@ describe('keyDidJsonWebKey', () => { verificationMethod.type = 'SomeRandomType' - expect(() => keyDidJsonWebKey.getKeyFromVerificationMethod(verificationMethod)).toThrowError( + expect(() => keyDidJsonWebKey.getKeyFromVerificationMethod(verificationMethod)).toThrow( 'Invalid verification method passed' ) }) diff --git a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts index 5fb6490e43..7055e34b32 100644 --- a/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts +++ b/packages/core/src/modules/dids/domain/key-type/__tests__/x25519.test.ts @@ -39,9 +39,9 @@ describe('x25519', () => { expect(didKey.fingerprint).toBe(TEST_X25519_FINGERPRINT) expect(didKey.publicKeyBase58).toBe(TEST_X25519_BASE58_KEY) - expect(didKey.publicKey).toEqual(TypedArrayEncoder.fromBase58(TEST_X25519_BASE58_KEY)) + expect(didKey.publicKey).toEqual(Uint8Array.from(TypedArrayEncoder.fromBase58(TEST_X25519_BASE58_KEY))) expect(didKey.keyType).toBe(KeyType.X25519) - expect(didKey.prefixedPublicKey.equals(TEST_X25519_PREFIX_BYTES)).toBe(true) + expect(Buffer.from(didKey.prefixedPublicKey).equals(TEST_X25519_PREFIX_BYTES)).toBe(true) }) it('should return a valid verification method', async () => { diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 13ad0630a8..18e4a4e0f1 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -1,16 +1,3 @@ -import type { PresentationSignCallBackParams, Validated, VerifiablePresentationResult } from '@animo-id/pex' -import type { InputDescriptorV2 } from '@sphereon/pex-models' -import type { - SdJwtDecodedVerifiableCredential, - W3CVerifiablePresentation as SphereonW3cVerifiablePresentation, - W3CVerifiablePresentation, -} from '@sphereon/ssi-types' -import type { AgentContext } from '../../agent' -import type { Query } from '../../storage/StorageService' -import type { VerificationMethod } from '../dids' -import type { SdJwtVcRecord } from '../sd-jwt-vc' -import type { W3cCredentialRecord, W3cJsonLdVerifiablePresentation } from '../vc' -import type { IAnonCredsDataIntegrityService } from '../vc/data-integrity/models/IAnonCredsDataIntegrityService' import type { DifPexCredentialsForRequest, DifPexInputDescriptorToCredentials, @@ -22,6 +9,19 @@ import type { VerifiablePresentation, } from './models' import type { PresentationToCreate } from './utils' +import type { AgentContext } from '../../agent' +import type { Query } from '../../storage/StorageService' +import type { VerificationMethod } from '../dids' +import type { SdJwtVcRecord } from '../sd-jwt-vc' +import type { W3cCredentialRecord, W3cJsonLdVerifiablePresentation } from '../vc' +import type { IAnonCredsDataIntegrityService } from '../vc/data-integrity/models/IAnonCredsDataIntegrityService' +import type { PresentationSignCallBackParams, Validated, VerifiablePresentationResult } from '@animo-id/pex' +import type { InputDescriptorV2 } from '@sphereon/pex-models' +import type { + SdJwtDecodedVerifiableCredential, + W3CVerifiablePresentation as SphereonW3cVerifiablePresentation, + W3CVerifiablePresentation, +} from '@sphereon/ssi-types' import { PEVersion, PEX, Status } from '@animo-id/pex' import { PartialSdJwtDecodedVerifiableCredential } from '@animo-id/pex/dist/main/lib' @@ -31,7 +31,13 @@ import { Hasher, getJwkFromKey } from '../../crypto' import { CredoError } from '../../error' import { JsonTransformer } from '../../utils' import { DidsApi, getKeyFromVerificationMethod } from '../dids' -import { Mdoc, MdocApi, MdocOpenId4VpSessionTranscriptOptions, MdocRecord } from '../mdoc' +import { + Mdoc, + MdocApi, + MdocOpenId4VpDcApiSessionTranscriptOptions, + MdocOpenId4VpSessionTranscriptOptions, + MdocRecord, +} from '../mdoc' import { MdocDeviceResponse } from '../mdoc/MdocDeviceResponse' import { SdJwtVcApi } from '../sd-jwt-vc' import { @@ -158,7 +164,9 @@ export class DifPresentationExchangeService { presentationSubmissionLocation?: DifPresentationExchangeSubmissionLocation challenge: string domain?: string - openid4vp?: Omit + openid4vp?: + | Omit + | Omit transactionDataAuthorization?: TransactionDataAuthorization } ) { @@ -208,7 +216,7 @@ export class DifPresentationExchangeService { } const { deviceResponseBase64Url, presentationSubmission } = - await MdocDeviceResponse.createOpenId4VpDeviceResponse(agentContext, { + await MdocDeviceResponse.createPresentationDefinitionDeviceResponse(agentContext, { mdocs: [Mdoc.fromBase64Url(mdocRecord.base64Url)], presentationDefinition: presentationDefinition, sessionTranscriptOptions: { diff --git a/packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts b/packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts index 14f66bf398..2a7b571320 100644 --- a/packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts +++ b/packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts @@ -491,7 +491,12 @@ describe('DifPresentationExchangeService', () => { presentationDefinition, domain: 'hello', presentationSubmissionLocation: DifPresentationExchangeSubmissionLocation.EXTERNAL, - openid4vp: { mdocGeneratedNonce: 'something', responseUri: 'https://response.com' }, + openid4vp: { + type: 'openId4Vp', + clientId: 'hello', + mdocGeneratedNonce: 'something', + responseUri: 'https://response.com', + }, }) expect(presentation).toMatchObject({ @@ -631,7 +636,12 @@ describe('DifPresentationExchangeService', () => { presentationDefinition, domain: 'hello', presentationSubmissionLocation: DifPresentationExchangeSubmissionLocation.EXTERNAL, - openid4vp: { mdocGeneratedNonce: 'something', responseUri: 'https://response.com' }, + openid4vp: { + type: 'openId4Vp', + clientId: 'hello', + mdocGeneratedNonce: 'something', + responseUri: 'https://response.com', + }, }) expect(presentation).toMatchObject({ diff --git a/packages/core/src/modules/mdoc/MdocContext.ts b/packages/core/src/modules/mdoc/MdocContext.ts index ca91839fb9..b2e124291f 100644 --- a/packages/core/src/modules/mdoc/MdocContext.ts +++ b/packages/core/src/modules/mdoc/MdocContext.ts @@ -105,7 +105,10 @@ export const getMdocContext = (agentContext: AgentContext): MdocContext => { getCertificateData: async (input) => { const { certificate } = input const x509Certificate = X509Certificate.fromRawCertificate(certificate) - return x509Certificate.getData(crypto) + return { + ...x509Certificate.data, + thumbprint: await x509Certificate.getThumprint(agentContext), + } }, } satisfies X509Context, } diff --git a/packages/core/src/modules/mdoc/MdocDeviceResponse.ts b/packages/core/src/modules/mdoc/MdocDeviceResponse.ts index dd7e2dca7d..49e89e80a7 100644 --- a/packages/core/src/modules/mdoc/MdocDeviceResponse.ts +++ b/packages/core/src/modules/mdoc/MdocDeviceResponse.ts @@ -1,13 +1,13 @@ import type { - MdocDcqlDeviceResponseOpenId4VpOptions, - MdocDeviceResponseOpenId4VpOptions, MdocDeviceResponseOptions, + MdocDeviceResponsePresentationDefinitionOptions, MdocDeviceResponseVerifyOptions, + MdocSessionTranscriptOptions, } from './MdocOptions' import type { AgentContext } from '../../agent' import type { JwkJson } from '../../crypto' import type { DifPresentationExchangeDefinition } from '../dif-presentation-exchange' -import type { IssuerSignedDocument, PresentationDefinition } from '@animo-id/mdoc' +import type { IssuerSignedDocument, MdocContext, PresentationDefinition } from '@animo-id/mdoc' import type { InputDescriptorV2 } from '@sphereon/pex-models' import { @@ -26,6 +26,7 @@ import { import { getJwkFromJson } from '../../crypto' import { CredoError } from '../../error' import { uuid } from '../../utils/uuid' +import { ClaimFormat } from '../vc' import { X509Certificate } from '../x509/X509Certificate' import { X509ModuleConfig } from '../x509/X509ModuleConfig' @@ -34,7 +35,6 @@ import { Mdoc } from './Mdoc' import { getMdocContext } from './MdocContext' import { MdocError } from './MdocError' import { isMdocSupportedSignatureAlgorithm, mdocSupporteSignatureAlgorithms } from './mdocSupportedAlgs' -import { ClaimFormat } from '../vc/index' import { nameSpacesRecordToMap } from './mdocUtil' export class MdocDeviceResponse { @@ -176,8 +176,10 @@ export class MdocDeviceResponse { return disclosedPayloadAsRecord } - static async createOpenId4VpDeviceResponse(agentContext: AgentContext, options: MdocDeviceResponseOpenId4VpOptions) { - const { sessionTranscriptOptions } = options + public static async createPresentationDefinitionDeviceResponse( + agentContext: AgentContext, + options: MdocDeviceResponsePresentationDefinitionOptions + ) { const presentationDefinition = this.partitionPresentationDefinition( options.presentationDefinition ).mdocPresentationDefinition @@ -204,13 +206,14 @@ export class MdocDeviceResponse { const deviceResponseBuilder = DeviceResponse.from(new MDoc([issuerSignedDocument])) .usingPresentationDefinition(presentationDefinitionForDocument) - .usingSessionTranscriptForOID4VP(sessionTranscriptOptions) .authenticateWithSignature(publicDeviceJwk, alg) for (const [nameSpace, nameSpaceValue] of Object.entries(options.deviceNameSpaces ?? {})) { deviceResponseBuilder.addDeviceNameSpace(nameSpace, nameSpaceValue) } + MdocDeviceResponse.usingSessionTranscript(deviceResponseBuilder, options.sessionTranscriptOptions) + const deviceResponseMdoc = await deviceResponseBuilder.sign(getMdocContext(agentContext)) combinedDeviceResponseMdoc.addDocument(deviceResponseMdoc.documents[0]) } @@ -227,57 +230,6 @@ export class MdocDeviceResponse { } } - static async createOpenId4VpDcqlDeviceResponse( - agentContext: AgentContext, - options: MdocDcqlDeviceResponseOpenId4VpOptions - ) { - const { sessionTranscriptOptions } = options - - const issuerSignedDocuments = options.mdocs.map((mdoc) => - parseIssuerSigned(TypedArrayEncoder.fromBase64(mdoc.base64Url), mdoc.docType) - ) - const docTypes = issuerSignedDocuments.map((i) => i.docType) - - const combinedDeviceResponseMdoc = new MDoc() - - for (const issuerSignedDocument of issuerSignedDocuments) { - const { publicDeviceJwk, alg } = this.parseDeviceKeyFromIssuerSigned(issuerSignedDocument) - const deviceKey = issuerSignedDocument.issuerSigned.issuerAuth.decodedPayload.deviceKeyInfo?.deviceKey - if (!deviceKey) throw new MdocError(`Device key is missing in mdoc with doctype ${issuerSignedDocument.docType}`) - - // We do dcql filtering on a different layer, so we only include the needed input descriptor here - const docRequestsForDocument = options.docRequests.filter( - (r) => r.itemsRequestData.docType === issuerSignedDocument.docType - ) - const deviceRequest = DeviceRequest.from( - '1.0', - docRequestsForDocument.map((r) => ({ - ...r, - itemsRequestData: { - ...r.itemsRequestData, - nameSpaces: nameSpacesRecordToMap(r.itemsRequestData.nameSpaces), - }, - })) - ) - - const deviceResponseBuilder = DeviceResponse.from(new MDoc([issuerSignedDocument])) - .usingDeviceRequest(deviceRequest) - .usingSessionTranscriptForOID4VP(sessionTranscriptOptions) - .authenticateWithSignature(publicDeviceJwk, alg) - - for (const [nameSpace, nameSpaceValue] of Object.entries(options.deviceNameSpaces ?? {})) { - deviceResponseBuilder.addDeviceNameSpace(nameSpace, nameSpaceValue) - } - - const deviceResponseMdoc = await deviceResponseBuilder.sign(getMdocContext(agentContext)) - combinedDeviceResponseMdoc.addDocument(deviceResponseMdoc.documents[0]) - } - - return { - deviceResponseBase64Url: TypedArrayEncoder.toBase64URL(combinedDeviceResponseMdoc.encode()), - } - } - public static async createDeviceResponse(agentContext: AgentContext, options: MdocDeviceResponseOptions) { const issuerSignedDocuments = options.mdocs.map((mdoc) => parseIssuerSigned(TypedArrayEncoder.fromBase64(mdoc.base64Url), mdoc.docType) @@ -290,17 +242,23 @@ export class MdocDeviceResponse { const deviceKey = issuerSignedDocument.issuerSigned.issuerAuth.decodedPayload.deviceKeyInfo?.deviceKey if (!deviceKey) throw new CredoError(`Device key is missing in mdoc with doctype ${issuerSignedDocument.docType}`) - const deviceRequestForDocument = new DeviceRequest( - options.deviceRequest.version, - options.deviceRequest.docRequests.filter( - (request) => request.itemsRequest.data.docType === issuerSignedDocument.docType - ) + const deviceRequestForDocument = DeviceRequest.from( + '1.0', + options.documentRequests + .filter((request) => request.docType === issuerSignedDocument.docType) + .map((request) => ({ + itemsRequestData: { + docType: request.docType, + nameSpaces: nameSpacesRecordToMap(request.nameSpaces), + }, + })) ) const deviceResponseBuilder = DeviceResponse.from(new MDoc([issuerSignedDocument])) - .usingSessionTranscriptBytes(options.sessionTranscriptBytes) - .usingDeviceRequest(deviceRequestForDocument) .authenticateWithSignature(publicDeviceJwk, alg) + .usingDeviceRequest(deviceRequestForDocument) + + MdocDeviceResponse.usingSessionTranscript(deviceResponseBuilder, options.sessionTranscriptOptions) for (const [nameSpace, nameSpaceValue] of Object.entries(options.deviceNameSpaces ?? {})) { deviceResponseBuilder.addDeviceNameSpace(nameSpace, nameSpaceValue) @@ -310,9 +268,7 @@ export class MdocDeviceResponse { combinedDeviceResponseMdoc.addDocument(deviceResponseMdoc.documents[0]) } - return { - deviceResponseBase64Url: TypedArrayEncoder.toBase64URL(combinedDeviceResponseMdoc.encode()), - } + return combinedDeviceResponseMdoc.encode() } public async verify(agentContext: AgentContext, options: Omit) { @@ -354,10 +310,10 @@ export class MdocDeviceResponse { const result = await verifier.verifyDeviceResponse( { encodedDeviceResponse: TypedArrayEncoder.fromBase64(this.base64Url), - encodedSessionTranscript: await DeviceResponse.calculateSessionTranscriptForOID4VP({ - ...options.sessionTranscriptOptions, - context: mdocContext, - }), + encodedSessionTranscript: await MdocDeviceResponse.getSessionTranscriptBytesForOptions( + mdocContext, + options.sessionTranscriptOptions + ), trustedCertificates: trustedCertificates.map( (cert) => X509Certificate.fromEncodedCertificate(cert).rawCertificate ), @@ -377,6 +333,47 @@ export class MdocDeviceResponse { return this.documents } + private static async getSessionTranscriptBytesForOptions( + context: MdocContext, + options: MdocSessionTranscriptOptions + ) { + if (options.type === 'sesionTranscriptBytes') { + return options.sessionTranscriptBytes + } + + if (options.type === 'openId4Vp') { + return await DeviceResponse.calculateSessionTranscriptForOID4VP({ + ...options, + context, + }) + } + + if (options.type === 'openId4VpDcApi') { + return await DeviceResponse.calculateSessionTranscriptForOID4VPDCApi({ + ...options, + context, + }) + } + + throw new MdocError('Unsupported session transcript option') + } + + private static usingSessionTranscript(deviceResponse: DeviceResponse, options: MdocSessionTranscriptOptions) { + if (options.type === 'sesionTranscriptBytes') { + return deviceResponse.usingSessionTranscriptBytes(options.sessionTranscriptBytes) + } + + if (options.type === 'openId4Vp') { + return deviceResponse.usingSessionTranscriptForOID4VP(options) + } + + if (options.type === 'openId4VpDcApi') { + return deviceResponse.usingSessionTranscriptForForOID4VPDCApi(options) + } + + throw new MdocError('Unsupported session transcript option') + } + private static parseDeviceKeyFromIssuerSigned(issuerSignedDocument: IssuerSignedDocument) { const deviceKey = issuerSignedDocument.issuerSigned.issuerAuth.decodedPayload.deviceKeyInfo?.deviceKey if (!deviceKey) throw new MdocError(`Device key is missing in mdoc with doctype ${issuerSignedDocument.docType}`) diff --git a/packages/core/src/modules/mdoc/MdocOptions.ts b/packages/core/src/modules/mdoc/MdocOptions.ts index 006507e44e..e1f6198c74 100644 --- a/packages/core/src/modules/mdoc/MdocOptions.ts +++ b/packages/core/src/modules/mdoc/MdocOptions.ts @@ -2,7 +2,7 @@ import type { Mdoc } from './Mdoc' import type { Key } from '../../crypto/Key' import type { DifPresentationExchangeDefinition } from '../dif-presentation-exchange' import type { EncodedX509Certificate } from '../x509' -import type { DeviceRequest, ValidityInfo } from '@animo-id/mdoc' +import type { ValidityInfo } from '@animo-id/mdoc' export { DateOnly } from '@animo-id/mdoc' @@ -14,43 +14,52 @@ export type MdocVerifyOptions = { } export type MdocOpenId4VpSessionTranscriptOptions = { + type: 'openId4Vp' responseUri: string clientId: string verifierGeneratedNonce: string mdocGeneratedNonce: string } -export type MdocDeviceResponseOpenId4VpOptions = { - mdocs: [Mdoc, ...Mdoc[]] - presentationDefinition: DifPresentationExchangeDefinition - deviceNameSpaces?: MdocNameSpaces - sessionTranscriptOptions: MdocOpenId4VpSessionTranscriptOptions +export type MdocSessionTranscriptByteOptions = { + type: 'sesionTranscriptBytes' + sessionTranscriptBytes: Uint8Array +} + +export type MdocOpenId4VpDcApiSessionTranscriptOptions = { + type: 'openId4VpDcApi' + clientId: string + origin: string + verifierGeneratedNonce: string +} + +export type MdocSessionTranscriptOptions = + | MdocOpenId4VpSessionTranscriptOptions + | MdocSessionTranscriptByteOptions + | MdocOpenId4VpDcApiSessionTranscriptOptions + +export type MdocDocumentRequest = { + docType: string + nameSpaces: Record> } export type MdocDeviceResponseOptions = { mdocs: [Mdoc, ...Mdoc[]] - deviceRequest: DeviceRequest + documentRequests: MdocDocumentRequest[] deviceNameSpaces?: MdocNameSpaces - sessionTranscriptBytes: Uint8Array + sessionTranscriptOptions: MdocSessionTranscriptOptions } -export type MdocDocRequest = { - itemsRequestData: { - docType: string - nameSpaces: Record> - } -} - -export type MdocDcqlDeviceResponseOpenId4VpOptions = { +export type MdocDeviceResponsePresentationDefinitionOptions = { mdocs: [Mdoc, ...Mdoc[]] - docRequests: MdocDocRequest[] + presentationDefinition: DifPresentationExchangeDefinition deviceNameSpaces?: MdocNameSpaces - sessionTranscriptOptions: MdocOpenId4VpSessionTranscriptOptions + sessionTranscriptOptions: MdocSessionTranscriptOptions } export type MdocDeviceResponseVerifyOptions = { trustedCertificates?: EncodedX509Certificate[] - sessionTranscriptOptions: MdocOpenId4VpSessionTranscriptOptions + sessionTranscriptOptions: MdocSessionTranscriptOptions /** * The base64Url-encoded device response string. */ diff --git a/packages/core/src/modules/mdoc/MdocService.ts b/packages/core/src/modules/mdoc/MdocService.ts index e092cb6da2..a8513c198b 100644 --- a/packages/core/src/modules/mdoc/MdocService.ts +++ b/packages/core/src/modules/mdoc/MdocService.ts @@ -1,6 +1,6 @@ import type { MdocSignOptions, - MdocDeviceResponseOpenId4VpOptions, + MdocDeviceResponsePresentationDefinitionOptions, MdocDeviceResponseVerifyOptions, MdocVerifyOptions, MdocDeviceResponseOptions, @@ -42,8 +42,11 @@ export class MdocService { return MdocDeviceResponse.createDeviceResponse(agentContext, options) } - public async createOpenId4VpDeviceResponse(agentContext: AgentContext, options: MdocDeviceResponseOpenId4VpOptions) { - return MdocDeviceResponse.createOpenId4VpDeviceResponse(agentContext, options) + public async createPresentationDefinitionDeviceResponse( + agentContext: AgentContext, + options: MdocDeviceResponsePresentationDefinitionOptions + ) { + return MdocDeviceResponse.createPresentationDefinitionDeviceResponse(agentContext, options) } public async verifyDeviceResponse(agentContext: AgentContext, options: MdocDeviceResponseVerifyOptions) { diff --git a/packages/core/src/modules/mdoc/__tests__/mdocOpenId4VcDeviceResponse.test.ts b/packages/core/src/modules/mdoc/__tests__/mdocOpenId4VcDeviceResponse.test.ts index 929ae180a1..20ab260f19 100644 --- a/packages/core/src/modules/mdoc/__tests__/mdocOpenId4VcDeviceResponse.test.ts +++ b/packages/core/src/modules/mdoc/__tests__/mdocOpenId4VcDeviceResponse.test.ts @@ -2,7 +2,7 @@ import type { DifPresentationExchangeDefinition } from '../../dif-presentation-e import { cborEncode, parseDeviceResponse } from '@animo-id/mdoc' // eslint-disable-next-line import/no-extraneous-dependencies -import { Key as AskarKey, Jwk } from '@hyperledger/aries-askar-nodejs' +import { Key as AskarKey, Jwk } from '@openwallet-foundation/askar-nodejs' import { getInMemoryAgentOptions } from '../../../../tests' import { Agent } from '../../../agent/Agent' @@ -197,10 +197,11 @@ describe('mdoc device-response openid4vp test', () => { // This is the Device side { - const result = await MdocDeviceResponse.createOpenId4VpDeviceResponse(agent.context, { + const result = await MdocDeviceResponse.createPresentationDefinitionDeviceResponse(agent.context, { mdocs: [mdoc], presentationDefinition: PRESENTATION_DEFINITION_1, sessionTranscriptOptions: { + type: 'openId4Vp', clientId, responseUri, verifierGeneratedNonce, @@ -232,6 +233,7 @@ describe('mdoc device-response openid4vp test', () => { const res = await mdocDeviceResponse.verify(agent.context, { trustedCertificates: [ISSUER_CERTIFICATE_P256], sessionTranscriptOptions: { + type: 'openId4Vp', clientId, responseUri, verifierGeneratedNonce, @@ -258,6 +260,7 @@ describe('mdoc device-response openid4vp test', () => { await mdocDeviceResponse.verify(agent.context, { trustedCertificates: [ISSUER_CERTIFICATE_P256], sessionTranscriptOptions: { + type: 'openId4Vp', clientId: values.clientId, responseUri: values.responseUri, verifierGeneratedNonce: values.verifierGeneratedNonce, @@ -357,10 +360,11 @@ describe('mdoc device-response openid4vp test', () => { // This is the Device side - const result = await MdocDeviceResponse.createOpenId4VpDeviceResponse(agent.context, { + const result = await MdocDeviceResponse.createPresentationDefinitionDeviceResponse(agent.context, { mdocs: [mdoc], presentationDefinition: PRESENTATION_DEFINITION_1, sessionTranscriptOptions: { + type: 'openId4Vp', clientId, responseUri, verifierGeneratedNonce, @@ -389,6 +393,7 @@ describe('mdoc device-response openid4vp test', () => { await MdocDeviceResponse.fromBase64Url(result.deviceResponseBase64Url).verify(agent.context, { trustedCertificates: [issuerCertificate.toString('pem')], sessionTranscriptOptions: { + type: 'openId4Vp', clientId, responseUri, verifierGeneratedNonce, diff --git a/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts b/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts index c7295a2f09..fedb6e42a2 100644 --- a/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts +++ b/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts @@ -1,6 +1,6 @@ import { cborEncode, DeviceRequest, parseDeviceResponse } from '@animo-id/mdoc' // eslint-disable-next-line import/no-extraneous-dependencies -import { Key as AskarKey, Jwk } from '@hyperledger/aries-askar-nodejs' +import { Key as AskarKey, Jwk } from '@openwallet-foundation/askar-nodejs' import { Agent, KeyType } from '../../..' import { getInMemoryAgentOptions } from '../../../../tests' @@ -8,6 +8,7 @@ import { getJwkFromJson } from '../../../crypto/jose/jwk/transform' import { Buffer, TypedArrayEncoder } from '../../../utils' import { Mdoc } from '../Mdoc' import { MdocDeviceResponse } from '../MdocDeviceResponse' +import { namespacesMapToRecord } from '../mdocUtil' const DEVICE_JWK_PUBLIC = { kty: 'EC', @@ -74,7 +75,6 @@ const DEVICE_REQUEST_1 = DeviceRequest.from('1.0', [ ]) describe('mdoc device-response proximity test', () => { - let deviceResponse: string let mdoc: Mdoc let parsedDocument: Mdoc let agent: Agent @@ -145,16 +145,23 @@ describe('mdoc device-response proximity test', () => { { const result = await MdocDeviceResponse.createDeviceResponse(agent.context, { mdocs: [mdoc], - deviceRequest: DEVICE_REQUEST_1, - // TODO: we should also add methods for Credo to create device requests with session transcript bytes. - sessionTranscriptBytes: cborEncode(new Uint8Array([1, 2, 3])), + + documentRequests: DEVICE_REQUEST_1.docRequests.map((v) => { + return { + docType: v.itemsRequest.data.docType, + nameSpaces: namespacesMapToRecord(v.itemsRequest.data.nameSpaces), + } + }), + sessionTranscriptOptions: { + type: 'sesionTranscriptBytes', + sessionTranscriptBytes: cborEncode(new Uint8Array([1, 2, 3])), + }, deviceNameSpaces: { 'com.foobar-device': { test: 1234 }, }, }) - deviceResponse = result.deviceResponseBase64Url - const parsed = parseDeviceResponse(TypedArrayEncoder.fromBase64(deviceResponse)) + const parsed = parseDeviceResponse(result) expect(parsed.documents).toHaveLength(1) const prepared = parsed.documents[0].prepare() diff --git a/packages/core/src/modules/mdoc/index.ts b/packages/core/src/modules/mdoc/index.ts index d482d82665..da856176f0 100644 --- a/packages/core/src/modules/mdoc/index.ts +++ b/packages/core/src/modules/mdoc/index.ts @@ -1,3 +1,5 @@ +export { DateOnly } from '@animo-id/mdoc' + export * from './MdocApi' export * from './MdocModule' export * from './MdocService' diff --git a/packages/core/src/modules/mdoc/mdocUtil.ts b/packages/core/src/modules/mdoc/mdocUtil.ts index 58999f0f4f..4c3e8d564a 100644 --- a/packages/core/src/modules/mdoc/mdocUtil.ts +++ b/packages/core/src/modules/mdoc/mdocUtil.ts @@ -5,3 +5,13 @@ export function nameSpacesRecordToMap< >(nameSpaces: NameSpaces): Map> { return new Map(Object.entries(nameSpaces).map(([key, value]) => [key, new Map(Object.entries(value))] as const)) } + +export function namespacesMapToRecord< + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint + NamespaceValue extends unknown, + NameSpaces extends Map> +>(nameSpaces: NameSpaces): Record> { + return Object.fromEntries( + Array.from(nameSpaces.entries()).map(([key, value]) => [key, Object.fromEntries(Array.from(value.entries()))]) + ) +} diff --git a/packages/core/src/modules/x509/X509Certificate.ts b/packages/core/src/modules/x509/X509Certificate.ts index 8cae40538a..7a5a07191e 100644 --- a/packages/core/src/modules/x509/X509Certificate.ts +++ b/packages/core/src/modules/x509/X509Certificate.ts @@ -1,5 +1,5 @@ import type { X509CreateSelfSignedCertificateOptions } from './X509ServiceOptions' -import type { CredoWebCrypto } from '../../crypto/webcrypto' +import type { AgentContext } from '../../agent' import { AsnParser } from '@peculiar/asn1-schema' import { @@ -12,9 +12,7 @@ import { import * as x509 from '@peculiar/x509' import { Key } from '../../crypto/Key' -import { KeyType } from '../../crypto/KeyType' -import { compress } from '../../crypto/jose/jwk/ecCompression' -import { CredoWebCryptoKey } from '../../crypto/webcrypto' +import { CredoWebCrypto, CredoWebCryptoKey } from '../../crypto/webcrypto' import { credoKeyTypeIntoCryptoKeyAlgorithm, spkiAlgorithmIntoCredoKeyType } from '../../crypto/webcrypto/utils' import { TypedArrayEncoder } from '../../utils' @@ -85,19 +83,9 @@ export class X509Certificate { const privateKey = certificate.privateKey ? new Uint8Array(certificate.privateKey.rawData) : undefined const keyType = spkiAlgorithmIntoCredoKeyType(publicKey.algorithm) + const publicKeyBytes = new Uint8Array(publicKey.subjectPublicKey) - // TODO(crypto): Currently this only does point-compression for P256. - // We should either store all keys as uncompressed, or we should compress all supported keys here correctly - let keyBytes = new Uint8Array(publicKey.subjectPublicKey) - if (publicKey.subjectPublicKey.byteLength === 65 && keyType === KeyType.P256) { - if (keyBytes[0] !== 0x04) { - throw new X509Error('Received P256 key with 65 bytes, but key did not start with 0x04. Invalid key') - } - // TODO(crypto): the compress method is bugged because it does not expect the required `0x04` prefix. Here we strip that and receive the expected result - keyBytes = compress(keyBytes.slice(1)) - } - - const key = new Key(keyBytes, keyType) + const key = new Key(publicKeyBytes, keyType) const extensions = certificate.extensions .map((e) => { @@ -271,16 +259,30 @@ export class X509Certificate { } } - public async getData(crypto?: CredoWebCrypto) { + /** + * Get the thumprint of the X509 certificate in hex format. + */ + public async getThumprint(agentContext: AgentContext) { const certificate = new x509.X509Certificate(this.rawCertificate) - const thumbprint = await certificate.getThumbprint(crypto) + const thumbprint = await certificate.getThumbprint(new CredoWebCrypto(agentContext)) const thumbprintHex = TypedArrayEncoder.toHex(new Uint8Array(thumbprint)) + + return thumbprintHex + } + + /** + * Get the data elements of the x509 certificate + */ + public get data() { + const certificate = new x509.X509Certificate(this.rawCertificate) + return { issuerName: certificate.issuerName.toString(), + issuer: certificate.issuer, subjectName: certificate.subjectName.toString(), + subject: certificate.subject, serialNumber: certificate.serialNumber, - thumbprint: thumbprintHex, pem: certificate.toString(), notBefore: certificate.notBefore, notAfter: certificate.notAfter, diff --git a/packages/core/src/modules/x509/__tests__/X509Service.test.ts b/packages/core/src/modules/x509/__tests__/X509Service.test.ts index f47e37c6f3..fea2fa6c4e 100644 --- a/packages/core/src/modules/x509/__tests__/X509Service.test.ts +++ b/packages/core/src/modules/x509/__tests__/X509Service.test.ts @@ -128,13 +128,16 @@ describe('X509Service', () => { const x509Certificate = X509Service.parseCertificate(agentContext, { encodedCertificate }) - expect(x509Certificate.publicKey.publicKey.length).toStrictEqual(33) - expect(x509Certificate.publicKey.publicKeyBase58).toStrictEqual('23vfBuUJkWXTC3zZWMh1TR57CTubFjFfhm4CGfgszRMHU') + expect(x509Certificate.publicKey.keyType).toStrictEqual(KeyType.P256) + expect(x509Certificate.publicKey.publicKey.length).toStrictEqual(65) + expect(x509Certificate.publicKey.publicKeyBase58).toStrictEqual( + 'QDaLvg9KroUnpuviZ9W7Q3DauqAuKiJN4sKC6cLo4HtxnpJCwwayNBLzRpsCHfHsLJsiKDeTCV8LqmCBSPkmiJNe' + ) const jwk = getJwkFromKey(x509Certificate.publicKey) expect(jwk).toBeInstanceOf(P256Jwk) - expect(jwk).toMatchObject({ + expect(jwk.toJson()).toMatchObject({ x: 'iTwtg0eQbcbNabf2Nq9L_VM_lhhPCq2s0Qgw2kRx29s', y: 'YKwXDRz8U0-uLZ3NSI93R_35eNkl6jHp6Qg8OCup7VM', }) @@ -164,7 +167,7 @@ describe('X509Service', () => { sanUriNames: expect.arrayContaining(['animo.id']), }) - expect(x509Certificate.publicKey.publicKey.length).toStrictEqual(33) + expect(x509Certificate.publicKey.publicKey.length).toStrictEqual(65) }) it('should correctly parse x5c chain provided as a test-vector', async () => { @@ -206,7 +209,7 @@ describe('X509Service', () => { keyUsage: [KeyUsage.DigitalSignature, KeyUsage.KeyCertSign], }) - expect(x509Certificate.publicKey.publicKey.length).toStrictEqual(33) + expect(x509Certificate.publicKey.publicKey.length).toStrictEqual(65) }) it('should validate a valid certificate chain', async () => { diff --git a/packages/core/src/wallet/Wallet.ts b/packages/core/src/wallet/Wallet.ts index aebe9c592b..dcd2a46863 100644 --- a/packages/core/src/wallet/Wallet.ts +++ b/packages/core/src/wallet/Wallet.ts @@ -122,7 +122,7 @@ export interface UnpackedMessageContext { export interface WalletDirectEncryptCompactJwtEcdhEsOptions { recipientKey: Key - encryptionAlgorithm: 'A256GCM' + encryptionAlgorithm: 'A256GCM' | 'A128CBC-HS256' apu?: string apv?: string data: Buffer diff --git a/packages/didcomm/src/decorators/transport/TransportDecorator.test.ts b/packages/didcomm/src/decorators/transport/TransportDecorator.test.ts index 5c2956f6f8..f0f587ed6c 100644 --- a/packages/didcomm/src/decorators/transport/TransportDecorator.test.ts +++ b/packages/didcomm/src/decorators/transport/TransportDecorator.test.ts @@ -6,7 +6,7 @@ const validTransport = (transportJson: Record) => MessageValidator.validateSync(JsonTransformer.fromJSON(transportJson, TransportDecorator)) const expectValid = (transportJson: Record) => expect(validTransport(transportJson)).toBeUndefined() const expectInvalid = (transportJson: Record) => - expect(() => validTransport(transportJson)).toThrowError(ClassValidationError) + expect(() => validTransport(transportJson)).toThrow(ClassValidationError) const valid = { all: { diff --git a/packages/didcomm/src/modules/routing/services/__tests__/MediationRecipientService.test.ts b/packages/didcomm/src/modules/routing/services/__tests__/MediationRecipientService.test.ts index 20075ad282..611f62cd11 100644 --- a/packages/didcomm/src/modules/routing/services/__tests__/MediationRecipientService.test.ts +++ b/packages/didcomm/src/modules/routing/services/__tests__/MediationRecipientService.test.ts @@ -223,7 +223,7 @@ describe('MediationRecipientService', () => { expect(extendedRouting).toMatchObject({ endpoints: ['https://a-mediator-endpoint.com'], - routingKeys: [routingKey], + routingKeys: routing.routingKeys, }) expect(mediationRepository.findSingleByQuery).toHaveBeenCalledWith(agentContext, { default: true }) }) diff --git a/packages/indy-sdk-to-askar-migration/package.json b/packages/indy-sdk-to-askar-migration/package.json index d2a7da69d7..4b81f5ad46 100644 --- a/packages/indy-sdk-to-askar-migration/package.json +++ b/packages/indy-sdk-to-askar-migration/package.json @@ -32,12 +32,12 @@ "@credo-ts/node": "workspace:*" }, "devDependencies": { - "@hyperledger/aries-askar-nodejs": "^0.2.3", - "@hyperledger/aries-askar-shared": "^0.2.3", + "@openwallet-foundation/askar-nodejs": "^0.3.1", + "@openwallet-foundation/askar-shared": "^0.3.1", "rimraf": "^4.4.0", "typescript": "~5.5.2" }, "peerDependencies": { - "@hyperledger/aries-askar-shared": "^0.2.3" + "@openwallet-foundation/askar-shared": "^0.3.1" } } diff --git a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts index 3d9f51aced..191d86af5c 100644 --- a/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts +++ b/packages/indy-sdk-to-askar-migration/src/IndySdkToAskarMigrationUpdater.ts @@ -1,11 +1,11 @@ import type { AnonCredsCredentialValue } from '@credo-ts/anoncreds' import type { Agent, FileSystem, WalletConfig } from '@credo-ts/core' -import type { EntryObject } from '@hyperledger/aries-askar-shared' +import type { EntryObject } from '@openwallet-foundation/askar-shared' import { AnonCredsCredentialRecord, AnonCredsLinkSecretRecord } from '@credo-ts/anoncreds' import { AskarWallet } from '@credo-ts/askar' import { InjectionSymbols, KeyDerivationMethod, JsonTransformer, TypedArrayEncoder } from '@credo-ts/core' -import { Migration, Key, KeyAlgs, Store } from '@hyperledger/aries-askar-shared' +import { Migration, Key, KeyAlgorithm, Store } from '@openwallet-foundation/askar-shared' import { IndySdkToAskarMigrationError } from './errors/IndySdkToAskarMigrationError' import { keyDerivationMethodToStoreKeyMethod, transformFromRecordTagValues } from './utils' @@ -267,7 +267,7 @@ export class IndySdkToAskarMigrationUpdater { const signKey: string = JSON.parse(row.value as string).signkey const keySk = TypedArrayEncoder.fromBase58(signKey) const key = Key.fromSecretBytes({ - algorithm: KeyAlgs.Ed25519, + algorithm: KeyAlgorithm.Ed25519, secretKey: new Uint8Array(keySk.slice(0, 32)), }) await txn.insertKey({ name: row.name, key }) diff --git a/packages/indy-sdk-to-askar-migration/src/utils.ts b/packages/indy-sdk-to-askar-migration/src/utils.ts index b1dff7343d..ed6949f088 100644 --- a/packages/indy-sdk-to-askar-migration/src/utils.ts +++ b/packages/indy-sdk-to-askar-migration/src/utils.ts @@ -1,7 +1,7 @@ import type { TagsBase } from '@credo-ts/core' import { KeyDerivationMethod } from '@credo-ts/core' -import { KdfMethod, StoreKeyMethod } from '@hyperledger/aries-askar-shared' +import { KdfMethod, StoreKeyMethod } from '@openwallet-foundation/askar-shared' /** * Adopted from `AskarStorageService` implementation and should be kept in sync. diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index c951083e18..03e41fdae0 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -33,8 +33,8 @@ "class-transformer": "^0.5.1", "rxjs": "^7.8.0", "zod": "^3.23.8", - "@animo-id/oid4vci": "link:../../../Code/oid4vc-ts/packages/oid4vci", - "@animo-id/oauth2": "link:../../../Code/oid4vc-ts/packages/oauth2", + "@openid4vc/oid4vci": "link:../../../Code/oid4vc-ts/packages/oid4vci", + "@openid4vc/oauth2": "link:../../../Code/oid4vc-ts/packages/oauth2", "@openid4vc/oid4vp": "link:../../../Code/oid4vc-ts/packages/openid4vp" }, "devDependencies": { diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts index c27ee7ee3c..ead15caddf 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts @@ -7,7 +7,10 @@ import type { OpenId4VciRetrieveAuthorizationCodeUsingPresentationOptions, OpenId4VciSendNotificationOptions, } from './OpenId4VciHolderServiceOptions' -import type { OpenId4VcSiopAcceptAuthorizationRequestOptions } from './OpenId4vcSiopHolderServiceOptions' +import type { + OpenId4VcSiopAcceptAuthorizationRequestOptions, + ResolveSiopAuthorizationRequestOptions, +} from './OpenId4vcSiopHolderServiceOptions' import { AgentContext, @@ -48,11 +51,18 @@ export class OpenId4VcHolderApi { * incoming request. When `presentationExchange` is present, you MUST supply `presentationExchange` * when calling `acceptSiopAuthorizationRequest` as well. * - * @param requestJwtOrUri JWT or an SIOPv2 request URI + * @param request + * Can be: + * - JWT + * - URI containing request or request_uri param + * - Request payload * @returns the resolved and verified authentication request. */ - public async resolveSiopAuthorizationRequest(requestJwtOrUri: string, origin?: string) { - return this.openId4VcSiopHolderService.resolveAuthorizationRequest(this.agentContext, requestJwtOrUri, undefined, origin) + public async resolveSiopAuthorizationRequest( + request: string | Record, + options?: ResolveSiopAuthorizationRequestOptions + ) { + return this.openId4VcSiopHolderService.resolveAuthorizationRequest(this.agentContext, request, options) } /** diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts index d0dd853316..6bdae58078 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts @@ -162,7 +162,7 @@ export interface OpenId4VciAcceptCredentialOfferOptions { credentialConfigurationIds?: string[] /** - * Whether to request a batch of credentials if supported by the crednetial issuer. + * Whether to request a batch of credentials if supported by the credential issuer. * * You can also provide a number to indicate the batch size. If `true` is provided * the max size from the credential issuer will be used. diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts index 29a2f5d876..e9d8646ab0 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts @@ -1,3 +1,9 @@ +import type { + OpenId4VcSiopAcceptAuthorizationRequestOptions, + OpenId4VcSiopResolvedAuthorizationRequest, + ResolveSiopAuthorizationRequestOptions, +} from './OpenId4vcSiopHolderServiceOptions' +import type { OpenId4VcJwtIssuer } from '../shared' import type { AgentContext, DcqlCredentialsForRequest, @@ -12,12 +18,6 @@ import type { TransactionDataRequest, VerifiablePresentation, } from '@credo-ts/core' -import { isJarmResponseMode, isOpenid4vpAuthorizationRequestDcApi, Oid4vpClient, Openid4vpAuthorizationResponseDcApi } from '@openid4vc/oid4vp' -import type { OpenId4VcJwtIssuer } from '../shared' -import type { - OpenId4VcSiopAcceptAuthorizationRequestOptions, - OpenId4VcSiopResolvedAuthorizationRequest, -} from './OpenId4vcSiopHolderServiceOptions' import { asArray, @@ -30,6 +30,7 @@ import { W3cJsonLdVerifiablePresentation, W3cJwtVerifiablePresentation, } from '@credo-ts/core' +import { isJarmResponseMode, isOpenid4vpAuthorizationRequestDcApi, Oid4vpClient } from '@openid4vc/oid4vp' import { getOid4vcCallbacks } from '../shared/callbacks' import { openIdTokenIssuerToJwtIssuer } from '../shared/utils' @@ -129,17 +130,30 @@ export class OpenId4VcSiopHolderService { public async resolveAuthorizationRequest( agentContext: AgentContext, - requestJwtOrUri: string, - trustedCertificates?: EncodedX509Certificate[], - origin?: string + /** + * Can be: + * - JWT + * - URI containing request or request_uri param + * - Request payload + */ + request: string | Record, + options?: ResolveSiopAuthorizationRequestOptions ): Promise { - const openid4vpClient = this.getOid4vpClient(agentContext, trustedCertificates) - const { params } = openid4vpClient.parseOpenid4vpAuthorizationRequestPayload({ requestPayload: requestJwtOrUri }) - const verifiedAuthRequest = await openid4vpClient.resolveOpenId4vpAuthorizationRequest({ request: params, origin }) + const openid4vpClient = this.getOid4vpClient(agentContext, options?.trustedCertificates) + const { params } = openid4vpClient.parseOpenid4vpAuthorizationRequestPayload({ requestPayload: request }) + const verifiedAuthRequest = await openid4vpClient.resolveOpenId4vpAuthorizationRequest({ + request: params, + origin: options?.origin, + }) const { client, pex, transactionData, dcql } = verifiedAuthRequest - if (client.scheme !== 'x509_san_dns' && client.scheme !== 'x509_san_uri' && client.scheme !== 'did' && client.scheme !== 'web-origin') { + if ( + client.scheme !== 'x509_san_dns' && + client.scheme !== 'x509_san_uri' && + client.scheme !== 'did' && + client.scheme !== 'web-origin' + ) { throw new CredoError(`Client scheme '${client.scheme}' is not supported`) } @@ -151,8 +165,8 @@ export class OpenId4VcSiopHolderService { ? await this.handleDcqlRequest(agentContext, dcql.query, transactionData) : { dcql: undefined } - agentContext.config.logger.debug(`verified SIOP Authorization Request`) - agentContext.config.logger.debug(`requestJwtOrUri '${requestJwtOrUri}'`) + agentContext.config.logger.debug(`verified Authorization Request`) + agentContext.config.logger.debug(`request '${request}'`) return { authorizationRequest: verifiedAuthRequest, @@ -215,7 +229,7 @@ export class OpenId4VcSiopHolderService { agentContext: AgentContext, options: OpenId4VcSiopAcceptAuthorizationRequestOptions ) { - const { authorizationRequest, presentationExchange, dcql } = options + const { authorizationRequest, presentationExchange, dcql, origin } = options let openIdTokenIssuer = options.openIdTokenIssuer let presentationExchangeOptions: | { @@ -232,9 +246,20 @@ export class OpenId4VcSiopHolderService { } | undefined = undefined + const isDcApiRequest = + authorizationRequest.payload.response_mode === 'dc_api' || + authorizationRequest.payload.response_mode === 'dc_api.jwt' const nonce = authorizationRequest.payload.nonce - const clientId = authorizationRequest.payload.client_id + // FIXME: we should always set this in oid4vc-ts, based on the origin if using DC API + let clientId = authorizationRequest.client.identifier + if (!clientId && isDcApiRequest) { + if (!origin) throw new CredoError('Missing required origin') + + clientId = `web-origin:${origin}` + } else if (!clientId) { + throw new CredoError('Could not extract client identifier') + } let responseUri: string if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequest.payload)) { @@ -246,7 +271,9 @@ export class OpenId4VcSiopHolderService { } else { const _responseUri = authorizationRequest.payload.response_uri ?? authorizationRequest.payload.redirect_uri if (!_responseUri) { - throw new CredoError('Missing required parameter `response_uri` or `redirect_uri` in the authorization request.') + throw new CredoError( + 'Missing required parameter `response_uri` or `redirect_uri` in the authorization request.' + ) } responseUri = _responseUri } @@ -285,7 +312,10 @@ export class OpenId4VcSiopHolderService { challenge: nonce, domain: clientId, presentationSubmissionLocation: DifPresentationExchangeSubmissionLocation.EXTERNAL, - openid4vp: { mdocGeneratedNonce: authorizationResponseNonce, responseUri }, + openid4vp: + isDcApiRequest && origin + ? { type: 'openId4VpDcApi', clientId, origin } + : { type: 'openId4Vp', mdocGeneratedNonce: authorizationResponseNonce, responseUri, clientId }, }) presentationExchangeOptions = { verifiablePresentations, encodedVerifiablePresentations, presentationSubmission } @@ -321,7 +351,10 @@ export class OpenId4VcSiopHolderService { : undefined, challenge: nonce, domain: clientId, - openid4vp: { mdocGeneratedNonce: authorizationResponseNonce, responseUri }, + openid4vp: + isDcApiRequest && origin + ? { type: 'openId4VpDcApi', clientId, origin } + : { type: 'openId4Vp', mdocGeneratedNonce: authorizationResponseNonce, responseUri, clientId }, }) dcqlOptions = { @@ -380,17 +413,18 @@ export class OpenId4VcSiopHolderService { vp_token: vpToken! as any, presentation_submission: presentationExchangeOptions?.presentationSubmission, }, - jarm: authorizationRequest.payload.response_mode && isJarmResponseMode(authorizationRequest.payload.response_mode) - ? { - jwtSigner: jwtIssuer!, - encryption: { nonce: authorizationResponseNonce }, - serverMetadata: { - authorization_signing_alg_values_supported: ['RS256'], - authorization_encryption_alg_values_supported: ['ECDH-ES'], - authorization_encryption_enc_values_supported: ['A256GCM'], - }, - } - : undefined, + jarm: + authorizationRequest.payload.response_mode && isJarmResponseMode(authorizationRequest.payload.response_mode) + ? { + jwtSigner: jwtIssuer!, + encryption: { nonce: authorizationResponseNonce }, + serverMetadata: { + authorization_signing_alg_values_supported: ['RS256'], + authorization_encryption_alg_values_supported: ['ECDH-ES'], + authorization_encryption_enc_values_supported: ['A256GCM'], + }, + } + : undefined, }) if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequest.payload)) { diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts index b0c5645455..b103ce2052 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderServiceOptions.ts @@ -1,3 +1,4 @@ +import type { OpenId4VcJwtIssuer, OpenId4VcSiopVerifiedAuthorizationRequest } from '../shared' import type { DcqlCredentialsForRequest, DcqlQueryResult, @@ -5,9 +6,14 @@ import type { DifPexCredentialsForRequest, DifPexInputDescriptorToCredentials, DifPresentationExchangeDefinition, + EncodedX509Certificate, TransactionDataRequest, } from '@credo-ts/core' -import type { OpenId4VcJwtIssuer, OpenId4VcSiopVerifiedAuthorizationRequest } from '../shared' + +export interface ResolveSiopAuthorizationRequestOptions { + trustedCertificates?: EncodedX509Certificate[] + origin?: string +} export interface OpenId4VcSiopResolvedAuthorizationRequest { /** diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts index 772e24041c..444edcea80 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerApi.ts @@ -69,8 +69,8 @@ export class OpenId4VcIssuerApi { } /** - * Creates a stateless credential offer. This can only be used with an external authorization server, as credo only supports statefull - * crednetial offers. + * Creates a stateless credential offer. This can only be used with an external authorization server, as credo only supports stateful + * credential offers. */ public async createStatelessCredentialOffer( options: OpenId4VciCreateStatelessCredentialOfferOptions & { issuerId: string } diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts index cea815a19f..6c2f39e083 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerModuleConfig.ts @@ -9,7 +9,7 @@ import { importExpress } from '../shared/router' const DEFAULT_C_NONCE_EXPIRES_IN = 1 * 60 // 1 minute const DEFAULT_AUTHORIZATION_CODE_EXPIRES_IN = 1 * 60 // 1 minute const DEFAULT_TOKEN_EXPIRES_IN = 3 * 60 // 3 minutes -const DEFAULT_STATEFULL_CREDENTIAL_OFFER_EXPIRES_IN = 3 * 60 // 3 minutes +const DEFAULT_STATEFUL_CREDENTIAL_OFFER_EXPIRES_IN = 3 * 60 // 3 minutes export interface OpenId4VcIssuerModuleConfigOptions { /** @@ -35,13 +35,13 @@ export interface OpenId4VcIssuerModuleConfigOptions { cNonceExpiresInSeconds?: number /** - * The time after which a statefull credential offer not bound to a subject expires. Once the offer has been bound + * The time after which a stateful credential offer not bound to a subject expires. Once the offer has been bound * to a subject the access token expiration takes effect. This is to prevent long-lived `pre-authorized_code` and * `issuer_state` values. * * @default 180 (3 minutes) */ - statefullCredentialOfferExpirationInSeconds?: number + statefulCredentialOfferExpirationInSeconds?: number /** * The time after which an authorization code will expire. @@ -72,7 +72,7 @@ export interface OpenId4VcIssuerModuleConfigOptions { * This requires an external authorization server which issues access tokens without * a `pre-authorized_code` or `issuer_state` parameter. * - * Credo only support statefull crednetial offer sessions (pre-auth or presentation during issuance) + * Credo only support stateful credential offer sessions (pre-auth or presentation during issuance) * * @default false */ @@ -170,14 +170,14 @@ export class OpenId4VcIssuerModuleConfig { } /** - * The time after which a statefull credential offer not bound to a subject expires. Once the offer has been bound + * The time after which a stateful credential offer not bound to a subject expires. Once the offer has been bound * to a subject the access token expiration takes effect. This is to prevent long-lived `pre-authorized_code` and * `issuer_state` values. * * @default 360 (5 minutes) */ - public get statefullCredentialOfferExpirationInSeconds(): number { - return this.options.statefullCredentialOfferExpirationInSeconds ?? DEFAULT_STATEFULL_CREDENTIAL_OFFER_EXPIRES_IN + public get statefulCredentialOfferExpirationInSeconds(): number { + return this.options.statefulCredentialOfferExpirationInSeconds ?? DEFAULT_STATEFUL_CREDENTIAL_OFFER_EXPIRES_IN } /** @@ -215,7 +215,7 @@ export class OpenId4VcIssuerModuleConfig { * This requires an external authorization server which issues access tokens without * a `pre-authorized_code` or `issuer_state` parameter. * - * Credo only supports statefull crednetial offer sessions (pre-auth or presentation during issuance) + * Credo only supports stateful credential offer sessions (pre-auth or presentation during issuance) * * @default false */ diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts index 93908fde16..910e393854 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -109,7 +109,7 @@ export class OpenId4VcIssuerService { } // Check if all the offered credential configuration ids have a scope value. If not, it won't be possible to actually request - // issuance of the crednetial later on + // issuance of the credential later on extractScopesForCredentialConfigurationIds({ credentialConfigurationIds: options.offeredCredentials, issuerMetadata, @@ -174,7 +174,7 @@ export class OpenId4VcIssuerService { ]) // Check if all the offered credential configuration ids have a scope value. If not, it won't be possible to actually request - // issuance of the crednetial later on. For pre-auth it's not needed to add a scope. + // issuance of the credential later on. For pre-auth it's not needed to add a scope. if (options.authorizationCodeFlowConfig) { extractScopesForCredentialConfigurationIds({ credentialConfigurationIds: options.offeredCredentials, diff --git a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts index f4a6a9de16..4818e49181 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/accessTokenEndpoint.ts @@ -69,7 +69,7 @@ export function handleTokenRequest(config: OpenId4VcIssuerModuleConfig) { if ( Date.now() > - addSecondsToDate(issuanceSession.createdAt, config.statefullCredentialOfferExpirationInSeconds).getTime() + addSecondsToDate(issuanceSession.createdAt, config.statefulCredentialOfferExpirationInSeconds).getTime() ) { issuanceSession.errorMessage = 'Credential offer has expired' await openId4VcIssuerService.updateState(agentContext, issuanceSession, OpenId4VcIssuanceSessionState.Error) @@ -110,7 +110,7 @@ export function handleTokenRequest(config: OpenId4VcIssuerModuleConfig) { expectedTxCode: issuanceSession.userPin, preAuthorizedCodeExpiresAt: addSecondsToDate( issuanceSession.createdAt, - config.statefullCredentialOfferExpirationInSeconds + config.statefulCredentialOfferExpirationInSeconds ), }) } else if (grant.grantType === authorizationCodeGrantIdentifier) { diff --git a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts index 3bdec853ce..b5fbf7dea2 100644 --- a/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts +++ b/packages/openid4vc/src/openid4vc-issuer/router/credentialEndpoint.ts @@ -151,10 +151,10 @@ export function configureCredentialEndpoint(router: Router, config: OpenId4VcIss ) } } - // Statefull session expired + // Stateful session expired else if ( Date.now() > - addSecondsToDate(issuanceSession.createdAt, config.statefullCredentialOfferExpirationInSeconds).getTime() + addSecondsToDate(issuanceSession.createdAt, config.statefulCredentialOfferExpirationInSeconds).getTime() ) { issuanceSession.errorMessage = 'Credential offer has expired' await openId4VcIssuerService.updateState(agentContext, issuanceSession, OpenId4VcIssuanceSessionState.Error) @@ -233,7 +233,7 @@ export function configureCredentialEndpoint(router: Router, config: OpenId4VcIss error: Oauth2ErrorCodes.CredentialRequestDenied, }, { - internalMessage: `Access token without 'issuer_state' or 'pre-authorized_code' issued by external authorization server provided, but 'allowDynamicIssuanceSessions' is disabled. Either bind the access token to a statefull credential offer, or enable 'allowDynamicIssuanceSessions'.`, + internalMessage: `Access token without 'issuer_state' or 'pre-authorized_code' issued by external authorization server provided, but 'allowDynamicIssuanceSessions' is disabled. Either bind the access token to a stateful credential offer, or enable 'allowDynamicIssuanceSessions'.`, } ) ) diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index 97be911ec9..7a17f11eb1 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -1,3 +1,12 @@ +import type { + OpenId4VcSiopCreateAuthorizationRequestOptions, + OpenId4VcSiopCreateAuthorizationRequestReturn, + OpenId4VcSiopCreateVerifierOptions, + OpenId4VcSiopVerifiedAuthorizationResponse, + OpenId4VcSiopVerifiedAuthorizationResponseDcql, + OpenId4VcSiopVerifyAuthorizationResponseOptions, + ResponseMode, +} from './OpenId4VcSiopVerifierServiceOptions' import type { AgentContext, DcqlQuery, @@ -11,29 +20,10 @@ import type { TransactionDataResult, VerifiablePresentation, } from '@credo-ts/core' -import { - Oid4vcVerifier, - JarmClientMetadata, - VpTokenPresentationParseResult, - parseOpenid4vpAuthorizationResponse, - parseOpenid4vpAuthorizationRequestPayload, - ParsedOpenid4vpAuthorizationResponse, - isOpenid4vpAuthorizationResponseDcApi, - isJarmResponseMode, -} from '@openid4vc/oid4vp' -import type { - OpenId4VcSiopCreateAuthorizationRequestOptions, - OpenId4VcSiopCreateAuthorizationRequestReturn, - OpenId4VcSiopCreateVerifierOptions, - OpenId4VcSiopVerifiedAuthorizationResponse, - OpenId4VcSiopVerifiedAuthorizationResponseDcql, - OpenId4VcSiopVerifyAuthorizationResponseOptions, - ResponseMode, -} from './OpenId4VcSiopVerifierServiceOptions' -import { OpenId4VcVerificationSessionRecord } from './repository' import { CredoError, + DcqlService, DidsApi, DifPresentationExchangeService, extractPresentationsWithDescriptorsFromSubmission, @@ -44,6 +34,7 @@ import { inject, injectable, InjectionSymbols, + isMdocSupportedSignatureAlgorithm, joinUriParts, JsonEncoder, JsonTransformer, @@ -61,31 +52,40 @@ import { X509Certificate, X509ModuleConfig, X509Service, - isMdocSupportedSignatureAlgorithm, - DcqlService, } from '@credo-ts/core' +import { Oauth2ErrorCodes, Oauth2ServerErrorResponseError } from '@openid4vc/oauth2' +import { + ClientIdScheme, + isJarmResponseMode, + isOpenid4vpAuthorizationResponseDcApi, + JarmClientMetadata, + Oid4vcVerifier, + ParsedOpenid4vpAuthorizationResponse, + parseOpenid4vpAuthorizationRequestPayload, + parseOpenid4vpAuthorizationResponse, + VpTokenPresentationParseResult, + zOpenid4vpAuthorizationResponse, +} from '@openid4vc/oid4vp' import { PresentationDefinitionLocation } from '@sphereon/did-auth-siop' import { assertValidVerifiablePresentations, extractPresentationsFromVpToken, } from '@sphereon/did-auth-siop/dist/authorization-response/OpenID4VP' +import { getOid4vcCallbacks } from '../shared/callbacks' +import { OpenId4VcSiopAuthorizationResponsePayload } from '../shared/index' import { storeActorIdForContextCorrelationId } from '../shared/router' import { getSupportedJwaSignatureAlgorithms, openIdTokenIssuerToJwtIssuer, parseIfJson } from '../shared/utils' -import { ClientIdScheme } from '@openid4vc/oid4vp' -import { getOid4vcCallbacks } from '../shared/callbacks' -import { OpenId4VcSiopAuthorizationResponsePayload } from '../shared/index' import { OpenId4VcVerificationSessionState } from './OpenId4VcVerificationSessionState' import { OpenId4VcVerifierModuleConfig } from './OpenId4VcVerifierModuleConfig' import { + OpenId4VcVerificationSessionRecord, OpenId4VcVerificationSessionRepository, OpenId4VcVerifierRecord, OpenId4VcVerifierRepository, } from './repository' import { OpenId4VcRelyingPartyEventHandler } from './repository/OpenId4VcRelyingPartyEventEmitter' -import { Oauth2ErrorCodes, Oauth2ServerErrorResponseError } from '@openid4vc/oauth2' -import { zOpenid4vpAuthorizationResponse } from '@openid4vc/oid4vp' /** * @internal @@ -285,7 +285,9 @@ export class OpenId4VcSiopVerifierService { } verificationSession = session - const authorizationRequest = parseOpenid4vpAuthorizationRequestPayload({ requestPayload: verificationSession.authorizationRequestJwt }) + const authorizationRequest = parseOpenid4vpAuthorizationRequestPayload({ + requestPayload: verificationSession.authorizationRequestJwt, + }) if (authorizationRequest.type !== 'openid4vp') { throw new CredoError( `Invalid authorization request jwt. Expected 'openid4vp' request, received '${authorizationRequest.type}'.` @@ -303,7 +305,7 @@ export class OpenId4VcSiopVerifierService { verificationSession.state === OpenId4VcVerificationSessionState.RequestCreated) ) { const parsed = zOpenid4vpAuthorizationResponse.safeParse(rawResponsePayload) - if (!parsed.success) throw new error + if (!parsed.success) throw new error() await agentContext.dependencyManager .resolve(OpenId4VcRelyingPartyEventHandler) @@ -416,7 +418,7 @@ export class OpenId4VcSiopVerifierService { // This should be provided by pex-light! // It must check if the presentations match the presentation definition - assertValidVerifiablePresentations({ + await assertValidVerifiablePresentations({ presentationDefinitions: [ { definition: result.pex.presentationDefinition as any, @@ -517,11 +519,12 @@ export class OpenId4VcSiopVerifierService { throw new CredoError('No authorization response payload found in the verification session.') } - const openid4vpAuthorizationResponsePayload = isOpenid4vpAuthorizationResponseDcApi(verificationSession.authorizationResponsePayload) + const openid4vpAuthorizationResponsePayload = isOpenid4vpAuthorizationResponseDcApi( + verificationSession.authorizationResponsePayload + ) ? verificationSession.authorizationResponsePayload.data : verificationSession.authorizationResponsePayload - const openid4vpVerifier = this.getOpenid4vpVerifier(agentContext) const authorizationRequest = openid4vpVerifier.parseOpenid4vpAuthorizationRequestPayload({ requestPayload: verificationSession.authorizationRequestJwt, @@ -540,7 +543,7 @@ export class OpenId4VcSiopVerifierService { : undefined let presentationExchange: OpenId4VcSiopVerifiedAuthorizationResponse['presentationExchange'] | undefined = undefined - let dcql = + const dcql = result.type === 'dcql' ? this.getDcqlVerifiedResponse(agentContext, authorizationRequest.params.dcql_query, result.dcql.presentation) : undefined @@ -755,6 +758,10 @@ export class OpenId4VcSiopVerifierService { 'kb-jwt_alg_values': supportedAlgs, 'sd-jwt_alg_values': supportedAlgs, }, + 'dc+sd-jwt': { + 'kb-jwt_alg_values': supportedAlgs, + 'sd-jwt_alg_values': supportedAlgs, + }, }, } } @@ -951,6 +958,7 @@ export class OpenId4VcSiopVerifierService { await mdocDeviceResponse.verify(agentContext, { sessionTranscriptOptions: { + type: 'openId4Vp', clientId: options.audience, mdocGeneratedNonce: options.mdocGeneratedNonce, responseUri: options.responseUri, diff --git a/packages/openid4vc/src/shared/callbacks.ts b/packages/openid4vc/src/shared/callbacks.ts index 12b208a97e..04e0a14276 100644 --- a/packages/openid4vc/src/shared/callbacks.ts +++ b/packages/openid4vc/src/shared/callbacks.ts @@ -21,7 +21,7 @@ import { X509Service, } from '@credo-ts/core' -import { DecryptJweCallback, EncryptJweCallback } from '@openid4vc/oauth2/src/callbacks' +import { DecryptJweCallback, EncryptJweCallback } from '@openid4vc/oauth2' import { getKeyFromDid } from './utils' export function getOid4vciJwtVerifyCallback( diff --git a/packages/openid4vc/src/shared/models/index.ts b/packages/openid4vc/src/shared/models/index.ts index 1d142e594c..8f1ce60ce0 100644 --- a/packages/openid4vc/src/shared/models/index.ts +++ b/packages/openid4vc/src/shared/models/index.ts @@ -12,11 +12,16 @@ import type { IssuerMetadataResult, ParseCredentialRequestReturn, } from '@openid4vc/oid4vci' -import { Openid4vpAuthorizationResponse, Openid4vpAuthorizationResponseDcApi, ResolvedOpenid4vpAuthRequest, Openid4vpAuthorizationRequest} from '@openid4vc/oid4vp' -import { Openid4vpAuthorizationRequestDcApi } from '@openid4vc/oid4vp/src/authorization-request/z-authorization-request-dc-api.js' +import type { + Openid4vpAuthorizationResponse, + Openid4vpAuthorizationResponseDcApi, + ResolvedOpenid4vpAuthRequest, + Openid4vpAuthorizationRequest, + Openid4vpAuthorizationRequestDcApi, +} from '@openid4vc/oid4vp' export { authorizationCodeGrantIdentifier, preAuthorizedCodeGrantIdentifier } from '@openid4vc/oauth2' -export { Openid4vpAuthorizationRequest} from '@openid4vc/oid4vp' +export { Openid4vpAuthorizationRequest } from '@openid4vc/oid4vp' export type OpenId4VciCredentialConfigurationSupportedWithFormats = CredentialConfigurationSupportedWithFormats export type OpenId4VciCredentialConfigurationSupported = CredentialConfigurationSupported @@ -44,8 +49,12 @@ export type OpenId4VciCredentialRequestWithFormats = CredentialRequestWithFormat export type OpenId4VciCredentialOfferPayload = CredentialOfferObject export type OpenId4VcSiopVerifiedAuthorizationRequest = ResolvedOpenid4vpAuthRequest -export type OpenId4VcSiopAuthorizationRequestPayload = Openid4vpAuthorizationRequest | Openid4vpAuthorizationRequestDcApi -export type OpenId4VcSiopAuthorizationResponsePayload = Openid4vpAuthorizationResponse | Openid4vpAuthorizationResponseDcApi['data'] +export type OpenId4VcSiopAuthorizationRequestPayload = + | Openid4vpAuthorizationRequest + | Openid4vpAuthorizationRequestDcApi +export type OpenId4VcSiopAuthorizationResponsePayload = + | Openid4vpAuthorizationResponse + | Openid4vpAuthorizationResponseDcApi['data'] export * from './CredentialHolderBinding' export * from './OpenId4VciAuthorizationServerConfig' diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index c46a247fb8..aacff40552 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -1,27 +1,21 @@ -import type { AuthorizationServerMetadata } from '@openid4vc/oauth2' +import type { AgentType, TenantType } from './utils' +import type { OpenId4VciSignMdocCredentials } from '../src' +import type { OpenId4VciCredentialBindingResolver } from '../src/openid4vc-holder' import type { + DcqlQuery, DifPresentationExchangeDefinitionV2, JwkJson, Mdoc, MdocDeviceResponse, SdJwtVc, - DcqlQuery, } from '@credo-ts/core' -import type { OpenId4VciSignMdocCredentials } from '../src' -import type { OpenId4VciCredentialBindingResolver } from '../src/openid4vc-holder' -import type { AgentType, TenantType } from './utils' +import type { AuthorizationServerMetadata } from '@openid4vc/oauth2' +import type { Server } from 'http' -import { - calculateJwkThumbprint, - clientAuthenticationNone, - HashAlgorithm, - Oauth2AuthorizationServer, - preAuthorizedCodeGrantIdentifier, -} from '@openid4vc/oauth2' -import { AuthorizationFlow } from '@openid4vc/oid4vci' import { ClaimFormat, CredoError, + DateOnly, DidsApi, getJwkFromKey, getKeyFromVerificationMethod, @@ -32,7 +26,9 @@ import { Jwt, JwtPayload, KeyType, + MdocRecord, parseDid, + SdJwtVcRecord, W3cCredential, W3cCredentialSubject, w3cDate, @@ -40,10 +36,15 @@ import { X509Module, X509ModuleConfig, X509Service, - SdJwtVcRecord, - MdocRecord, - DateOnly, } from '@credo-ts/core' +import { + calculateJwkThumbprint, + clientAuthenticationNone, + HashAlgorithm, + Oauth2AuthorizationServer, + preAuthorizedCodeGrantIdentifier, +} from '@openid4vc/oauth2' +import { AuthorizationFlow } from '@openid4vc/oid4vci' import express, { type Express } from 'express' import { AskarModule } from '../../askar/src' @@ -58,7 +59,6 @@ import { } from '../src' import { getOid4vcCallbacks } from '../src/shared/callbacks' -import { Server } from 'http' import { createAgentFromModules, createTenantForAgent, @@ -1025,10 +1025,8 @@ describe('OpenId4Vc', () => { state: OpenId4VcVerificationSessionState.ResponseVerified, verificationSessionId: verificationSession.id, }) - const { - presentationExchange, - transactionData: _transactionData, - } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + const { presentationExchange, transactionData: _transactionData } = + await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) const presentation = presentationExchange?.presentations[0] as SdJwtVc expect(transactionData).toEqual(_transactionData) @@ -1272,8 +1270,9 @@ describe('OpenId4Vc', () => { state: OpenId4VcVerificationSessionState.ResponseVerified, verificationSessionId: verificationSession.id, }) - const { presentationExchange } = - await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + const { presentationExchange } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse( + verificationSession.id + ) const presentation = presentationExchange?.presentations[0] as SdJwtVc @@ -1661,10 +1660,8 @@ describe('OpenId4Vc', () => { state: OpenId4VcVerificationSessionState.ResponseVerified, verificationSessionId: verificationSession.id, }) - const { - presentationExchange, - transactionData: tdResult, - } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + const { presentationExchange, transactionData: tdResult } = + await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) expect(transactionData).toEqual(tdResult) @@ -1769,8 +1766,6 @@ describe('OpenId4Vc', () => { ], descriptors: expect.any(Array), }) - - console.log('test') }) it('e2e flow with tenants, issuer endpoints requesting a mdoc', async () => { @@ -2026,8 +2021,8 @@ describe('OpenId4Vc', () => { expect(result.ok).toBe(false) expect(result.serverResponse.body).toMatchObject({ - error: "invalid_request", - error_description: "Invalid response mode for openid4vp response. Expected jarm response.", + error: 'invalid_request', + error_description: 'Invalid response mode for openid4vp response. Expected jarm response.', }) }) @@ -2283,8 +2278,9 @@ describe('OpenId4Vc', () => { state: OpenId4VcVerificationSessionState.ResponseVerified, verificationSessionId: verificationSession.id, }) - const { presentationExchange } = - await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + const { presentationExchange } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse( + verificationSession.id + ) const presentation = presentationExchange?.presentations[0] as MdocDeviceResponse expect(presentation.documents).toHaveLength(1) @@ -2614,8 +2610,9 @@ describe('OpenId4Vc', () => { state: OpenId4VcVerificationSessionState.ResponseVerified, verificationSessionId: verificationSession.id, }) - const { presentationExchange } = - await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + const { presentationExchange } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse( + verificationSession.id + ) const presentation = presentationExchange?.presentations[0] as SdJwtVc @@ -2949,8 +2946,9 @@ describe('OpenId4Vc', () => { verificationSessionId: verificationSession.id, }) - const { dcql, transactionData } = - await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(verificationSession.id) + const { dcql, transactionData } = await verifier.agent.modules.openId4VcVerifier.getVerifiedAuthorizationResponse( + verificationSession.id + ) expect(transactionData).toEqual([ { @@ -3071,31 +3069,32 @@ describe('OpenId4Vc', () => { ], } satisfies DcqlQuery - const { authorizationRequest, verificationSession } = - await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ - responseMode: 'dc_api.jwt', - expectedOrigins: ['https://example.com'], - verifierId: openIdVerifier.verifierId, - requestSigner: { - method: 'x5c', - x5c: [rawCertificate], - issuer: 'https://example.com/hakuna/matadata', - }, - dcql: { - query: dcqlQuery, - transactionData: [ - { - type: 'OpenBadgeTx', - credential_ids: ['OpenBadgeCredentialDescriptor'], - transaction_data_hashes_alg: ['sha-256'], - }, - ], - }, - }) + const { authorizationRequest } = await verifier.agent.modules.openId4VcVerifier.createAuthorizationRequest({ + responseMode: 'dc_api.jwt', + expectedOrigins: ['https://example.com'], + verifierId: openIdVerifier.verifierId, + requestSigner: { + method: 'x5c', + x5c: [rawCertificate], + issuer: 'https://example.com/hakuna/matadata', + }, + dcql: { + query: dcqlQuery, + transactionData: [ + { + type: 'OpenBadgeTx', + credential_ids: ['OpenBadgeCredentialDescriptor'], + transaction_data_hashes_alg: ['sha-256'], + }, + ], + }, + }) const resolvedAuthorizationRequest = await holder.agent.modules.openId4VcHolder.resolveSiopAuthorizationRequest( authorizationRequest, - 'https://example.com' + { + origin: 'https://example.com', + } ) expect(resolvedAuthorizationRequest.dcql).toEqual({ @@ -3202,12 +3201,13 @@ describe('OpenId4Vc', () => { resolvedAuthorizationRequest.dcql.queryResult ) - await expect(holder.agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ + await expect( + holder.agent.modules.openId4VcHolder.acceptSiopAuthorizationRequest({ authorizationRequest: resolvedAuthorizationRequest.authorizationRequest, dcql: { credentials: selectedCredentials, }, - })).rejects.toThrow('Submission of DC API responses is not yet supported.') - + }) + ).rejects.toThrow('Submission of DC API responses is not yet supported.') }) }) diff --git a/packages/tenants/tests/tenants-askar-profiles.test.ts b/packages/tenants/tests/tenants-askar-profiles.test.ts index 93761bc218..c2702784c2 100644 --- a/packages/tenants/tests/tenants-askar-profiles.test.ts +++ b/packages/tenants/tests/tenants-askar-profiles.test.ts @@ -23,7 +23,7 @@ describe('Tenants Askar database schemes E2E', () => { modules: { tenants: new TenantsModule(), askar: new AskarModule({ - ariesAskar: askarModuleConfig.ariesAskar, + askar: askarModuleConfig.askar, // Database per wallet multiWalletDatabaseScheme: AskarMultiWalletDatabaseScheme.DatabasePerWallet, }), @@ -81,7 +81,7 @@ describe('Tenants Askar database schemes E2E', () => { modules: { tenants: new TenantsModule(), askar: new AskarModule({ - ariesAskar: askarModuleConfig.ariesAskar, + askar: askarModuleConfig.askar, // Profile per wallet multiWalletDatabaseScheme: AskarMultiWalletDatabaseScheme.ProfilePerWallet, }), diff --git a/packages/tenants/tests/tenants-storage-update.test.ts b/packages/tenants/tests/tenants-storage-update.test.ts index e443893440..68dd7d6c18 100644 --- a/packages/tenants/tests/tenants-storage-update.test.ts +++ b/packages/tenants/tests/tenants-storage-update.test.ts @@ -6,7 +6,7 @@ import { agentDependencies } from '@credo-ts/node' import path from 'path' import { AskarModule, AskarMultiWalletDatabaseScheme } from '../../askar/src' -import { ariesAskar } from '../../askar/tests/helpers' +import { askar } from '../../askar/tests/helpers' import { testLogger } from '../../core/tests' import { getDefaultDidcommModules } from '../../didcomm/src/util/modules' import { TenantSessionCoordinator } from '../src/context/TenantSessionCoordinator' @@ -26,7 +26,7 @@ const modules = { ...getDefaultDidcommModules(), tenants: new TenantsModule(), askar: new AskarModule({ - ariesAskar, + askar: askar, multiWalletDatabaseScheme: AskarMultiWalletDatabaseScheme.ProfilePerWallet, }), connections: new ConnectionsModule({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b40323f14..e17dd71cea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: overrides: '@types/node': 18.18.8 undici: ^6.20.1 + '@openid4vc/oid4vci': file:./local/openid4vc-oid4vci-0.2.0.tgz + '@openid4vc/oid4vp': file:./local/openid4vc-oid4vp-0.1.4.tgz + '@openid4vc/oauth2': file:./local/openid4vc-oauth2-0.2.0.tgz + '@openid4vc/utils': file:./local/openid4vc-utils-0.2.0.tgz importers: @@ -21,15 +25,12 @@ importers: '@changesets/cli': specifier: ^2.27.5 version: 2.27.5 - '@hyperledger/aries-askar-nodejs': - specifier: ^0.2.3 - version: 0.2.3 '@jest/types': specifier: ^29.6.3 version: 29.6.3 - '@types/bn.js': - specifier: ^5.1.5 - version: 5.1.5 + '@openwallet-foundation/askar-nodejs': + specifier: ^0.3.1 + version: 0.3.1 '@types/cors': specifier: ^2.8.10 version: 2.8.17 @@ -63,9 +64,6 @@ importers: '@typescript-eslint/parser': specifier: ^7.14.1 version: 7.18.0(eslint@8.57.0)(typescript@5.5.4) - bn.js: - specifier: ^5.2.1 - version: 5.2.1 cors: specifier: ^2.8.5 version: 2.8.5 @@ -129,12 +127,12 @@ importers: '@hyperledger/anoncreds-nodejs': specifier: ^0.2.2 version: 0.2.2 - '@hyperledger/aries-askar-nodejs': - specifier: ^0.2.3 - version: 0.2.3 '@hyperledger/indy-vdr-nodejs': specifier: ^0.2.2 version: 0.2.2 + '@openwallet-foundation/askar-nodejs': + specifier: ^0.3.1 + version: 0.3.1 inquirer: specifier: ^8.2.5 version: 8.2.6 @@ -181,15 +179,15 @@ importers: '@hyperledger/anoncreds-nodejs': specifier: ^0.2.2 version: 0.2.2 - '@hyperledger/aries-askar-nodejs': - specifier: ^0.2.3 - version: 0.2.3 '@hyperledger/indy-vdr-nodejs': specifier: ^0.2.2 version: 0.2.2 '@koa/bodyparser': specifier: ^5.1.1 version: 5.1.1(koa@2.15.3) + '@openwallet-foundation/askar-nodejs': + specifier: ^0.3.1 + version: 0.3.1 express: specifier: ^4.18.1 version: 4.19.2 @@ -282,12 +280,6 @@ importers: '@sphereon/pex-models': specifier: ^2.3.1 version: 2.3.2 - big-integer: - specifier: ^1.6.51 - version: 1.6.52 - bn.js: - specifier: ^5.2.1 - version: 5.2.1 class-transformer: specifier: 0.5.1 version: 0.5.1 @@ -322,9 +314,6 @@ importers: '@credo-ts/core': specifier: workspace:* version: link:../core - bn.js: - specifier: ^5.2.1 - version: 5.2.1 class-transformer: specifier: 0.5.1 version: 0.5.1 @@ -339,17 +328,14 @@ importers: version: 4.8.0 devDependencies: '@animo-id/expo-secure-environment': - specifier: ^0.1.0-alpha.11 + specifier: ^0.1.0-alpha.12 version: 0.1.0-alpha.12(expo@51.0.14(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)))(react-native@0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(react@18.3.1))(react@18.3.1) - '@hyperledger/aries-askar-nodejs': - specifier: ^0.2.3 - version: 0.2.3 - '@hyperledger/aries-askar-shared': - specifier: ^0.2.3 - version: 0.2.3 - '@types/bn.js': - specifier: ^5.1.0 - version: 5.1.5 + '@openwallet-foundation/askar-nodejs': + specifier: ^0.3.1 + version: 0.3.1 + '@openwallet-foundation/askar-shared': + specifier: ^0.3.1 + version: 0.3.1 '@types/ref-array-di': specifier: ^1.2.6 version: 1.2.8 @@ -400,11 +386,11 @@ importers: packages/cheqd: dependencies: '@cheqd/sdk': - specifier: 2.5.1 - version: 2.5.1 + specifier: ~2.6.0 + version: 2.6.0 '@cheqd/ts-proto': - specifier: ~2.3.2 - version: 2.3.2 + specifier: ~2.4.0 + version: 2.4.0 '@cosmjs/crypto': specifier: ~0.30.0 version: 0.30.1 @@ -446,11 +432,11 @@ importers: packages/core: dependencies: '@animo-id/mdoc': - specifier: 0.3.0 - version: 0.3.0 + specifier: 0.4.0 + version: 0.4.0 '@animo-id/pex': - specifier: 4.1.1-alpha.0 - version: 4.1.1-alpha.0 + specifier: 4.1.1-alpha.1 + version: 4.1.1-alpha.1 '@astronautlabs/jsonpath': specifier: ^1.1.2 version: 1.1.2 @@ -517,9 +503,6 @@ importers: '@types/ws': specifier: ^8.5.4 version: 8.5.10 - big-integer: - specifier: ^1.6.51 - version: 1.6.52 borc: specifier: ^3.0.0 version: 3.0.0 @@ -533,11 +516,14 @@ importers: specifier: 0.14.1 version: 0.14.1 dcql: - specifier: ^0.2.17 - version: 0.2.19(typescript@5.5.4) + specifier: ^0.2.20 + version: 0.2.20(typescript@5.5.4) did-resolver: specifier: ^4.1.0 version: 4.1.0 + ec-compression: + specifier: 0.0.1-alpha.10 + version: 0.0.1-alpha.10 lru_map: specifier: ^0.4.1 version: 0.4.1 @@ -677,12 +663,12 @@ importers: specifier: workspace:* version: link:../node devDependencies: - '@hyperledger/aries-askar-nodejs': - specifier: ^0.2.3 - version: 0.2.3 - '@hyperledger/aries-askar-shared': - specifier: ^0.2.3 - version: 0.2.3 + '@openwallet-foundation/askar-nodejs': + specifier: ^0.3.1 + version: 0.3.1 + '@openwallet-foundation/askar-shared': + specifier: ^0.3.1 + version: 0.3.1 rimraf: specifier: ^4.4.0 version: 4.4.1 @@ -773,17 +759,23 @@ importers: specifier: workspace:* version: link:../core '@openid4vc/oauth2': - specifier: link:../../../Code/oid4vc-ts/packages/oauth2 - version: link:../../../Code/oid4vc-ts/packages/oauth2 + specifier: file:../../local/openid4vc-oauth2-0.2.0.tgz + version: file:local/openid4vc-oauth2-0.2.0.tgz '@openid4vc/oid4vci': - specifier: link:../../../Code/oid4vc-ts/packages/oid4vci - version: link:../../../Code/oid4vc-ts/packages/oid4vci + specifier: file:../../local/openid4vc-oid4vci-0.2.0.tgz + version: file:local/openid4vc-oid4vci-0.2.0.tgz '@openid4vc/oid4vp': - specifier: link:../../../Code/oid4vc-ts/packages/openid4vp - version: link:../../../Code/oid4vc-ts/packages/openid4vp + specifier: file:../../local/openid4vc-oid4vp-0.1.4.tgz + version: file:local/openid4vc-oid4vp-0.1.4.tgz '@sphereon/did-auth-siop': specifier: 0.16.1-fix.173 version: 0.16.1-fix.173(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.4))(typescript@5.5.4) + '@sphereon/oid4vc-common': + specifier: 0.16.1-fix.173 + version: 0.16.1-fix.173(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.4)) + '@sphereon/ssi-types': + specifier: 0.30.2-next.135 + version: 0.30.2-next.135 class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -791,7 +783,7 @@ importers: specifier: ^7.8.0 version: 7.8.1 zod: - specifier: ^3.24.1 + specifier: ^3.23.8 version: 3.24.1 devDependencies: '@credo-ts/tenants': @@ -911,9 +903,9 @@ importers: '@credo-ts/node': specifier: workspace:* version: link:../../packages/node - '@hyperledger/aries-askar-nodejs': - specifier: ^0.2.3 - version: 0.2.3 + '@openwallet-foundation/askar-nodejs': + specifier: ^0.3.1 + version: 0.3.1 class-validator: specifier: 0.14.1 version: 0.14.1 @@ -986,13 +978,17 @@ packages: react: '*' react-native: '*' - '@animo-id/mdoc@0.3.0': - resolution: {integrity: sha512-igpkhrmUbH6VEm0yoKmLyHS3yWdPp2rzdAmBz43Isqot5J5RePM8GFLxi7O0svGYBumoduS4LKddXSqmHNp7zQ==} + '@animo-id/mdoc@0.4.0': + resolution: {integrity: sha512-ziuaAn4MnocSwoGZzKbgDB3g5ohfl79USR8Ly/uGImPRdZ6qxaB/cqO3dP0162PhKFbWsywgsgpghCiOeYoVBA==} '@animo-id/pex@4.1.1-alpha.0': resolution: {integrity: sha512-6ieHhH9UE9DLFOJegMCabG3qUFlQk4TLhBefxInpyjx2Ly6kuloVMScJYcnQTs/E6nuHGMd7ebUaKy4+0+ZbOA==} engines: {node: '>=18'} + '@animo-id/pex@4.1.1-alpha.1': + resolution: {integrity: sha512-b1VH2y3EYnLir7wiu4sap2iajmGmG05j0O+3AiAMSuLSWDeRyiTR+BumUmAxYDTZuXuoe3L82mJ5VTXPUNFM1g==} + engines: {node: '>=18'} + '@animo-id/react-native-bbs-signatures@0.1.0': resolution: {integrity: sha512-7qvsiWhGfUev8ngE8YzF6ON9PtCID5LiYVYM4EC5eyj80gCdhx3R46CI7K1qbqIlGsoTYQ/Xx5Ubo5Ji9eaUEA==} peerDependencies: @@ -1759,6 +1755,9 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bufbuild/protobuf@2.2.3': + resolution: {integrity: sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg==} + '@changesets/apply-release-plan@7.0.3': resolution: {integrity: sha512-klL6LCdmfbEe9oyfLxnidIf/stFXmrbFO/3gT5LU5pcyoZytzJe4gWpTBx3BPmyNPl16dZ1xrkcW7b98e3tYkA==} @@ -1814,13 +1813,13 @@ packages: '@changesets/write@0.3.1': resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} - '@cheqd/sdk@2.5.1': - resolution: {integrity: sha512-Vm9hwFIzgXhK1/ilaciauPnfa2mm8/aGPVRxM/O3LCLUyBdmS+dKIY6G6FKN/ttcsfUJSRmpR50weFY/vMqs3g==} + '@cheqd/sdk@2.6.0': + resolution: {integrity: sha512-Lx3/0dT7QoKaWfQcc4PzhcaFud5Fkgm1//Q7KfPXakerSLuOkyaVy95VnFnnoLERuWZcE6r3nI1xoKq1GFckDg==} engines: {node: '>=18'} - '@cheqd/ts-proto@2.3.2': - resolution: {integrity: sha512-WqwthQGyF16tx3LyqwJlLq/4DfTigNsCWtGY3ct9RSfTNTPI/JZ1qLE7eSkcE9Glk4Yk95d+SYbqOeoSNP442Q==} - engines: {node: '>=18'} + '@cheqd/ts-proto@2.4.0': + resolution: {integrity: sha512-+iPJujk1IfeWdcbVzMEjleVXab1leYshiFZNQGpDw6PCOWkRRZ/0BOJgRm6/LQjcj70ceDje4Io/8+CIh+9A5g==} + engines: {node: '>=20'} '@confio/ics23@0.6.8': resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==} @@ -2242,13 +2241,6 @@ packages: '@hyperledger/anoncreds-shared@0.2.2': resolution: {integrity: sha512-dfYpqbAkqtHJkRkuGmWdJruHfLegLUIbu/dSAWnz5dMRKd8ad8rEnQkwRnockQZ/pc7QDr8kxfG6bT2YDGZeMw==} - '@hyperledger/aries-askar-nodejs@0.2.3': - resolution: {integrity: sha512-2BnGqK08Y96DEB8tDuXy2x+soetChyMGB0+L1yqdHx1Xv5FvRerYrTXdTjJXTW6ANb48k2Np8WlJ4YNePSo6ww==} - engines: {node: '>= 18'} - - '@hyperledger/aries-askar-shared@0.2.3': - resolution: {integrity: sha512-g9lao8qa80kPCLqqp02ovNqEfQIrm6cAf4xZVzD5P224VmOhf4zM6AKplQTvQx7USNKoXroe93JrOOSVxPeqrA==} - '@hyperledger/indy-vdr-nodejs@0.2.2': resolution: {integrity: sha512-mc0iKuHCtKuOV0sMnGOTVWnQrpfBMS+1tIRyob+CvGCvwC2llGo3Hu5AvgPcR9nqCo/wJ0LoKBo66dYYb0uZbw==} engines: {node: '>= 18'} @@ -2472,6 +2464,29 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@openid4vc/oauth2@file:local/openid4vc-oauth2-0.2.0.tgz': + resolution: {integrity: sha512-n51ygKJD1ijgOjarkq8Xk0UuTfSPJjj/aX1L6bzrpHQAZGuiel7gew2Fgj7Z+mExWKSOViQ63iGaTI7G6yMcdQ==, tarball: file:local/openid4vc-oauth2-0.2.0.tgz} + version: 0.2.0 + + '@openid4vc/oid4vci@file:local/openid4vc-oid4vci-0.2.0.tgz': + resolution: {integrity: sha512-OSgSaFMdwh6hEuUpCVrKiA5ost8xVXcHPJPQcr89WqLyqxtPmFAWsjfeiCL6ZhMZHTyjCEYzhYiwtSnrpakqfQ==, tarball: file:local/openid4vc-oid4vci-0.2.0.tgz} + version: 0.2.0 + + '@openid4vc/oid4vp@file:local/openid4vc-oid4vp-0.1.4.tgz': + resolution: {integrity: sha512-zcgbpJ+6T+2rQzORyR/ZfltRd4nrjG4AMz/G4jHjM1NREgtyOwG7vISkzYAL30ySHFk9d2PjE0u8hqkJxszTXQ==, tarball: file:local/openid4vc-oid4vp-0.1.4.tgz} + version: 0.1.4 + + '@openid4vc/utils@file:local/openid4vc-utils-0.2.0.tgz': + resolution: {integrity: sha512-59Gp7g+7PdtVier4CT3Jit3fzF+nebuNkFjpzFOtW0Y76tkXSRv9t4LNMHHKsskFylo+CEpg81TQFtd/SiTIWw==, tarball: file:local/openid4vc-utils-0.2.0.tgz} + version: 0.2.0 + + '@openwallet-foundation/askar-nodejs@0.3.1': + resolution: {integrity: sha512-m3L8KEPC+qgA3MAFssMtjSqJiAQtrawZEWPmW6eiB7OPjZvkKjodMhx/cuUV5YTl4eQlSix2EY4vXMzk4vt+cQ==} + engines: {node: '>= 18'} + + '@openwallet-foundation/askar-shared@0.3.1': + resolution: {integrity: sha512-KpkVuLHnKkJ7nPYZ/6PJ8b9gq+elC6Mhg8UiG+wLOKchVi6Vbv/nOsAdKK9j1/CCZ3DzvfTbhV+KRk6YNXmIsA==} + '@peculiar/asn1-cms@2.3.13': resolution: {integrity: sha512-joqu8A7KR2G85oLPq+vB+NFr2ro7Ls4ol13Zcse/giPSzUNN0n2k3v8kMpf6QdGUhI13e5SzQYN8AKP8sJ8v4w==} @@ -2710,6 +2725,7 @@ packages: '@sphereon/kmp-mdl-mdoc@0.2.0-SNAPSHOT.22': resolution: {integrity: sha512-uAZZExVy+ug9JLircejWa5eLtAZ7bnBP6xb7DO2+86LRsHNLh2k2jMWJYxp+iWtGHTsh6RYsZl14ScQLvjiQ/A==} + bundledDependencies: [] '@sphereon/oid4vc-common@0.16.1-fix.173': resolution: {integrity: sha512-+AAUvEEFs0vzz1mrgjSgvDkcBtr18d2XEVgJex7QlAqxCKVGfjzZlqL2Q2vOLKYVaXsazhD5LnYiY6B5WMTC3Q==} @@ -2855,9 +2871,6 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@types/bn.js@5.1.5': - resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} - '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} @@ -3962,8 +3975,8 @@ packages: dayjs@1.11.11: resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} - dcql@0.2.19: - resolution: {integrity: sha512-/EvT8tArlg8zFsTQbRn6PijfeQ3nUwuEeCRDpptWcYqE8Wyt8J9Sb44gMPFzVCoIEb3R0M7Hl+XWkUMobC8jXQ==} + dcql@0.2.20: + resolution: {integrity: sha512-r45vIFfjFcBLWWs2n2MVgVNJne0Rp0FezUD7lPWZ8LFtG/xJdwtGDwRoHp1XqOIFpqJcY1nn1/KW2BxjmDfKBA==} debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -4154,6 +4167,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ec-compression@0.0.1-alpha.10: + resolution: {integrity: sha512-40i890RKwkaxUqAI8XZkELoKWhQS4VDfHU0D0lYwrFjfA7vtUZWaCNCbpoGwCMZqrPrWGfXqej76/N44kVKxMg==} + ed25519-signature-2018-context@1.1.0: resolution: {integrity: sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA==} @@ -8061,7 +8077,7 @@ snapshots: react: 18.3.1 react-native: 0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(react@18.3.1) - '@animo-id/mdoc@0.3.0': + '@animo-id/mdoc@0.4.0': dependencies: compare-versions: 6.1.1 @@ -8081,6 +8097,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@animo-id/pex@4.1.1-alpha.1': + dependencies: + '@astronautlabs/jsonpath': 1.1.2 + '@sd-jwt/decode': 0.7.2 + '@sd-jwt/present': 0.7.2 + '@sd-jwt/types': 0.7.2 + '@sphereon/pex-models': 2.3.2 + '@sphereon/ssi-types': 0.30.2-next.135 + ajv: 8.16.0 + ajv-formats: 2.1.1(ajv@8.16.0) + jwt-decode: 3.1.2 + nanoid: 3.3.7 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - supports-color + '@animo-id/react-native-bbs-signatures@0.1.0(react-native@0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(react@18.3.1))(react@18.3.1)': dependencies: react: 18.3.1 @@ -9030,6 +9062,8 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@bufbuild/protobuf@2.2.3': {} + '@changesets/apply-release-plan@7.0.3': dependencies: '@babel/runtime': 7.24.7 @@ -9187,9 +9221,9 @@ snapshots: human-id: 1.0.2 prettier: 2.8.8 - '@cheqd/sdk@2.5.1': + '@cheqd/sdk@2.6.0': dependencies: - '@cheqd/ts-proto': 2.3.2 + '@cheqd/ts-proto': 2.4.0 '@cosmjs/amino': 0.30.1 '@cosmjs/crypto': 0.30.1 '@cosmjs/encoding': 0.30.1 @@ -9213,8 +9247,9 @@ snapshots: - debug - utf-8-validate - '@cheqd/ts-proto@2.3.2': + '@cheqd/ts-proto@2.4.0': dependencies: + '@bufbuild/protobuf': 2.2.3 long: 5.2.3 protobufjs: 7.4.0 @@ -9969,22 +10004,6 @@ snapshots: '@hyperledger/anoncreds-shared@0.2.2': {} - '@hyperledger/aries-askar-nodejs@0.2.3': - dependencies: - '@2060.io/ffi-napi': 4.0.9 - '@2060.io/ref-napi': 3.0.6 - '@hyperledger/aries-askar-shared': 0.2.3 - '@mapbox/node-pre-gyp': 1.0.11 - ref-array-di: 1.2.2 - ref-struct-di: 1.1.1 - transitivePeerDependencies: - - encoding - - supports-color - - '@hyperledger/aries-askar-shared@0.2.3': - dependencies: - buffer: 6.0.3 - '@hyperledger/indy-vdr-nodejs@0.2.2': dependencies: '@2060.io/ffi-napi': 4.0.9 @@ -10367,6 +10386,44 @@ snapshots: '@open-draft/until@2.1.0': {} + '@openid4vc/oauth2@file:local/openid4vc-oauth2-0.2.0.tgz': + dependencies: + '@openid4vc/utils': file:local/openid4vc-utils-0.2.0.tgz + zod: 3.24.1 + + '@openid4vc/oid4vci@file:local/openid4vc-oid4vci-0.2.0.tgz': + dependencies: + '@openid4vc/oauth2': file:local/openid4vc-oauth2-0.2.0.tgz + '@openid4vc/utils': file:local/openid4vc-utils-0.2.0.tgz + zod: 3.24.1 + + '@openid4vc/oid4vp@file:local/openid4vc-oid4vp-0.1.4.tgz': + dependencies: + '@openid4vc/oauth2': file:local/openid4vc-oauth2-0.2.0.tgz + '@openid4vc/utils': file:local/openid4vc-utils-0.2.0.tgz + zod: 3.24.1 + + '@openid4vc/utils@file:local/openid4vc-utils-0.2.0.tgz': + dependencies: + buffer: 6.0.3 + zod: 3.24.1 + + '@openwallet-foundation/askar-nodejs@0.3.1': + dependencies: + '@2060.io/ffi-napi': 4.0.9 + '@2060.io/ref-napi': 3.0.6 + '@mapbox/node-pre-gyp': 1.0.11 + '@openwallet-foundation/askar-shared': 0.3.1 + ref-array-di: 1.2.2 + ref-struct-di: 1.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@openwallet-foundation/askar-shared@0.3.1': + dependencies: + buffer: 6.0.3 + '@peculiar/asn1-cms@2.3.13': dependencies: '@peculiar/asn1-schema': 2.3.13 @@ -11343,10 +11400,6 @@ snapshots: dependencies: '@babel/types': 7.26.3 - '@types/bn.js@5.1.5': - dependencies: - '@types/node': 18.18.8 - '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 @@ -12682,7 +12735,7 @@ snapshots: dayjs@1.11.11: {} - dcql@0.2.19(typescript@5.5.4): + dcql@0.2.20(typescript@5.5.4): dependencies: valibot: 1.0.0-beta.8(typescript@5.5.4) transitivePeerDependencies: @@ -12867,6 +12920,8 @@ snapshots: eastasianwidth@0.2.0: {} + ec-compression@0.0.1-alpha.10: {} + ed25519-signature-2018-context@1.1.0: {} ed25519-signature-2020-context@1.1.0: {} diff --git a/samples/extension-module/package.json b/samples/extension-module/package.json index 670f00f7a2..d48503c652 100644 --- a/samples/extension-module/package.json +++ b/samples/extension-module/package.json @@ -25,6 +25,6 @@ "@credo-ts/askar": "workspace:*", "class-validator": "0.14.1", "rxjs": "^7.8.0", - "@hyperledger/aries-askar-nodejs": "^0.2.3" + "@openwallet-foundation/askar-nodejs": "^0.3.1" } } diff --git a/samples/extension-module/requester.ts b/samples/extension-module/requester.ts index 7b5502b6c9..929fe04c56 100644 --- a/samples/extension-module/requester.ts +++ b/samples/extension-module/requester.ts @@ -11,7 +11,7 @@ import { MessagePickupModule, } from '@credo-ts/didcomm' import { agentDependencies } from '@credo-ts/node' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import { filter, first, firstValueFrom, map, ReplaySubject, timeout } from 'rxjs' import { DummyEventTypes, DummyState, DummyModule } from './dummy' @@ -33,7 +33,7 @@ const run = async () => { logger: new ConsoleLogger(LogLevel.info), }, modules: { - askar: new AskarModule({ ariesAskar }), + askar: new AskarModule({ askar }), didcomm: new DidCommModule(), oob: new OutOfBandModule(), messagePickup: new MessagePickupModule(), diff --git a/samples/extension-module/responder.ts b/samples/extension-module/responder.ts index 67934d7f15..feca637417 100644 --- a/samples/extension-module/responder.ts +++ b/samples/extension-module/responder.ts @@ -5,7 +5,7 @@ import { AskarModule } from '@credo-ts/askar' import { Agent, ConsoleLogger, LogLevel } from '@credo-ts/core' import { ConnectionsModule, DidCommModule, MessagePickupModule, OutOfBandModule } from '@credo-ts/didcomm' import { agentDependencies, HttpInboundTransport, WsInboundTransport } from '@credo-ts/node' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import express from 'express' import { Server } from 'ws' @@ -32,7 +32,7 @@ const run = async () => { logger: new ConsoleLogger(LogLevel.debug), }, modules: { - askar: new AskarModule({ ariesAskar }), + askar: new AskarModule({ askar }), didcomm: new DidCommModule({ endpoints: [`http://localhost:${port}`] }), oob: new OutOfBandModule(), messagePickup: new MessagePickupModule(), diff --git a/samples/extension-module/tests/dummy.test.ts b/samples/extension-module/tests/dummy.test.ts index e3834617c1..bdf550c2a0 100644 --- a/samples/extension-module/tests/dummy.test.ts +++ b/samples/extension-module/tests/dummy.test.ts @@ -3,7 +3,7 @@ import type { ConnectionRecord } from '@credo-ts/didcomm' import { AskarModule } from '@credo-ts/askar' import { Agent } from '@credo-ts/core' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import { Subject } from 'rxjs' import { getAgentOptions, makeConnection } from '../../../packages/core/tests/helpers' @@ -18,7 +18,7 @@ import { waitForDummyRecord } from './helpers' const modules = { dummy: new DummyModule(), askar: new AskarModule({ - ariesAskar, + askar: askar, }), } diff --git a/samples/mediator.ts b/samples/mediator.ts index f92d42ce9a..17abf6d272 100644 --- a/samples/mediator.ts +++ b/samples/mediator.ts @@ -15,7 +15,7 @@ import type { InitConfig } from '@credo-ts/core' import type { Socket } from 'net' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' import express from 'express' import { Server } from 'ws' @@ -60,7 +60,7 @@ const agent = new Agent({ config: agentConfig, dependencies: agentDependencies, modules: { - askar: new AskarModule({ ariesAskar }), + askar: new AskarModule({ askar }), didcomm: new DidCommModule({ endpoints }), oob: new OutOfBandModule(), messagePickup: new MessagePickupModule(), diff --git a/tests/InMemoryWallet.ts b/tests/InMemoryWallet.ts index eb16f3eaa3..29dfd807d0 100644 --- a/tests/InMemoryWallet.ts +++ b/tests/InMemoryWallet.ts @@ -8,12 +8,10 @@ import type { Wallet, } from '@credo-ts/core' -import { CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-nodejs' -import BigNumber from 'bn.js' +import { CryptoBox, Store, Key as AskarKey, keyAlgorithmFromString } from '@openwallet-foundation/askar-nodejs' import { convertToAskarKeyBackend } from '../packages/askar/src/utils/askarKeyBackend' import { didcommV1Pack, didcommV1Unpack } from '../packages/askar/src/wallet/didcommV1' - import { JsonEncoder, WalletNotFoundError, @@ -27,7 +25,7 @@ import { Key, TypedArrayEncoder, KeyBackend, -} from '@credo-ts/core' +} from '../packages/core' const inMemoryWallets: InMemoryWallets = {} @@ -171,7 +169,7 @@ export class InMemoryWallet implements Wallet { throw new WalletError(`Unsupported key type: '${keyType}'`) } - const algorithm = keyAlgFromString(keyType) + const algorithm = keyAlgorithmFromString(keyType) // Create key let key: AskarKey | undefined @@ -183,8 +181,10 @@ export class InMemoryWallet implements Wallet { : AskarKey.generate(algorithm, convertToAskarKeyBackend(keyBackend)) const keyPublicBytes = key.publicBytes + // Store key - this.getInMemoryKeys()[TypedArrayEncoder.toBase58(keyPublicBytes)] = { + const _key = new Key(keyPublicBytes, keyType) + this.getInMemoryKeys()[TypedArrayEncoder.toBase58(_key.publicKey)] = { publicKeyBytes: keyPublicBytes, secretKeyBytes: key.secretBytes, keyType, @@ -227,7 +227,7 @@ export class InMemoryWallet implements Wallet { try { const inMemoryKey = this.getInMemoryKeys()[key.publicKeyBase58] askarKey = AskarKey.fromSecretBytes({ - algorithm: keyAlgFromString(inMemoryKey.keyType), + algorithm: keyAlgorithmFromString(inMemoryKey.keyType), secretKey: inMemoryKey.secretKeyBytes, }) @@ -259,8 +259,8 @@ export class InMemoryWallet implements Wallet { let askarKey: AskarKey | undefined try { askarKey = AskarKey.fromPublicBytes({ - algorithm: keyAlgFromString(key.keyType), - publicKey: key.publicKey, + algorithm: keyAlgorithmFromString(key.keyType), + publicKey: key.compressedPublicKey, }) return askarKey.verifySignature({ message: data as Buffer, signature }) } finally { @@ -289,7 +289,7 @@ export class InMemoryWallet implements Wallet { const askarSenderKey = senderKey ? AskarKey.fromSecretBytes({ - algorithm: keyAlgFromString(senderKey.keyType), + algorithm: keyAlgorithmFromString(senderKey.keyType), secretKey: senderKey.secretKeyBytes, }) : undefined @@ -317,7 +317,7 @@ export class InMemoryWallet implements Wallet { const recipientKey = this.getInMemoryKeys()[recipientKid] const recipientAskarKey = recipientKey ? AskarKey.fromSecretBytes({ - algorithm: keyAlgFromString(recipientKey.keyType), + algorithm: keyAlgorithmFromString(recipientKey.keyType), secretKey: recipientKey.secretKeyBytes, }) : undefined @@ -338,7 +338,7 @@ export class InMemoryWallet implements Wallet { try { // generate an 80-bit nonce suitable for AnonCreds proofs const nonce = CryptoBox.randomNonce().slice(0, 10) - return new BigNumber(nonce).toString() + return nonce.reduce((acc, byte) => (acc << 8n) | BigInt(byte), 0n).toString() } catch (error) { if (!isError(error)) { throw new CredoError('Attempted to throw error, but it was not of type Error', { cause: error })