From a53fc54fb2245b28a629e122e7c880bc5a127a2b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 4 Feb 2025 19:14:59 +0800 Subject: [PATCH 01/17] feat: support A128CBC-HS256 encryption algorithm for JWE (#2174) Signed-off-by: Timo Glastra --- .changeset/serious-rivers-clap.md | 6 +++ packages/askar/src/wallet/AskarBaseWallet.ts | 19 ++++----- .../src/wallet/__tests__/AskarWallet.test.ts | 42 ++++++++++++++++++- packages/core/src/wallet/Wallet.ts | 2 +- .../OpenId4vcSiopHolderService.ts | 8 ++-- 5 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 .changeset/serious-rivers-clap.md 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/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 64755bb951..5ae967425b 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -483,12 +483,13 @@ 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' ? KeyAlgs.AesA256Gcm : KeyAlgs.AesA128CbcHs256 // Create ephemeral key const ephemeralKey = AskarKey.generate(keyAlgFromString(recipientKey.keyType)) @@ -497,7 +498,7 @@ export abstract class AskarBaseWallet implements Wallet { ...header, apv, apu, - enc: 'A256GCM', + enc: encryptionAlgorithm, alg: 'ECDH-ES', epk: ephemeralKey.jwkPublic, } @@ -548,8 +549,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') @@ -566,9 +567,7 @@ export abstract class AskarBaseWallet implements Wallet { throw new WalletError('Key entry not found') } - // Only one supported for now - const encAlg = KeyAlgs.AesA256Gcm - + const encAlg = header.enc === 'A256GCM' ? KeyAlgs.AesA256Gcm : KeyAlgs.AesA128CbcHs256 const ecdh = new EcdhEs({ algId: Uint8Array.from(Buffer.from(header.enc)), apu: header.apu ? Uint8Array.from(TypedArrayEncoder.fromBase64(header.apu)) : Uint8Array.from([]), diff --git a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts index 106d371dfc..687a48d59a 100644 --- a/packages/askar/src/wallet/__tests__/AskarWallet.test.ts +++ b/packages/askar/src/wallet/__tests__/AskarWallet.test.ts @@ -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/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/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts index 33f8071052..61124bb3fc 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4vcSiopHolderService.ts @@ -182,7 +182,7 @@ export class OpenId4VcSiopHolderService { client_metadata: requestObjectPayload.client_metadata, server_metadata: { authorization_encryption_alg_values_supported: ['ECDH-ES'], - authorization_encryption_enc_values_supported: ['A256GCM'], + authorization_encryption_enc_values_supported: ['A256GCM', 'A128CBC-HS256'], }, }) @@ -388,8 +388,10 @@ export class OpenId4VcSiopHolderService { throw new CredoError("Only 'ECDH-ES' is supported as 'alg' value for JARM response encryption") } - if (options.enc !== 'A256GCM') { - throw new CredoError("Only 'A256GCM' is supported as 'enc' value for JARM response encryption") + if (options.enc !== 'A256GCM' && options.enc !== 'A128CBC-HS256') { + throw new CredoError( + "Only 'A256GCM' and 'A128CBC-HS256' are supported as 'enc' value for JARM response encryption" + ) } if (key.keyType !== KeyType.P256) { From 589fc4083759e5b0a891ab289e0b24749c2c2cd9 Mon Sep 17 00:00:00 2001 From: DaevMithran <61043607+DaevMithran@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:14:56 +0530 Subject: [PATCH 02/17] fix(cheqd): cheqd revocationRegistryDefinition resource name (#2139) Signed-off-by: DaevMithran --- .changeset/wet-pianos-walk.md | 7 +++++ packages/cheqd/package.json | 4 +-- .../services/CheqdAnonCredsRegistry.ts | 27 ++++++++++------- .../cheqd/src/anoncreds/utils/identifiers.ts | 7 +++++ pnpm-lock.yaml | 30 +++++++++++-------- 5 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 .changeset/wet-pianos-walk.md 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/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/pnpm-lock.yaml b/pnpm-lock.yaml index 5cdf3f9c7e..9d3528d15b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -400,11 +400,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 @@ -2013,6 +2013,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==} @@ -2068,13 +2071,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==} @@ -9678,6 +9681,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 @@ -9835,9 +9840,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 @@ -9861,8 +9866,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 From bea846b28be558cc558ec362bc71f16c3a540fe7 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 6 Feb 2025 15:46:04 +0800 Subject: [PATCH 03/17] refactor: split async `getData` method on x509 certificate (#2180) --- .changeset/eleven-deers-rush.md | 5 ++++ packages/core/src/modules/mdoc/MdocContext.ts | 5 +++- .../core/src/modules/x509/X509Certificate.ts | 24 +++++++++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 .changeset/eleven-deers-rush.md 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/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/x509/X509Certificate.ts b/packages/core/src/modules/x509/X509Certificate.ts index 8cae40538a..0a6433556b 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 { @@ -14,7 +14,7 @@ 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' @@ -271,16 +271,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, From 297d2092743ab1701601b4034a7e5c8461822140 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:43:02 +0100 Subject: [PATCH 04/17] feat: default to uncompressed keys (#2165) Signed-off-by: Berend Sliedrecht --- .changeset/cool-pets-grab.md | 9 ++ .changeset/cyan-parents-relax.md | 9 ++ package.json | 2 - packages/anoncreds/package.json | 2 - .../AnonCredsDataIntegrityService.ts | 4 +- packages/anoncreds/src/utils/bytesToBigint.ts | 12 ++ packages/anoncreds/src/utils/credential.ts | 8 +- .../tests/InMemoryAnonCredsRegistry.ts | 7 +- packages/askar/package.json | 4 +- .../secureEnvironment/secureEnvironment.ts | 4 +- packages/askar/src/wallet/AskarBaseWallet.ts | 16 ++- .../tests/bbs-signing-provider.test.ts | 2 +- packages/core/package.json | 12 +- packages/core/src/crypto/JwsService.ts | 11 +- packages/core/src/crypto/Key.ts | 21 ++- .../src/crypto/__tests__/JwsService.test.ts | 2 +- .../core/src/crypto/jose/jwk/Ed25519Jwk.ts | 19 +-- packages/core/src/crypto/jose/jwk/Jwk.ts | 3 +- packages/core/src/crypto/jose/jwk/K256Jwk.ts | 66 ++++++--- packages/core/src/crypto/jose/jwk/P256Jwk.ts | 66 ++++++--- packages/core/src/crypto/jose/jwk/P384Jwk.ts | 66 ++++++--- packages/core/src/crypto/jose/jwk/P521Jwk.ts | 66 ++++++--- .../core/src/crypto/jose/jwk/X25519Jwk.ts | 19 +-- .../jose/jwk/__tests__/Ed25519Jwk.test.ts | 4 +- .../jose/jwk/__tests__/K_256Jwk.test.ts | 58 ++++++++ .../jose/jwk/__tests__/P_256Jwk.test.ts | 42 +++--- .../jose/jwk/__tests__/P_384Jwk.test.ts | 41 +++--- .../jose/jwk/__tests__/P_521Jwk.test.ts | 41 +++--- .../jose/jwk/__tests__/X25519Jwk.test.ts | 4 +- .../core/src/crypto/jose/jwk/ecCompression.ts | 127 ------------------ .../key-type/__tests__/bls12381g1.test.ts | 6 +- .../key-type/__tests__/bls12381g1g2.test.ts | 10 +- .../key-type/__tests__/bls12381g2.test.ts | 4 +- .../domain/key-type/__tests__/ed25519.test.ts | 6 +- .../domain/key-type/__tests__/jwk.test.ts | 2 +- .../domain/key-type/__tests__/x25519.test.ts | 4 +- .../core/src/modules/x509/X509Certificate.ts | 16 +-- .../x509/__tests__/X509Service.test.ts | 13 +- .../transport/TransportDecorator.test.ts | 2 +- .../MediationRecipientService.test.ts | 2 +- pnpm-lock.yaml | 53 ++------ tests/InMemoryWallet.ts | 12 +- 42 files changed, 461 insertions(+), 416 deletions(-) create mode 100644 .changeset/cool-pets-grab.md create mode 100644 .changeset/cyan-parents-relax.md create mode 100644 packages/anoncreds/src/utils/bytesToBigint.ts create mode 100644 packages/core/src/crypto/jose/jwk/__tests__/K_256Jwk.test.ts delete mode 100644 packages/core/src/crypto/jose/jwk/ecCompression.ts 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/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/package.json b/package.json index f1d2ebef5b..e7d09d5e9c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "@changesets/cli": "^2.27.5", "@hyperledger/aries-askar-nodejs": "^0.2.3", "@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", 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..8fcc93cac3 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", + "@animo-id/expo-secure-environment": "^0.1.0-alpha.12", "@hyperledger/aries-askar-nodejs": "^0.2.3", "@hyperledger/aries-askar-shared": "^0.2.3", - "@types/bn.js": "^5.1.0", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", 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/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 5ae967425b..e8298741e6 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -38,7 +38,6 @@ import { KeyAlgs, Jwk, } from '@hyperledger/aries-askar-shared' -import BigNumber from 'bn.js' import { importSecureEnvironment } from '../secureEnvironment' import { @@ -181,7 +180,7 @@ export abstract class AskarBaseWallet implements Wallet { // This will be fixed once we use the new 'using' syntax key = _key - const keyPublicBytes = key.publicBytes + const keyPublicBytes = new Key(key.publicBytes, keyType).publicKey // Store key await this.withSession((session) => @@ -206,7 +205,9 @@ export abstract class AskarBaseWallet implements Wallet { // Generate a hardware-backed P-256 keypair await secureEnvironment.generateKeypair(kid) - const publicKeyBytes = await secureEnvironment.getPublicBytesForKeyId(kid) + const compressedPublicKeyBytes = await secureEnvironment.getPublicBytesForKeyId(kid) + + const publicKeyBytes = new Key(compressedPublicKeyBytes, keyType).publicKey const publicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyBytes) await this.storeSecureEnvironmentKeyById({ @@ -349,7 +350,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() } @@ -592,7 +598,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 }) 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/core/package.json b/packages/core/package.json index 2117d99997..7b0c3ca4e8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,6 +25,9 @@ "prepublishOnly": "pnpm run build" }, "dependencies": { + "@animo-id/mdoc": "0.3.0", + "@animo-id/pex": "4.1.1-alpha.0", + "@astronautlabs/jsonpath": "^1.1.2", "@digitalcredentials/jsonld": "^6.0.0", "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", @@ -35,25 +38,22 @@ "@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", "@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", "@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.9", "lru_map": "^0.4.1", "make-error": "^1.3.6", "object-inspect": "^1.10.3", @@ -70,9 +70,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 31f04163a5..055c3754f8 100644 --- a/packages/core/src/crypto/JwsService.ts +++ b/packages/core/src/crypto/JwsService.ts @@ -9,12 +9,11 @@ import type { Key } from './Key' import type { Jwk } from './jose/jwk' import type { JwkJson } from './jose/jwk/Jwk' import type { AgentContext } from '../agent' -import type { Buffer } from '../utils' 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/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/x509/X509Certificate.ts b/packages/core/src/modules/x509/X509Certificate.ts index 0a6433556b..7a5a07191e 100644 --- a/packages/core/src/modules/x509/X509Certificate.ts +++ b/packages/core/src/modules/x509/X509Certificate.ts @@ -12,8 +12,6 @@ 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 { 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) => { 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/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/pnpm-lock.yaml b/pnpm-lock.yaml index 9d3528d15b..271f0570f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,9 +27,6 @@ importers: '@jest/types': specifier: ^29.6.3 version: 29.6.3 - '@types/bn.js': - specifier: ^5.1.5 - version: 5.1.5 '@types/cors': specifier: ^2.8.10 version: 2.8.17 @@ -63,9 +60,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 @@ -282,12 +276,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 +310,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,7 +324,7 @@ 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 @@ -347,9 +332,6 @@ importers: '@hyperledger/aries-askar-shared': specifier: ^0.2.3 version: 0.2.3 - '@types/bn.js': - specifier: ^5.1.0 - version: 5.1.5 '@types/ref-array-di': specifier: ^1.2.6 version: 1.2.8 @@ -514,9 +496,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 @@ -532,6 +511,9 @@ importers: did-resolver: specifier: ^4.1.0 version: 4.1.0 + ec-compression: + specifier: 0.0.1-alpha.9 + version: 0.0.1-alpha.9 lru_map: specifier: ^0.4.1 version: 0.4.1 @@ -2756,9 +2738,6 @@ packages: '@peculiar/asn1-schema@2.3.13': resolution: {integrity: sha512-3Xq3a01WkHRZL8X04Zsfg//mGaA21xlL4tlVn4v2xGT0JStiztATRkMwa5b+f/HXmY2smsiLXYK46Gwgzvfg3g==} - '@peculiar/asn1-schema@2.3.8': - resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} - '@peculiar/asn1-x509-attr@2.3.13': resolution: {integrity: sha512-WpEos6CcnUzJ6o2Qb68Z7Dz5rSjRGv/DtXITCNBtjZIRWRV12yFVci76SVfOX8sisL61QWMhpLKQibrG8pi2Pw==} @@ -3119,9 +3098,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==} @@ -4437,6 +4413,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ec-compression@0.0.1-alpha.9: + resolution: {integrity: sha512-x0U9mYW69oBMM/XpteYAm9BL7/hbJhIzCmj9LTdQyBbfi5SbOFeZqGCGf8bNLmM22cwWmbXvQ5QoOqTKeFOBng==} + ed25519-signature-2018-context@1.1.0: resolution: {integrity: sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA==} @@ -4651,6 +4630,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true esniff@2.0.1: @@ -6043,9 +6023,11 @@ packages: lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} @@ -7575,6 +7557,7 @@ packages: sudo-prompt@9.2.1: resolution: {integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. superagent@9.0.2: resolution: {integrity: sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==} @@ -8376,7 +8359,7 @@ snapshots: '@animo-id/expo-secure-environment@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)': dependencies: '@peculiar/asn1-ecc': 2.3.14 - '@peculiar/asn1-schema': 2.3.8 + '@peculiar/asn1-schema': 2.3.13 '@peculiar/asn1-x509': 2.3.13 expo: 51.0.14(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)) react: 18.3.1 @@ -11085,12 +11068,6 @@ snapshots: pvtsutils: 1.3.5 tslib: 2.8.1 - '@peculiar/asn1-schema@2.3.8': - dependencies: - asn1js: 3.0.5 - pvtsutils: 1.3.5 - tslib: 2.6.3 - '@peculiar/asn1-x509-attr@2.3.13': dependencies: '@peculiar/asn1-schema': 2.3.13 @@ -12005,10 +11982,6 @@ snapshots: dependencies: '@babel/types': 7.24.7 - '@types/bn.js@5.1.5': - dependencies: - '@types/node': 18.18.8 - '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 @@ -13542,6 +13515,8 @@ snapshots: eastasianwidth@0.2.0: {} + ec-compression@0.0.1-alpha.9: {} + ed25519-signature-2018-context@1.1.0: {} ed25519-signature-2020-context@1.1.0: {} diff --git a/tests/InMemoryWallet.ts b/tests/InMemoryWallet.ts index eb16f3eaa3..768d032e9e 100644 --- a/tests/InMemoryWallet.ts +++ b/tests/InMemoryWallet.ts @@ -9,11 +9,9 @@ import type { } from '@credo-ts/core' import { CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-nodejs' -import BigNumber from 'bn.js' 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 = {} @@ -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, @@ -260,7 +260,7 @@ export class InMemoryWallet implements Wallet { try { askarKey = AskarKey.fromPublicBytes({ algorithm: keyAlgFromString(key.keyType), - publicKey: key.publicKey, + publicKey: key.compressedPublicKey, }) return askarKey.verifySignature({ message: data as Buffer, signature }) } finally { @@ -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 }) From 281471ed613f7e091bb6cc60a13c46e038bc86a4 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Tue, 11 Feb 2025 11:54:42 +0100 Subject: [PATCH 05/17] chore: update @hyperledger/aries-askar to @openwallet-foundation/askar (#2188) Signed-off-by: Berend Sliedrecht --- .changeset/smooth-balloons-breathe.md | 6 ++ demo-openid/package.json | 2 +- demo-openid/src/Holder.ts | 4 +- demo-openid/src/Issuer.ts | 4 +- demo-openid/src/Verifier.ts | 4 +- demo/package.json | 2 +- demo/src/BaseAgent.ts | 4 +- package.json | 2 +- packages/askar/package.json | 6 +- packages/askar/src/AskarModule.ts | 2 +- packages/askar/src/AskarModuleConfig.ts | 22 ++--- .../askar/src/storage/AskarStorageService.ts | 2 +- .../__tests__/AskarStorageService.test.ts | 6 +- packages/askar/src/storage/utils.ts | 2 +- packages/askar/src/utils/askarError.ts | 6 +- packages/askar/src/utils/askarKeyBackend.ts | 2 +- packages/askar/src/utils/askarKeyTypes.ts | 16 ++-- packages/askar/src/utils/askarWalletConfig.ts | 2 +- packages/askar/src/wallet/AskarBaseWallet.ts | 28 +++--- .../askar/src/wallet/AskarProfileWallet.ts | 6 +- packages/askar/src/wallet/AskarWallet.ts | 4 +- .../src/wallet/__tests__/AskarWallet.test.ts | 6 +- packages/askar/src/wallet/didcommV1.ts | 18 ++-- packages/askar/tests/askar-sqlite.test.ts | 4 +- packages/askar/tests/helpers.ts | 10 +- packages/askar/tests/setup.ts | 2 +- .../tests/bbs-signatures.test.ts | 2 +- ...proof.credentials.propose-offerBbs.test.ts | 2 +- .../mdocOpenId4VcDeviceResponse.test.ts | 2 +- .../mdocProximityDeviceResponse.test.ts | 2 +- .../indy-sdk-to-askar-migration/package.json | 6 +- .../src/IndySdkToAskarMigrationUpdater.ts | 6 +- .../indy-sdk-to-askar-migration/src/utils.ts | 2 +- .../tests/tenants-askar-profiles.test.ts | 4 +- .../tests/tenants-storage-update.test.ts | 4 +- pnpm-lock.yaml | 94 +++++++++---------- samples/extension-module/package.json | 2 +- samples/extension-module/requester.ts | 4 +- samples/extension-module/responder.ts | 4 +- samples/extension-module/tests/dummy.test.ts | 4 +- samples/mediator.ts | 4 +- tests/InMemoryWallet.ts | 12 +-- 42 files changed, 166 insertions(+), 160 deletions(-) create mode 100644 .changeset/smooth-balloons-breathe.md 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/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 703e641771..68bb5ccdcf 100644 --- a/demo-openid/src/Holder.ts +++ b/demo-openid/src/Holder.ts @@ -17,14 +17,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 83dcd146ef..b60940a441 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' @@ -70,7 +70,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 e7d09d5e9c..6eb146e00f 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@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/cors": "^2.8.10", "@types/eslint": "^8.21.2", diff --git a/packages/askar/package.json b/packages/askar/package.json index 8fcc93cac3..3e47b8dbdc 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -34,8 +34,8 @@ }, "devDependencies": { "@animo-id/expo-secure-environment": "^0.1.0-alpha.12", - "@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", "@types/ref-array-di": "^1.2.6", "@types/ref-struct-di": "^1.1.10", "reflect-metadata": "^0.1.13", @@ -43,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/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 e8298741e6..42fa48b8c7 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,11 +33,11 @@ import { CryptoBox, Store, Key as AskarKey, - keyAlgFromString, + keyAlgorithmFromString, EcdhEs, - KeyAlgs, + KeyAlgorithm, Jwk, -} from '@hyperledger/aries-askar-shared' +} from '@openwallet-foundation/askar-shared' import { importSecureEnvironment } from '../secureEnvironment' import { @@ -165,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 @@ -272,7 +272,7 @@ export abstract class AskarBaseWallet implements Wallet { 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 @@ -312,7 +312,7 @@ export abstract class AskarBaseWallet implements Wallet { (keyPair ? AskarKey.fromSecretBytes({ secretKey: TypedArrayEncoder.fromBase58(keyPair.privateKeyBase58), - algorithm: keyAlgFromString(keyPair.keyType), + algorithm: keyAlgorithmFromString(keyPair.keyType), }) : undefined) @@ -382,7 +382,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 }) @@ -495,10 +495,10 @@ export abstract class AskarBaseWallet implements Wallet { ) } - const encAlg = encryptionAlgorithm === 'A256GCM' ? KeyAlgs.AesA256Gcm : KeyAlgs.AesA128CbcHs256 + 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, @@ -518,11 +518,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 @@ -573,7 +573,7 @@ export abstract class AskarBaseWallet implements Wallet { throw new WalletError('Key entry not found') } - const encAlg = header.enc === 'A256GCM' ? KeyAlgs.AesA256Gcm : KeyAlgs.AesA128CbcHs256 + 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([]), @@ -583,7 +583,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), 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 687a48d59a..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' 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/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/core/src/modules/mdoc/__tests__/mdocOpenId4VcDeviceResponse.test.ts b/packages/core/src/modules/mdoc/__tests__/mdocOpenId4VcDeviceResponse.test.ts index 929ae180a1..6433cb3aa3 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' diff --git a/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts b/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts index c7295a2f09..50a307070d 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' 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/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 271f0570f9..b50fe91067 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,12 +21,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 + '@openwallet-foundation/askar-nodejs': + specifier: ^0.3.1 + version: 0.3.1 '@types/cors': specifier: ^2.8.10 version: 2.8.17 @@ -123,12 +123,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 @@ -175,15 +175,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 @@ -326,12 +326,12 @@ importers: '@animo-id/expo-secure-environment': 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 + '@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 @@ -653,12 +653,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 @@ -890,9 +890,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 @@ -2480,13 +2480,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'} @@ -2714,6 +2707,13 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@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==} @@ -10606,22 +10606,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 @@ -11006,6 +10990,22 @@ snapshots: '@open-draft/until@2.1.0': {} + '@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 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 768d032e9e..29dfd807d0 100644 --- a/tests/InMemoryWallet.ts +++ b/tests/InMemoryWallet.ts @@ -8,7 +8,7 @@ import type { Wallet, } from '@credo-ts/core' -import { CryptoBox, Store, Key as AskarKey, keyAlgFromString } from '@hyperledger/aries-askar-nodejs' +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' @@ -169,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 @@ -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,7 +259,7 @@ export class InMemoryWallet implements Wallet { let askarKey: AskarKey | undefined try { askarKey = AskarKey.fromPublicBytes({ - algorithm: keyAlgFromString(key.keyType), + algorithm: keyAlgorithmFromString(key.keyType), publicKey: key.compressedPublicKey, }) return askarKey.verifySignature({ message: data as Buffer, signature }) @@ -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 From 81dbbec671adb6e8f4761f606b6b1cf3a8c8a2f9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 11 Feb 2025 19:45:52 +0800 Subject: [PATCH 06/17] fix: typo statefull -> stateful (#2189) Signed-off-by: Timo Glastra --- .changeset/cuddly-jobs-learn.md | 5 +++++ .../OpenId4VciHolderServiceOptions.ts | 2 +- .../src/openid4vc-issuer/OpenId4VcIssuerApi.ts | 4 ++-- .../OpenId4VcIssuerModuleConfig.ts | 16 ++++++++-------- .../openid4vc-issuer/OpenId4VcIssuerService.ts | 4 ++-- .../router/accessTokenEndpoint.ts | 4 ++-- .../router/credentialEndpoint.ts | 6 +++--- 7 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 .changeset/cuddly-jobs-learn.md 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/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts index 9379a79b8c..8d4f017c96 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-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 2056824d14..3568d7dd68 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 c874fa0995..19fa92882b 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 5148d1a830..64014685cd 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 fc27ced0ed..60ddf57437 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'.`, } ) ) From 92bba5fe9f23927305824ed5818b154f641253b8 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:31:11 +0100 Subject: [PATCH 07/17] chore: update ec-compression (#2190) Signed-off-by: Berend Sliedrecht --- packages/core/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 7b0c3ca4e8..a374871acb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -53,7 +53,7 @@ "class-transformer": "0.5.1", "class-validator": "0.14.1", "did-resolver": "^4.1.0", - "ec-compression": "0.0.1-alpha.9", + "ec-compression": "0.0.1-alpha.10", "lru_map": "^0.4.1", "make-error": "^1.3.6", "object-inspect": "^1.10.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b50fe91067..8029443174 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -512,8 +512,8 @@ importers: specifier: ^4.1.0 version: 4.1.0 ec-compression: - specifier: 0.0.1-alpha.9 - version: 0.0.1-alpha.9 + specifier: 0.0.1-alpha.10 + version: 0.0.1-alpha.10 lru_map: specifier: ^0.4.1 version: 0.4.1 @@ -4413,8 +4413,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ec-compression@0.0.1-alpha.9: - resolution: {integrity: sha512-x0U9mYW69oBMM/XpteYAm9BL7/hbJhIzCmj9LTdQyBbfi5SbOFeZqGCGf8bNLmM22cwWmbXvQ5QoOqTKeFOBng==} + ec-compression@0.0.1-alpha.10: + resolution: {integrity: sha512-40i890RKwkaxUqAI8XZkELoKWhQS4VDfHU0D0lYwrFjfA7vtUZWaCNCbpoGwCMZqrPrWGfXqej76/N44kVKxMg==} ed25519-signature-2018-context@1.1.0: resolution: {integrity: sha512-ppDWYMNwwp9bploq0fS4l048vHIq41nWsAbPq6H4mNVx9G/GxW3fwg4Ln0mqctP13MoEpREK7Biz8TbVVdYXqA==} @@ -13515,7 +13515,7 @@ snapshots: eastasianwidth@0.2.0: {} - ec-compression@0.0.1-alpha.9: {} + ec-compression@0.0.1-alpha.10: {} ed25519-signature-2018-context@1.1.0: {} From 312a7b2b90020bf285e3df10682fd490145d2dab Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:31:43 +0100 Subject: [PATCH 08/17] chore(mdoc): update dependency (#2191) Signed-off-by: Berend Sliedrecht Co-authored-by: Timo Glastra --- .changeset/brave-cars-deny.md | 5 +++++ .github/workflows/continuous-integration.yml | 2 +- packages/core/package.json | 2 +- packages/core/src/modules/mdoc/MdocDeviceResponse.ts | 4 +--- .../mdoc/__tests__/mdocProximityDeviceResponse.test.ts | 4 +--- pnpm-lock.yaml | 10 +++++----- 6 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 .changeset/brave-cars-deny.md 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/.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/packages/core/package.json b/packages/core/package.json index a374871acb..8371c67afb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,7 +25,7 @@ "prepublishOnly": "pnpm run build" }, "dependencies": { - "@animo-id/mdoc": "0.3.0", + "@animo-id/mdoc": "0.4.0", "@animo-id/pex": "4.1.1-alpha.0", "@astronautlabs/jsonpath": "^1.1.2", "@digitalcredentials/jsonld": "^6.0.0", diff --git a/packages/core/src/modules/mdoc/MdocDeviceResponse.ts b/packages/core/src/modules/mdoc/MdocDeviceResponse.ts index bb087d76ec..35664d6650 100644 --- a/packages/core/src/modules/mdoc/MdocDeviceResponse.ts +++ b/packages/core/src/modules/mdoc/MdocDeviceResponse.ts @@ -245,9 +245,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) { diff --git a/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts b/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts index 50a307070d..8803f59c65 100644 --- a/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts +++ b/packages/core/src/modules/mdoc/__tests__/mdocProximityDeviceResponse.test.ts @@ -74,7 +74,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 @@ -152,9 +151,8 @@ describe('mdoc device-response proximity test', () => { '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/pnpm-lock.yaml b/pnpm-lock.yaml index 8029443174..7769c6ff75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -428,8 +428,8 @@ 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 @@ -965,8 +965,8 @@ 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/oauth2-utils@0.1.4': resolution: {integrity: sha512-JUIshkNCiX7myPwdiE+Da/g77OjsxZreezmfXWBSWOgCOZrnQX+t5vkhpe6Jc6DRh40Y8r9Fa1AesV0vtVpUMg==} @@ -8365,7 +8365,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 From e610d388b21200a938076ed69c2ba03ce20f2132 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+berendsliedrecht@users.noreply.github.com> Date: Thu, 13 Feb 2025 15:06:37 +0100 Subject: [PATCH 09/17] chore(mdoc): export DateOnly from @animo-id/mdoc (#2192) Signed-off-by: Berend Sliedrecht --- packages/core/src/modules/mdoc/index.ts | 2 ++ 1 file changed, 2 insertions(+) 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' From c6369e4740c0d6dc430e9d354dc7fa1f39c7da5b Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 17 Feb 2025 23:54:08 +0800 Subject: [PATCH 10/17] fix(askar): use compressed key as identifier (#2193) Signed-off-by: Timo Glastra --- packages/askar/src/wallet/AskarBaseWallet.ts | 37 ++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 42fa48b8c7..5794b65a93 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -180,15 +180,17 @@ export abstract class AskarBaseWallet implements Wallet { // This will be fixed once we use the new 'using' syntax key = _key - const keyPublicBytes = new Key(key.publicBytes, keyType).publicKey + 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 @@ -207,16 +209,15 @@ export abstract class AskarBaseWallet implements Wallet { await secureEnvironment.generateKeypair(kid) const compressedPublicKeyBytes = await secureEnvironment.getPublicBytesForKeyId(kid) - const publicKeyBytes = new Key(compressedPublicKeyBytes, keyType).publicKey - const publicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyBytes) + 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)) { @@ -254,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 ) } @@ -265,7 +269,7 @@ 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 @@ -278,16 +282,16 @@ export abstract class AskarBaseWallet implements Wallet { 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') @@ -566,7 +570,10 @@ 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) { @@ -676,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, From 39ad3657b2913dfebe8aa29505ea158bfadddb36 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 18 Feb 2025 00:46:21 +0800 Subject: [PATCH 11/17] add local packed files Signed-off-by: Timo Glastra --- local/openid4vc-oauth2-0.2.0.tgz | Bin 0 -> 170472 bytes local/openid4vc-oid4vci-0.2.0.tgz | Bin 0 -> 157900 bytes local/openid4vc-oid4vp-0.1.4.tgz | Bin 0 -> 135757 bytes local/openid4vc-utils-0.2.0.tgz | Bin 0 -> 16512 bytes package.json | 8 +++ packages/openid4vc/package.json | 4 +- packages/openid4vc/src/shared/callbacks.ts | 2 +- pnpm-lock.yaml | 63 ++++++++++++++++++--- 8 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 local/openid4vc-oauth2-0.2.0.tgz create mode 100644 local/openid4vc-oid4vci-0.2.0.tgz create mode 100644 local/openid4vc-oid4vp-0.1.4.tgz create mode 100644 local/openid4vc-utils-0.2.0.tgz diff --git a/local/openid4vc-oauth2-0.2.0.tgz b/local/openid4vc-oauth2-0.2.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..51579d4c84efc8430b3107631a456ed93004579b GIT binary patch literal 170472 zcmV)5K*_%!iwFP!000006YPEccN@2{Xn%eFD-hNvBok^S%d(TuQB>D<8oQ0HYuQOp zY+rH31;w?BN8rCZJ3H2Y@9y5c^UWQybNAl;ojZ5#-{09IJ9qBx-u;IB zY3GK=;r+KPtGM{n&b9lpC-*<(UuRiTQdUJet~w8HeTWNkd^|2_Tv4)5UYxwA<0@jm zMQK7Ohef_n#R;9H8HI1OsLn;TQ(C=PW$^VeEyt?1`VO_BtVkDCUbyX^$8%bS8e*Z= z=CH`C{Kdqmj^C>D1${9QHK%dOfvB1bQ7uxX`b_A@MP5{7pY*D@IHlEq#IsqS?EgS6 zZxJ%d3(|v%Sv;pC&9LwmAw;1{wu34Ia!F2UHH3O^;KRECq1kdyi}++lhomZ&l&t#? zZms2Vu?5Gg^@|dQS3*yE52MpG}o@{DqF+XdKUGINURO-iPr3pfC3K z`M^a(15d2BL{Sgt1^v7^qvsZ)zUU>4uxwiEG5kQceV8j$i&RTQE+Ok%gcP(|78$8> z4ck@z^gK@nBrTuk3H>=w=zy?Q#3At&CHrKaC*+HLk}YR5av=9kPp3(M)bEoa zxm*wQiJTMO>vP=`5|zp({%Af>cweQj#pM zNSu);M?W(_lXON&HH|AWjZ1PuX-39*_JI}^O-PlKm^{ko^E`WY#HudKG&?0R`D=Va zXIl)0=n)_laaK<9VorK#L?hBUKBnc*d9s|*4)y^3V=pDG`U98*{>cD!5;}>Ov#J9< z!~A65ojJmY*tRM=NNMhLBcNu9eOSjY0N4D#ks1u5-QH=kd!VHc@oY(l1Q*n^)yR-9 z?cf%HO1JubMGSJ-j3RLL^)*yo!|Lid(t73{4FG!o!L3`{+oUYU{J5=3?5r%toYTNQ zzg$1KB{)w{D;nVL{3$M{4`-)&kyg{W*e1qDn99@aLp)29A89q7zNFw2VgJaWi{P>xU8mmkzT~WDvoIJfl6S(3aa&^SxU3YY{0(Rbqp|% zrtxe>vr}{MEvws*@uL?T^|cfnPa|KAM|na^Pc{6m>ONalhjHN@icw^=_=OhfBpu^) z{SBQOs{Kn^=F4JCeamN-xouu&F-J8`JRJwCDM$=*(<+2&0i46LF$E;bb^e)F(>xIX z0>#ksXt_Whn*hDgtm61N8c*q*iuSdY07`5STb|Ir6>(NQO`z^1RTyom2e8KRY`mPs z6@9ikdo^9oPZmY0c8I?e+exxlBV?3&M8^fK{u-C*Sb}L&xee6AysX*+g}Nb_kI%Ds zUOzrwB?RGlp3%DIP!4SqI?faNY$b;+KNzJy(Kt~HXMKj66X1y+-u5%C;v}wODXKKo zldztx&Pt;Ze2}HT(%J0qS-#4=@SRdb7K#jTK#J{vB6W2Vpz{p>kY3Td9GoPiBqEt-&zx zY;`7!o~_QrH;uOWPhMg%lnaBGT;REWKFi}oufRWH4NY7S$Dgd~DZ@60Eor^6B0_uF zOzm}bZowVPl=JbTrsQc|sO93eN#10i6!c%qw4lAtU-AXb(q!+$cpFPQnh-rkfZG>a zIU;6@0%n9z%|Tw|3Ge>N(a${z_99O@Le{q>G_p^&zaoD*K0bW?^2zZr`D&Z=dUmB< zdGB^g-gGn>rt^;MlTL{rj&XpU2mVG}27iV35!NhapN`obrJ*6;w`R+Ne{ARz*3s$` zKLwkW%gH1izIx}SLCc-Hw z@~L>{-7I5S+_Y~TK#ysb(u9F?!5k;7xS6yRU_X6nurjed#R!e!j*^_KsoG)N;591=9(~1_Q35CiU#|u@Z6|;O@q+Wh`ye!go5SSv_ z4}>dSgF6drJrGhXw{ArS+B)1_&az>C$yjry@k3dWc9XG!!!#)+Bu%H@`hs<)T}Y; z-XF(RY#qR=@E9c85UgK8*HOLB;tz2;1LYf-6m)(F`SV4T_vtk^;8oX%<&Bd5tU6Ijhb)1M&v0^PU#k z8WVC>oe#aWW@$x>c-HGYdDKDt5;876`0EPfvT7gOR0{MeD#1$+*1UlUy!iX!&CcK^ z2lw*mp%@%(GqmjY8(KcD#L&)K^m^-EKa!76y&mvA<9!o8L}%4Gd$GuU)d$ z!D{2;!+?!Yv>L#o;E#_3BEFxKwRjVBd%$JCVi$FAam8%7l((b~%#8oY>R=+OZJ1{Q zj0IBx0?xG}8C?Kq5KqQ$t=xI`#qdfh^H~?;vGltnU02#(P5E)NE4dC07#Nk_G&qgtMQb? zM9mS3%c@w8t7SnW@+7UMw7_@E?pbx-70<1*!2lj&IoD0tGha#Kf|7Y!!n0~Lx>H;~CPcMKNk96^i-! zP3LGD@7}x51ev3s9&W)8Z8&Ci6kW`&h1?=WipTo4CQXPDYoEwI8PDRfB=loNv!rC( zLD4Y~NM7{jv@GLODiJ4k99wJb=x0_1ECl#p2M46{Iy(c!J*z_U@pxI#B*JNl*5i0t zQq`LGa1h~ll4fNUXJfG4U_(0~BXavvEFe~jc#rE58QSfQ>ii?t>+nI8%LOfZquZDI zEY`O#^QtyLy{1Zi{vBpV^yHK^^7U+&&nV3;@K+1Nucz#-e-gm z&**s>P10FKi(c=Nz^K+pU0=YTpFZIi;F6-%vZUG;eZM;UF)#j}CK}wcG$99Cm(e86 z5&#Yb3^@Fy13gOa00`T-!&AdL%;ef^+is{B=xnn_KJP=|c87-wd2i42x6KghFx{;8%?E zkgv7}vZ~;_Lp22CmaNA6)fq}I4}?9KqNpODAJGa7s9+8zqfq6Wj((%@x0wOXVW^D= zzpnW{7bNTsc7NYQeqWvz#gbZPR+D%Jd|~Zl2^U*H7LL~bZks!fai6*C6H+Y^^{!VW zwnTajkhtj8K|l~6uL~eB%=Q52bpd481rX0puL~l%Hh{&oA^hvwEAg?q0*I45pV2tu zFwU+!h%B(Q9?&MhwBdX+SLB9^tuq1<+XX&$9v-|fMDcR3!Fp0)^_-Fm z_v!>R$3%nu;fi^CjbffR%a>BrvofA`+RTNe+ve%sml?s)&**ZsFq{g<~} zZ@+%Kz5nj3{_SmM0xrkjc@Ay>1mBC81wG#Es?M#o+SL`Bh{4MpKB1zeQDCgvVTxA* z*v!@g$@VsR`QxMS_V&Ju@D8adw{qsp044=wu{@cjV|4qdaET#Kp;-HVpX`b=-PWBM!a}bD z++8?bac=v5RSn2es@2#)JJP!hoafklo|Sp_0I_?{J5%31?*Vr?E??zPA@meNJnh6o zg8mHP)?x!?j}3i^lQ%o>)UBnXw1xN{19RnrhHvn+&|!BFoGlsSiwYBL+97mgib&6X zeH9U|b@pkwnOUb0u|QwVwa{xx1yiwq-5H18l;pz)Hbozg800t2Vpm%#{WU~iMi_-x zuVu@(E=}JkHG_n6_p_4of^m13p0xtc0_1h_j3+zyC zf;YSG#P*WA$L}1{EAc;lX(zybN6ZR~J&z}8;yjOY0>M+AH;HGbf^P7P=AV-^d0oty zW?X-;@pOde=p#5ruhHLGnuyQFxy*}IAFYP;es#9tSWw>JpP4ZKOgyZc1C@m^nWRE62dN#amd*fm7r zopw|insG71=8>T^7YNkOXX;?77MiA1YDuH?RV9O6XeT6Ql%9lAx%Mt<; z_*-g~wB~C)-htLI&Bn83Ld#y~&mB*73wIj!fF&tiF=H|M^i`RKRMCUJL^XK$a|eSk zXtO$~#nYxE9^JmwV6Sgqs&TAG8%)Algu`f>&i9MZ#}RyNTz1%estmLj@~@n`YqnN0JYH8yRY9y#_zAJ-*&n4 zyRANnGJTRabb%R1;3_)Xkk`Gbz75s7iY9l!6HEqi z^T&cH_*$Ug*G@PXh{3mI@3m3+H^7y+I&P}i2AOGA z(ISgya?-2SN^|h1O!_aKt!lONY*{~`(BAdeCi~_Q^qD++;}J_)DZA z7brv1FhBs6VRwfgp$q)O0E=Jz zpnS-b#cN?*(AN|npw`KCnzO_&m&%xqCO~HLk`mlbNHkpBv)8!fp;@@lWiPlQE0+d% z6S=~;!|T^qZ(^^K-JGwH92j2#yIK@QLSPIpgS7E5)*-;!L!oiZ^DI7coM=YhF6@pw$W*4loJxaA8pARYB0b4n}DV?mRNE}KYQ zWpAnewx~)gF&FnDX9AwFz-%5FT|EV&hpj5AMYs+4;f;S|LtFxLT)*WBw&u0^Omxw3 zZ|tZ31AT=6YmxxuF#3H&=VDn-wT~_uCC=Wwb9yy5cmSo=(6v2=A0m)@a5*)07l}sT ztQ-kPZtTw4haPO{tH?Uh*V5MYZ90s&I9<-s>&qU>8wN^VW21;IOeC8GmVLL~y6^V; z@7Nm1?Gz#bA(UQsvTSA;BBT9uSvkbr_=C(>=jQOhyJM=d-AWeA7n@kII8ZUlR@`~W z<>y-U09V%;qJhpei7SE{LUQ|30a}k#Piu=|)7Y z>x+Z#iM~zXAf{3lbt;0V^#SpXT-t|7J+ip#?C+Dkojpq;fUA}My7q-^mdmFX^~Px2 z8Nwlh<-HJ#K&=TQCCvL(ED&=(?B2fg&0^iVzX-jg_;(PT6r zqC+Y-t@cvI3L|Kyze3gDQ!lnQzNvn@nML@w&9>a(h>gbU*S7(q-6btd8`y87i=V1$ zQNpf8_0<{8j%k)Gatx;1x+;bd<57D`72Rm)ACv27@omsb^4ow|Prlkqk-0!aN?A7_0gi+$9VHa!-pzk$#9PdJHa=HT6{-D(Rh9hmOxe(zb27B;y~` z!-u4o<*@kVoMH|Rlg89mzZoh6X_<`gre`M^6dhKt8du@XW{Rd5MS3jI%sDpXbZ+ znE8oZ3m2gEENStK)DVsHYhy zIg|``L3Is0o5jrSPm&}(X`gE}y zO9J?jl2|QRXVJTSFylO{;xsEo;GeFA(k>{AyAImAqgy`3{?#88r2Oa+sg58z>PHBk z6REq=HU>%d%78rklLYxmo&a;=b(SP1~uy(=KnZJN6qJMsN`u2JjLu0_GLcz2hn_|c!4y`z zq8g0|+aZc;#8m*W51H$(9e51-lEHEKoV#4}RltSSrx6tFH8dO3bHKA*HXxIho9)VE z5*(dQCc#RdhfJEfv&e+;J7VxVn?PEk@n#7mI1fvGz|=mPS$w;kAimvO%H=lJBgn9{ z+kPE17HkXW=Id2d+jX<;SQP}p>`Q{tiSdh?rXj~4Q^)nt$1Jqem!#X z^(0A5a*%2Kfs&YX#TaxpSLJNpgN9g}Rc0mfxxZSr(!$@+ z_^b@4lO^a|xc&54B1RffgV!qa1$(wSGksQBcZDeyiSFbOWyDoc!l0@5ttrp-V|rUk z&l6#yX!%Wwa_(WF%J~QF*~_36Hn0>f{vK~s$A;YvHA56Sn5zM5t#l0eeP3Y=jxTGt zV=+8z^1#dW8pjK9S}>TXFw|XJ%qq)Q5Grz%$%(dW6@cjFUn3P@Y%rM_ug7t=KO0sq20B#qsXG~VK+OOba|3nkaH(XaQGb= ze6i&$=*NXKiyyqTzP|BU(2om!7K^y7%sEKD#cdNGE|V0pOXw4E%D&J7$%^sE%j5>e z8!r>*njyMpYOJV!{7;z-?HT=3bse+Ep8C$TjmMsr<@LsnS!aAhX9d2IWB8n#pmuAmU1 zi^YsD)^=Qd4etyC(&2x-3r2484%HU~tZu9#Y?U0W6&&n4=Yh`~x9iSb8IH>v0n945 zB8vGtDVHbBmEb}5$$!0m`tr$Ruzbx6Fq4P-9=!CyJMVy;EGzjEhq6maM6@t9I|!I8EPUQb2Db^FOlzw7Ik%udI-%Hxx_HG_#v}BkI&$T z`PD0VH9FdfKEQ$v|IZ7i(FflEbFmc!)EF|^lmt;TUoQ$m#@K*Fb!TTIk;;*V_6%ug zS}omj-CA>6&HWWvB1VtYp$*UyJ6>e7bCtkp1tF7oVo0F(G=r^v0=e*j;!!KC(>yQ; zGoZbW3Rmv%j3X$|a(sJ)?OJptP>WO9dhdzxV{wRn6@_W;fiymtVT!o5ver9U;aCVBJ zli*AR%cm4T0t40zAK}u#k13O7q9fZV-|89R_GKCFxf^J4AhKM zGYGKXgoDad!*|ZBGY!pg^jJ*JezL~qY}8C!A@z(}iJo-K7!lYu*dCO|zzij+cQVc9 zLvrl$`eY5w=yBb>+B{tA9-nemaUPtR=y_Vr+Yy;NmuWS3XELqk&OD5Y{xJnEg95}) z)spPDTS&#FY9OQo$Asz)um^uT19OHUa*QFC`OHj_r&9&b zp7(p@{`1q25%AXzkf{+u?YwY3^yzHKR@0IxKN5D`g2W>Dhn8JLE`i>Sxmb2Wk|47+ z#vWw4el?K&>T^S(Nb7Vc!I;^OO3Dz0Za@(vg%Lvu#4L+Cbt)T2G&IajUEW{FKXqYn ziYJ9*8+I>x{qi4rlLt(L5S278#=v2j4?lbY5|NcpS4+TJ=z)zJ<*?L8#AGRS>3qId*`n@c*LHmtV@L4dN5#I%1lJ4(R0hcg?3~ z(CJHIR3-{XV>|A5??%QI#b9qT5Fq7UT*aR=b!$>(uvO5iIQJ+Hye93qu5b+mguR58 z=1oDFaS5wcbd<#>Jo$@{MYmRLUb3ocB$zyA8`mcA%mn?^q`ZQle@Z~oq79hHT-144Rr z6{y%a)D9d~yQ`@eQ^8+K!d*g$e*tV+$QY;F+pspwsQ?rIu{Q<~=KuEO70?JER`@bm za2%QUh$f|MuinN0*I-;H^)~Ftn0Ftw+xF>P``Yra;v$$lh08=94|to)0vC z5)I+j3pUKgd^Z%&G2F#OLDB-jYj5T~9r=7&v@_R4p?+m3y~b%4$}JhfI8iaRgHBZk zg0l^!)yZ1dFe+rJXsuBcX&}@UOVdZU^S+T&HhPMKJQZzn6oinC+1rUEB-DN9@{}42Q5-HGNMV zFey?M9~?+Sbyq+R&+eeL03cS}l9v5)ad)}R=_8n_((e5z3!UqBAuJMrO7JzNc zLUtS77ri!yDj0=nU}&5io5bT{EgEA@JHv1E*9;gdGb?2yriOP8jnSqpE0sHqRw{Vj zJk>kq+sm||xZ|^+sMu-mt)`oeuxMc*Y8TjmGH=n^EWG;L)P|Fgc#?NnHP->m74@)=kt_Q*?TbeXEIiRo5Tu=9Wh31y& z_|-6A8L3|lh-FTHH6XTe*VQOCYeZ=vr*&&naUUDEE^?|$&SvK2=s`oAAgh(uL+0(NQr58!2Q}(|-}p~yR&8v`Vod4@kKt5ri!FmC_ya&m z1&0@O!*lb`%!5do7Y3qC8EF?AV$S%J9haV`k=>n0T$R`;JKQaQ3-H?++Uu-4?f>)F z)#}H*n7=M&>@`|pI`jrTmI=j_GXU=xl?Gk}vK5x^V}~z}UWpug{-vt4bhb6C$zYu1 z$@$Pr>Eg*u&Uiy?7#C~Do~kKw0^iI`*;RAUb3?W8z%ceNs}#nr5T6+~JR6+J4X^V5 zTGHbD5GWDCQhBh}*Ivq;w@q#Bbj&bX^+tUFRbg}F15<)+5eY&icvf@Z7V0A2t`}SD zZbV+vF-hCY*rO=TQHv^kH^v7U+v&P1O#X1X7&C- zDo|JrOe0T5%cqWcwKyjj{Za)^0$sFsO5FlufTB`!c9W^%hWx*~b~Y@5rZ-|0G&SQpI}DGog2P7ha0%-8gWNRJKkFqQkC$*&2*)!5J_1x=^g1Lwpn=So zA_-QhjqS9FJVRnfB+6mqht13NqsCzl^~9BKi_SSKebTy&njFJ<-DN zSnF%(sVVuq-mfS8L|t4$EhvR=LuiJL(h5o5Zq-cLCbn<-y3tJIO@{qz-qc$ELzgLN zrQ(V$*pkNr$2%;4pgR>7P&CEpJsKJ!rjhKElY+))(#Xeagk=ZXa8{9>+3j7R!+Wpu z2;Ot>FkwuVUligJcUoSMWyu04z*keBt);dxH`d16%Co6DtPVfuuvN!;Qh^=9Z0^n+ zH?~^qlTV-E4X9dysEQdnVP!-ZbC6&Y&_vABjDkO81Bh0TWJ)VS(3cFSafrlHUAS%G!dZaeC&QF2~ zktI!{In1KS^NCKFfSp75Xz+Y5f!^E{6zk!CImZ4L{`s$~tbiP~itnRo=k(!6qrnCz z=O*Z^LuQL%2^Q&MsdiUfjZPUC65e;-(T%eS^dka8z{!ehs~^)>r_&Z9i65gkHu8G% z)DM0mX31mAt=V*TR7hxdrrSo==1x%cq`j8K)Km?xP~dkpAX>-^O|^Tw8`Vep!KGzP&nLPb6DJe#mE z7WDz6!tFd^qiQO@>9E2h%ue#xxJ<_^B$S!85wPxZ-@SclqEq9IEifx<_pL0#1k~9bRSfQrEVqE!`Sl4zQ-k{4BUPe$bD4EL zv~LitaxPwl{96J3|A*{bq2rHA?wst}lXknsza~kG?9je55)N(WNF`rw8_rBVV~NM- zSv*h2<^wJ7sVD9PFGQhKn+u}@^alu9O`r*a1;kK<8)q2+!xJqCFTg^;C%`%chomD) zJ9>|Ev4yXGFtedp$FtgiqKEx}G!t4WCrP|-wVZ}>-JG?uFY4iOp+sa43&f|Zj0)Pd z8&v(9`s+;XeAXwDg;+pH7LI{F0t_L@5888sV6K|RJr%@MqnC`xdwf^yT14$o?33~kdgB35m;Cc?Qd-G z>rtOmm@rf*T|1HrO_LEm=ohD|u@!s06DviIT}3$I!op`)D@4vbqlv50XzShr;v z2iZ3Ebv;vk?u|Bq;x?X~6*kTr1#b7Q9R)7SSA7}7v`qs~jW|ho@v9srh)`9iTl#U4 z7PM5kDMYZhWuiaJv&gC3=&!7Qv9F0lw|n45cirf#%OV@5w3-aj2)rC(YTlt5)o@WznW>j1$FOUK8gbFwdE)??U?!3% z=v?F~voY9^G1j;lbYu6%SYOgfLCYyJ_)Ujdu(tetiI1?T1z6Xg7}o}*tshTb7qz=` z)M-}HqGT5;ke|JkH198bjz26^<^+)3A&K2&hBjD)bbK^53;_1{)ZF#^)L_S^_QLEJ ze%(Yj1x?a|jw{G?7l0>PfTMh`ufVH;^7Z7#9k}5*aE&%@X0+_}rzI@T!Bos^aoFYw zJ?3fOiklc0PTBR3Q}ve_P~01Ss5NpDaWpn=V_C6gOKFiM*suu%$0&JDT>2q}bR~I4 zA#(vdB^BpPbCq)C)s)T_%zB@_i}B~Ll7p?q57v3tXXc8Ar`Mp>_NzV~-xQ^9Z=;#b z@ghZQAoO4m*;|q#o>b({9jqv7U9VHml%s-9(u_j73O=ua8WOvJe!&aSFiVzw`lRZC zvO~6JNSceta-*jca*|h5(&aU}B+e2H+k%a|s7K&Y*`ti7+$p7OR>6JNIVfOQr_+)lYG79$nBuT-JU=9fx@}jT6s+Py8 zJVMGLx!^;*u=%RZ<-kcz6B1e^88brvfqbYbPsQiQN+Nhg2Y3>^ZhVHI21^Ccb@Q)_Wp=Zg2$`VOm^71h2`6 zO?yS9XP#V2tmaDozJdlF1mI5^4Zag=X}Gblk;A1vdl0FpG(UDI){LV7=L20 zlMK7)B>TZdii9~lSZSl2Ej=FHx=Id5IjQMVZ^Ybp!M*Wb?%su;pBvZoimU2hSJx@t zGu$q|awA^Y83A#*8_=R&a!uP<24!DPAAd8S?}UKUH@^3BSZ~Nv%Uf~G=UUDABWb_yNK81qf7PrdaM?ng$7kce-pE4 zV5+aC)W%~SeJNK<*^{3)a_;xTyOdBQPxYaJXA zrnO_Uz~?Ugv5TVuz9W8EWZ`#KR0bdD4T3UgkvQ0V$>HB0Jz*MQLA{ZT%;4(`*-xAi zI$u;gtV3i6z!`f}?i<8X5sJcpn--yGY^l?oJiDfPXZ-4;K24O= z>#O{`Z3H!Z90TJAOb^Vg-9VXnK;x zGnobqRR}VYl^=SV@$?!zbq60xT=eDPJGL(UgWR@3he>l_zb0Cc; z!_u{5sj>NSIS!o{f1pFLJ(n5(j*MmeKaH2%F!R`eTLe(`zYOBDGl<$D4Tv0t*1OFo zq+p^a%6`8JhEM)|HM$)~enK5B1Lxrs<_xUBaw;B(nZjJNrODBJEy(39ux|*iWFGBr#yW1q64p_O}t8)$U4^71Iwg*<1 zL1@zyIeU#BD~8PPbv@Yg8SsgUnk36-!+82(p*jJAGh16f;oft%puby$qe3C@R8ZCQxTIx?fhuL zUOxQsm2mN3DB>2Ma5BU!*`Ho~%!-di@y(1TX!RYSW#9Jo60_?S85n327x9n8Hbrk5 z4$zp#T3yl1%`R|=d0h!ufVYKVap}&?h$d)mxCznSIC0&e^lUo|)KaY1=%|Utoww!h zYFr3vfqC*4JGS_qppAUrcmp0hVy2g-e|yuMtOz4Dh%>fHd+gc!^7<$=AhyTj_BQz& zS`f|1kLlsVTiUHrYVG0Y;8x&rXw-VFO4KqbcA+oO5&I}daah0G?ER)a7Rj#)*29;# zG;J7TFP3)nb&x&oLjS*XGdbrqfKYBZ9%mi%RDJ@wbpQ@irDlET8klPT0YP#zBELIxu~qvx)s@CmFM@QssgU7) zHS}#tpLljDeThWmyS2=CpdO9i>#xIO4`^f@R?X7pMa1sO}srKD4w9w4bhQWcr;&mPJefbZ1 z8jsH(LmaqW-ep~${S^aaN9eZK+kiNe;c2#bi32H=jXZL zEjTg!n=W}_?iRl1)db0}=UJ_;dQxw~9|M8V%-aJHoOKa#7H(rk{5xwmq?khX?FYn% zl`p`*`x0bvnu&$WEI?lSFUO4#T!?XZ>ffd9muVBY^q*<2B2vEc_=DN0|6CVI)H>#&z5Uf!@Zl?h0UEtt z6KG|~RVC;T$HH`}FtbIz0Ku7-5rSgyo=!*!&sS3z(J2JfSiz&-fRxMe6l@?BBs-AT zKxRZ%R+9rltMPz=PAjrXXEQD=jq05dMnW_~%w#;|wy;kkOl{BLqUSwWZk|xSsx$#x zNf;^PDr|xfkf>F(Oi3I~wZ&>&F;Il;r+s@Zw6yRH5_czcch+~0VgpQ^vc|lzx2`qj zHtlb2%#!LKvFJ~+<1fU`*EE6#2hmZvJQ)G!N#}x4mL($+E;pZZk5Oi(Px2Lr!f6RA z)DotVRbpH5-9moMF>`pIWKt+7$#S6sg4f`Z0nRfO>rD|Z z6XjaQ?KO@DBe0-$PI%KmN!gyK1qvk!x~g8N#hObGGAnj(Q&eBncLP@lqz9@?5F}I& zPlIWPTs4xeG}8PWb=O*Hx)Dx@?wsx#5@nH|cS@enj>~13IOM9jsoFo|a>)41zT8V4 z@Ik>`j(??bK?`|P96t}oW)`I~0&_!X=FQP7Q0Ai9ck0^i0FFa}b?&q`sel8v{K{CZl&GtL&7gTspj%^+9BAuwm@M|wgh zxt5%U_sTdi$|^K*js(5Kk&;+QIn9?d015L@-Ff{!AggJl!PM+iM;ah;)_^E>hRmH? zwo3A-!@F+aT4OcbY7Ncyu7hv6oJ`U&Q^FdV6`AX&DmGK{SZ=?M+uQlJ+@Xf)F*|#X z8jXY7nEqND{E8OaSHQi!iP-rb&_9QtVsw>7iPttu00=y04eA~**DBa<>o-u>rcPktFs^F zolHmnlmE0NP8bXF5hwzR5tg19F&EiL;X)A`xR-3(@QZ6D1cZ zZomlULlyPZ%g5j(LMG%ZWHAtl)}#P$E@qc}Ku{cPGHWug2*1Xq)3GkBr4hap%9Kbu;Wvmq!|Z+fKAV(7~dJHo*GD6=}Al0&`m_g zPfrg7e0@!SKwz#5Lu5s8p)^jexeo64tnXpord>C6NfHKN5T{L(<; zgRwZggOYjpVOLB18QhFo+USnZ1eBLNopz;Muf3eS5`capV{3nkEx#?!X0168sYB{1 zt-uHiOq0Xe!Wo@GK1*Ki2L?qJ@Kdq-p86VT%PX?rPiH65b+2M7Ju}d6E=828SP{%$ zA!)q)XNQU2;}Wd}yY7BhS|eJqpW7Ic-VBT)KbjuW>=ToQw(KW!a` z*tBP{w65w);@1Z6nd|WnOU(K3(BE~rKk~Ybi>-f9(p}{jyC$WpRF0jTiI7(SZc`g*`va3W6*m~u}WFq>Fl*=bM*~@ zcI>>fwJ~h*UYT@?G_ZB(tVxe?p7t5A>k((Fac03%I;dtew| zyIua;Oi_FM!4dxXWBY)HZDC&zj|KBj;pQ#P@5Ht3eihGcwe2f1?rlfD)R_Z&jOvo!JlPo$%+%O``|j;%nuIR)F%e} z>$JD^r*vo52>w`NFnC2xoLbc#Ji16z7u#-ZjMVG%%eqy{fS0k*`7`axN<6e;ta