diff --git a/demo-openid/src/Issuer.ts b/demo-openid/src/Issuer.ts index ee75e399df..414f10cef4 100644 --- a/demo-openid/src/Issuer.ts +++ b/demo-openid/src/Issuer.ts @@ -106,7 +106,7 @@ function getCredentialRequestToCredentialMapper({ method: 'did', didUrl: `${issuerDidKey.did}#${issuerDidKey.key.fingerprint}`, }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] }, } } diff --git a/packages/core/package.json b/packages/core/package.json index fb5b36f4db..0291b996b1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,8 +27,10 @@ "@digitalcredentials/jsonld-signatures": "^9.4.0", "@digitalcredentials/vc": "^6.0.1", "@multiformats/base-x": "^4.0.1", - "@sd-jwt/core": "^0.2.1", - "@sd-jwt/decode": "^0.2.1", + "@sd-jwt/core": "^0.6.1", + "@sd-jwt/decode": "^0.6.1", + "@sd-jwt/types": "^0.6.1", + "@sd-jwt/utils": "^0.6.1", "@sphereon/pex": "^3.3.2", "@sphereon/pex-models": "^2.2.4", "@sphereon/ssi-types": "^0.23.0", diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 98ecc99b4e..01abebf8c3 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -516,8 +516,8 @@ export class DifPresentationExchangeService { const sdJwtVcApi = this.getSdJwtVcApi(agentContext) const sdJwtVc = await sdJwtVcApi.present({ compactSdJwtVc: sdJwtInput.compactSdJwtVc, - // SD is already handled by PEX - presentationFrame: true, + // SD is already handled by PEX, so we presents all keys + presentationFrame: undefined, verifierMetadata: { audience: domain, nonce: challenge, diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index b631e3aadc..8606b01410 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -7,7 +7,7 @@ import type { import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@sphereon/pex' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' -import { decodeSdJwtVc } from '@sd-jwt/decode' +import { decodeSdJwt, decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' import { Rules } from '@sphereon/pex-models' import { default as jp } from 'jsonpath' @@ -59,12 +59,13 @@ export async function getCredentialsForRequest( if (credentialRecord instanceof SdJwtVcRecord) { // selectedEncoded always string when SdJwtVcRecord // Get the decoded payload from the the selected credential, this already has SD applied - const { decodedPayload } = decodeSdJwtVc(selectedEncoded as string, Hasher.hash) + const { jwt, disclosures } = decodeSdJwtSync(selectedEncoded as string, Hasher.hash) + const prettyClaims = getClaimsSync(jwt.payload, disclosures, Hasher.hash) return { type: ClaimFormat.SdJwtVc, credentialRecord, - disclosedPayload: decodedPayload, + disclosedPayload: prettyClaims as Record, } } else if (credentialRecord instanceof W3cCredentialRecord) { return { diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts index 17dbd81273..d9c905a1df 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcApi.ts @@ -37,10 +37,10 @@ export class SdJwtVcApi { * * Also, whether to include the holder key binding. */ - public async present
( + public async present( options: SdJwtVcPresentOptions ): Promise { - return await this.sdJwtVcService.present(this.agentContext, options) + return await this.sdJwtVcService.present(this.agentContext, options) } /** diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts index fcad595f33..9629759b46 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcOptions.ts @@ -1,11 +1,20 @@ import type { JwkJson, Jwk } from '../../crypto' import type { HashName } from '../../utils' -import type { DisclosureFrame, PresentationFrame } from '@sd-jwt/core' // TODO: extend with required claim names for input (e.g. vct) export type SdJwtVcPayload = Record export type SdJwtVcHeader = Record +export interface IDisclosureFrame { + _sd?: string[] + _sd_decoy?: number + [x: string]: string[] | number | IDisclosureFrame | undefined +} + +export interface IPresentationFrame { + [x: string]: boolean | IPresentationFrame +} + export interface SdJwtVcHolderDidBinding { method: 'did' didUrl: string @@ -33,7 +42,7 @@ export interface SdJwtVcSignOptions + disclosureFrame?: IDisclosureFrame /** * Default of sha-256 will be used if not provided @@ -41,13 +50,15 @@ export interface SdJwtVcSignOptions = { compactSdJwtVc: string /** * Use true to disclose everything */ - presentationFrame: PresentationFrame | true + presentationFrame?: IPresentationFrame /** * This information is received out-of-band from the verifier. diff --git a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts index ae4688b96d..1d8a9a1f7a 100644 --- a/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts +++ b/packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts @@ -10,21 +10,21 @@ import type { import type { AgentContext } from '../../agent' import type { JwkJson, Key } from '../../crypto' import type { Query } from '../../storage/StorageService' -import type { Signer, SdJwtVcVerificationResult, Verifier, HasherAndAlgorithm, DisclosureItem } from '@sd-jwt/core' +import type { SDJwt } from '@sd-jwt/core' +import type { Signer, Verifier, HasherSync, PresentationFrame, DisclosureFrame } from '@sd-jwt/types' -import { KeyBinding, SdJwtVc as _SdJwtVc, HasherAlgorithm } from '@sd-jwt/core' -import { decodeSdJwtVc } from '@sd-jwt/decode' +import { SDJwtInstance } from '@sd-jwt/core' +import { decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' +import { uint8ArrayToBase64Url } from '@sd-jwt/utils' import { injectable } from 'tsyringe' import { Jwk, getJwkFromJson, getJwkFromKey } from '../../crypto' -import { TypedArrayEncoder, Hasher, Buffer } from '../../utils' +import { TypedArrayEncoder, Hasher } from '../../utils' import { DidResolverService, parseDid, getKeyFromVerificationMethod } from '../dids' import { SdJwtVcError } from './SdJwtVcError' import { SdJwtVcRecord, SdJwtVcRepository } from './repository' -export { SdJwtVcVerificationResult, DisclosureItem } - export interface SdJwtVc< Header extends SdJwtVcHeader = SdJwtVcHeader, Payload extends SdJwtVcPayload = SdJwtVcPayload @@ -37,6 +37,22 @@ export interface SdJwtVc< prettyClaims: Payload } +export interface CnfPayload { + jwk?: JwkJson + kid?: string +} + +export interface VerificationResult { + isValid: boolean + isSignatureValid: boolean + isNotBeforeValid?: boolean + isExpiryTimeValid?: boolean + areRequiredClaimsIncluded?: boolean + isKeyBindingValid?: boolean + containsExpectedKeyBinding?: boolean + containsRequiredVcProperties?: boolean +} + /** * @internal */ @@ -65,71 +81,74 @@ export class SdJwtVcService { kid: issuer.kid, } as const - const sdJwtVc = new _SdJwtVc({}, { disclosureFrame }) - .withHasher(this.hasher) - .withSigner(this.signer(agentContext, issuer.key)) - .withSaltGenerator(agentContext.wallet.generateNonce) - .withHeader(header) - .withPayload({ ...payload }) - - // Add the `cnf` claim for the holder key binding - sdJwtVc.addPayloadClaim('cnf', holderBinding.cnf) - - // Add `iss` claim - sdJwtVc.addPayloadClaim('iss', issuer.iss) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + signer: this.signer(agentContext, issuer.key), + hashAlg: 'sha-256', + signAlg: issuer.alg, + saltGenerator: agentContext.wallet.generateNonce, + }) - // Add the issued at (iat) claim - sdJwtVc.addPayloadClaim('iat', Math.floor(new Date().getTime() / 1000)) + const compact = await sdjwt.issue( + { ...payload, cnf: holderBinding.cnf, iss: issuer.iss, iat: Math.floor(new Date().getTime() / 1000) }, + disclosureFrame as DisclosureFrame, + { header } + ) - const compact = await sdJwtVc.toCompact() - if (!sdJwtVc.signature) { - throw new SdJwtVcError('Invalid sd-jwt-vc state. Signature should have been set when calling `toCompact`.') + const prettyClaims = (await sdjwt.getClaims(compact)) as Payload + const a = await sdjwt.decode(compact) + const sdjwtPayload = a.jwt?.payload as Payload | undefined + if (!sdjwtPayload) { + throw new SdJwtVcError('Invalid sd-jwt-vc state.') } return { compact, - prettyClaims: await sdJwtVc.getPrettyClaims(), - header: sdJwtVc.header, - payload: sdJwtVc.payload, + prettyClaims, + header: header, + payload: sdjwtPayload, } satisfies SdJwtVc } public fromCompact
( compactSdJwtVc: string ): SdJwtVc { - // NOTE: we use decodeSdJwtVc so we can make this method sync - const { decodedPayload, header, signedPayload } = decodeSdJwtVc(compactSdJwtVc, Hasher.hash) + // NOTE: we use decodeSdJwtSync so we can make this method sync + const { jwt, disclosures } = decodeSdJwtSync(compactSdJwtVc, this.hasher) + const prettyClaims = getClaimsSync(jwt.payload, disclosures, this.hasher) return { compact: compactSdJwtVc, - header: header as Header, - payload: signedPayload as Payload, - prettyClaims: decodedPayload as Payload, + header: jwt.header as Header, + payload: jwt.payload as Payload, + prettyClaims: prettyClaims as Payload, } } - public async present
( + public async present( agentContext: AgentContext, { compactSdJwtVc, presentationFrame, verifierMetadata }: SdJwtVcPresentOptions ): Promise { - const sdJwtVc = _SdJwtVc.fromCompact
(compactSdJwtVc).withHasher(this.hasher) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + }) + const sdJwtVc = await sdjwt.decode(compactSdJwtVc) + const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) + sdjwt.config({ + kbSigner: this.signer(agentContext, holder.key), + kbSignAlg: holder.alg, + }) - const compactDerivedSdJwtVc = await sdJwtVc - .withKeyBinding( - new KeyBinding({ - header: { - alg: holder.alg, - typ: 'kb+jwt', - }, - payload: { - iat: verifierMetadata.issuedAt, - nonce: verifierMetadata.nonce, - aud: verifierMetadata.audience, - }, - }).withSigner(this.signer(agentContext, holder.key)) - ) - .present(presentationFrame === true ? undefined : presentationFrame) + const compactDerivedSdJwtVc = await sdjwt.present(compactSdJwtVc, presentationFrame as PresentationFrame, { + kb: { + payload: { + iat: verifierMetadata.issuedAt, + nonce: verifierMetadata.nonce, + aud: verifierMetadata.audience, + }, + }, + }) return compactDerivedSdJwtVc } @@ -138,29 +157,52 @@ export class SdJwtVcService { agentContext: AgentContext, { compactSdJwtVc, keyBinding, requiredClaimKeys }: SdJwtVcVerifyOptions ) { - const sdJwtVc = _SdJwtVc.fromCompact(compactSdJwtVc).withHasher(this.hasher) + const sdjwt = new SDJwtInstance({ + hasher: this.hasher, + }) + const sdJwtVc = await sdjwt.decode(compactSdJwtVc) + if (!sdJwtVc.jwt) { + throw new SdJwtVcError('Invalid sd-jwt-vc state.') + } const issuer = await this.extractKeyFromIssuer(agentContext, this.parseIssuerFromCredential(sdJwtVc)) const holder = await this.extractKeyFromHolderBinding(agentContext, this.parseHolderBindingFromCredential(sdJwtVc)) - const verificationResult = await sdJwtVc.verify( - this.verifier(agentContext), - requiredClaimKeys, - holder.cnf, - getJwkFromKey(holder.key).toJson(), - getJwkFromKey(issuer.key).toJson() - ) + sdjwt.config({ + verifier: this.verifier(agentContext, issuer.key), + kbVerifier: this.verifier(agentContext, holder.key), + }) + + const verificationResult: VerificationResult = { + isValid: false, + isSignatureValid: false, + } + + await sdjwt.verify(compactSdJwtVc, requiredClaimKeys, !!keyBinding) + + verificationResult.isValid = true + verificationResult.isSignatureValid = true + verificationResult.areRequiredClaimsIncluded = true // If keyBinding is present, verify the key binding try { if (keyBinding) { - if (!sdJwtVc.keyBinding || !sdJwtVc.keyBinding.payload) { + if (!sdJwtVc.kbJwt || !sdJwtVc.kbJwt.payload) { throw new SdJwtVcError('Keybinding is required for verification of the sd-jwt-vc') } // Assert `aud` and `nonce` claims - sdJwtVc.keyBinding.assertClaimInPayload('aud', keyBinding.audience) - sdJwtVc.keyBinding.assertClaimInPayload('nonce', keyBinding.nonce) + if (sdJwtVc.kbJwt.payload.aud !== keyBinding.audience) { + throw new SdJwtVcError('The key binding JWT does not contain the expected audience') + } + + if (sdJwtVc.kbJwt.payload.nonce !== keyBinding.nonce) { + throw new SdJwtVcError('The key binding JWT does not contain the expected nonce') + } + + verificationResult.isKeyBindingValid = true + verificationResult.containsExpectedKeyBinding = true + verificationResult.containsRequiredVcProperties = true } } catch (error) { verificationResult.isKeyBindingValid = false @@ -170,10 +212,10 @@ export class SdJwtVcService { return { verification: verificationResult, sdJwtVc: { - payload: sdJwtVc.payload, - header: sdJwtVc.header, + payload: sdJwtVc.jwt.payload as Payload, + header: sdJwtVc.jwt.header as Header, compact: compactSdJwtVc, - prettyClaims: await sdJwtVc.getPrettyClaims(), + prettyClaims: await sdJwtVc.getClaims(this.hasher), } satisfies SdJwtVc, } } @@ -217,32 +259,32 @@ export class SdJwtVcService { } } - private get hasher(): HasherAndAlgorithm { - return { - algorithm: HasherAlgorithm.Sha256, - hasher: Hasher.hash, - } + private get hasher(): HasherSync { + return Hasher.hash } /** * @todo validate the JWT header (alg) */ - private signer
(agentContext: AgentContext, key: Key): Signer
{ - return async (input: string) => agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + private signer(agentContext: AgentContext, key: Key): Signer { + return async (input: string) => { + const signedBuffer = await agentContext.wallet.sign({ key, data: TypedArrayEncoder.fromString(input) }) + return uint8ArrayToBase64Url(signedBuffer) + } } /** * @todo validate the JWT header (alg) */ - private verifier
(agentContext: AgentContext): Verifier
{ - return async ({ message, signature, publicKeyJwk }) => { - if (!publicKeyJwk) { + private verifier(agentContext: AgentContext, key: Key): Verifier { + return async (message: string, signatureBase64Url: string) => { + if (!key) { throw new SdJwtVcError('The public key used to verify the signature is missing') } return await agentContext.wallet.verify({ - signature: Buffer.from(signature), - key: getJwkFromJson(publicKeyJwk as JwkJson).key, + signature: TypedArrayEncoder.fromBase64(signatureBase64Url), + key, data: TypedArrayEncoder.fromString(message), }) } @@ -273,15 +315,31 @@ export class SdJwtVcService { } private parseIssuerFromCredential
( - sdJwtVc: _SdJwtVc + sdJwtVc: SDJwt ): SdJwtVcIssuer { - const iss = sdJwtVc.getClaimInPayload('iss') + if (!sdJwtVc.jwt?.payload) { + throw new SdJwtVcError('Credential not exist') + } + + if (!sdJwtVc.jwt?.payload['iss']) { + throw new SdJwtVcError('Credential does not contain an issuer') + } + + const iss = sdJwtVc.jwt.payload['iss'] as string if (iss.startsWith('did:')) { // If `did` is used, we require a relative KID to be present to identify // the key used by issuer to sign the sd-jwt-vc - sdJwtVc.assertClaimInHeader('kid') - const issuerKid = sdJwtVc.getClaimInHeader('kid') + + if (!sdJwtVc.jwt?.header) { + throw new SdJwtVcError('Credential does not contain a header') + } + + if (!sdJwtVc.jwt.header['kid']) { + throw new SdJwtVcError('Credential does not contain a kid in the header') + } + + const issuerKid = sdJwtVc.jwt.header['kid'] as string let didUrl: string if (issuerKid.startsWith('#')) { @@ -310,9 +368,16 @@ export class SdJwtVcService { } private parseHolderBindingFromCredential
( - sdJwtVc: _SdJwtVc + sdJwtVc: SDJwt ): SdJwtVcHolderBinding { - const cnf = sdJwtVc.getClaimInPayload<{ jwk?: JwkJson; kid?: string }>('cnf') + if (!sdJwtVc.jwt?.payload) { + throw new SdJwtVcError('Credential not exist') + } + + if (!sdJwtVc.jwt?.payload['cnf']) { + throw new SdJwtVcError('Credential does not contain a holder binding') + } + const cnf: CnfPayload = sdJwtVc.jwt.payload['cnf'] if (cnf.jwk) { return { diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts index d0768ed58a..a46ca47f60 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/SdJwtVcService.test.ts @@ -124,10 +124,51 @@ describe('SdJwtVcService', () => { }) }) + test('Sign sd-jwt-vc from a basic payload including false boolean values', async () => { + const { compact } = await sdJwtVcService.sign(agent.context, { + payload: { + claim: 'some-claim', + vct: 'IdentityCredential', + value: false, + discloseableValue: false, + }, + holder: { + // FIXME: is it nicer API to just pass either didUrl or JWK? + // Or none if you don't want to bind it? + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) + + const sdJwtVc = await sdJwtVcService.fromCompact(compact) + + expect(sdJwtVc.header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(sdJwtVc.prettyClaims).toEqual({ + claim: 'some-claim', + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + iss: parseDid(issuerDidUrl).did, + value: false, + discloseableValue: false, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + }) + test('Create sd-jwt-vc from a basic payload with a disclosure', async () => { const { compact, header, prettyClaims, payload } = await sdJwtVcService.sign(agent.context, { payload: { claim: 'some-claim', vct: 'IdentityCredential' }, - disclosureFrame: { claim: true }, + disclosureFrame: { _sd: ['claim'] }, holder: { method: 'jwk', jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), @@ -171,13 +212,10 @@ describe('SdJwtVcService', () => { test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { const { compact, header, payload, prettyClaims } = await sdJwtVcService.sign(agent.context, { disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { region: true, country: true }, - given_name: true, + _sd: ['is_over_65', 'is_over_21', 'is_over_18', 'birthdate', 'email', 'given_name'], + address: { + _sd: ['region', 'country'], + }, }, payload: { vct: 'IdentityCredential', @@ -262,6 +300,92 @@ describe('SdJwtVcService', () => { }, }) }) + + test('Create sd-jwt-vc from a basic payload with multiple (nested) disclosure where a disclosure contains other disclosures', async () => { + const { header, payload, prettyClaims } = await sdJwtVcService.sign(agent.context, { + disclosureFrame: { + _sd: ['is_over_65', 'is_over_21', 'is_over_18', 'birthdate', 'email', 'given_name', 'address'], + address: { + _sd: ['region', 'country'], + }, + }, + payload: { + vct: 'IdentityCredential', + given_name: 'John', + family_name: 'Doe', + email: 'johndoe@example.com', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US', + }, + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + }, + holder: { + method: 'jwk', + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + issuer: { + method: 'did', + didUrl: issuerDidUrl, + }, + }) + + expect(header).toEqual({ + alg: 'EdDSA', + typ: 'vc+sd-jwt', + kid: '#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + }) + + expect(payload).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + _sd: [ + '1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas', + 'R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU', + 'eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw', + 'pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc', + 'psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk', + 'sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI', + 'yPhxDEM7k7p7eQ9eHHC-Ca6VEA8bzebZpYu7vYmwG6c', + ], + _sd_alg: 'sha-256', + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + + expect(prettyClaims).toEqual({ + vct: 'IdentityCredential', + iat: Math.floor(new Date().getTime() / 1000), + address: { + region: 'Anystate', + country: 'US', + locality: 'Anytown', + street_address: '123 Main St', + }, + email: 'johndoe@example.com', + given_name: 'John', + phone_number: '+1-202-555-0101', + family_name: 'Doe', + iss: issuerDidUrl.split('#')[0], + birthdate: '1940-01-01', + is_over_18: true, + is_over_21: true, + is_over_65: true, + cnf: { + jwk: jwkJsonWithoutUse(getJwkFromKey(holderKey)), + }, + }) + }) }) describe('SdJwtVcService.receive', () => { @@ -404,9 +528,7 @@ describe('SdJwtVcService', () => { test('Present sd-jwt-vc from a basic payload with a disclosure', async () => { const presentation = await sdJwtVcService.present(agent.context, { compactSdJwtVc: sdJwtVcWithSingleDisclosure, - presentationFrame: { - claim: true, - }, + presentationFrame: { claim: true }, verifierMetadata: { issuedAt: new Date().getTime() / 1000, audience: verifierDid, @@ -418,16 +540,13 @@ describe('SdJwtVcService', () => { }) test('Present sd-jwt-vc from a basic payload with multiple (nested) disclosure', async () => { - const presentation = await sdJwtVcService.present< - Record, - { - // FIXME: when not passing a payload, adding nested presentationFrame is broken - // Needs to be fixed in sd-jwt library - address: { - country: string - } - } - >(agent.context, { + const presentation = await sdJwtVcService.present<{ + is_over_65: boolean + is_over_21: boolean + email: boolean + address: { country: string } + given_name: boolean + }>(agent.context, { compactSdJwtVc: complexSdJwtVc, verifierMetadata: { issuedAt: new Date().getTime() / 1000, @@ -489,9 +608,7 @@ describe('SdJwtVcService', () => { audience: verifierDid, nonce, }, - presentationFrame: { - claim: true, - }, + presentationFrame: { claim: true }, }) const { verification } = await sdJwtVcService.verify(agent.context, { @@ -513,26 +630,29 @@ describe('SdJwtVcService', () => { test('Verify sd-jwt-vc with multiple (nested) disclosure', async () => { const nonce = await agent.context.wallet.generateNonce() - const presentation = await sdJwtVcService.present( - agent.context, - { - compactSdJwtVc: complexSdJwtVc, - verifierMetadata: { - issuedAt: new Date().getTime() / 1000, - audience: verifierDid, - nonce, - }, - presentationFrame: { - is_over_65: true, - is_over_21: true, - email: true, - address: { - country: true, - }, - given_name: true, + const presentation = await sdJwtVcService.present<{ + is_over_65: boolean + is_over_21: boolean + email: boolean + address: { country: string } + given_name: boolean + }>(agent.context, { + compactSdJwtVc: complexSdJwtVc, + verifierMetadata: { + issuedAt: new Date().getTime() / 1000, + audience: verifierDid, + nonce, + }, + presentationFrame: { + is_over_65: true, + is_over_21: true, + email: true, + address: { + country: true, }, - } - ) + given_name: true, + }, + }) const { verification } = await sdJwtVcService.verify(agent.context, { compactSdJwtVc: presentation, @@ -551,9 +671,9 @@ describe('SdJwtVcService', () => { 'is_over_21', 'email', 'given_name', - 'street_address', - 'locality', - 'country', + 'address.street_address', + 'address.locality', + 'address.country', ], }) @@ -572,10 +692,10 @@ describe('SdJwtVcService', () => { agent.context, { compactSdJwtVc: - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rcnpRUEJyNHB5cUM3NzZLS3RyejEzU2NoTTVlUFBic3N1UHVRWmI1dDR1S1EifQ.eyJ2Y3QiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZGVncmVlIjoiYmFjaGVsb3IiLCJjbmYiOnsia2lkIjoiZGlkOmtleTp6Nk1rcEdSNGdzNFJjM1pwaDR2ajh3Um5qbkF4Z0FQU3hjUjhNQVZLdXRXc3BRemMjejZNa3BHUjRnczRSYzNacGg0dmo4d1Juam5BeGdBUFN4Y1I4TUFWS3V0V3NwUXpjIn0sImlzcyI6ImRpZDprZXk6ejZNa3J6UVBCcjRweXFDNzc2S0t0cnoxM1NjaE01ZVBQYnNzdVB1UVpiNXQ0dUtRIiwiaWF0IjoxNzA2MjY0ODQwLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyJTSm81ME0xX3NUQWRPSjFObF82ekJzZWg3Ynd4czhhbDhleVotQl9nTXZFIiwiYTJjT2xWOXY4TUlWNTRvMVFrODdBMDRNZ0c3Q0hiaFZUN1lkb00zUnM4RSJdfQ.PrZtmLFPr8tBY0FKsv2yHJeqzds8m0Rlrof-Z36o7ksNvON3ZSrKHOD8fFDJaQ8oFJcZAnjpUS6pY9kwAgU1Ag~WyI5Mjg3NDM3NDQyMTg0ODk1NTU3OTA1NTkiLCJ1bml2ZXJzaXR5IiwiaW5uc2JydWNrIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE3MDYyNjQ4NDAsIm5vbmNlIjoiODExNzMxNDIwNTMxODQ3NzAwNjM2ODUiLCJhdWQiOiJkaWQ6a2V5Ono2TWt0aVFRRXFtMnlhcFhCRHQxV0VWQjNkcWd2eXppOTZGdUZBTlltcmdUcktWOSIsIl9zZF9oYXNoIjoiSVd0cTEtOGUtLU9wWWpXa3Z1RTZrRjlaa2h5aDVfV3lOYXItaWtVT0FscyJ9.cJNnYH16lHn0PsF9tOQPofpONGoY19bQB5k6Ezux9TvQWS_Opnd-3m_fO9yKu8S0pmjyG2mu3Uzn1pUNqhL9AQ', + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJPcGVuQmFkZ2VDcmVkZW50aWFsIiwiZGVncmVlIjoiYmFjaGVsb3IiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJLbE5PM0VfYjRmdUwyOUd2QXdwTGczTGZHZTlxdDdhakUxMzlfU1pIbWk4Il0sIl9zZF9hbGciOiJzaGEtMjU2In0.TBWIECIMmNKNqVtjwHARSnR0Ii9Fefy871sXEK-zfThbTBALdvXBTBQ6iKvvI-CxsniSH1hJMEJTu1vK7esTDg~WyJzYWx0IiwidW5pdmVyc2l0eSIsImlubnNicnVjayJd~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiODlyX3JrSjdvb3RuSGJ3TXdjMW9sNzZncU03WU1zNVUzVnpkMHN6N3VkbyJ9.VkrxL06aP8t-G_lVtlAZNgJC2gouqR__rXDgJQPParq5OGxna3ZoQQbjv7e3I2TUaVaMV6xUpJY1KufZlPDwAg', keyBinding: { - audience: 'did:key:z6MktiQQEqm2yapXBDt1WEVB3dqgvyzi96FuFANYmrgTrKV9', - nonce: '81173142053184770063685', + audience: 'did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y', + nonce: 'salt', }, } ) diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts index c8c721aa7a..1df450b847 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdJwtVc.test.ts @@ -66,16 +66,21 @@ describe('sd-jwt-vc end to end test', () => { method: 'did', }, disclosureFrame: { - is_over_65: true, - is_over_21: true, - is_over_18: true, - birthdate: true, - email: true, - address: { country: true, region: true, locality: true, __decoyCount: 2, street_address: true }, - __decoyCount: 2, - given_name: true, - family_name: true, - phone_number: true, + _sd: [ + 'is_over_65', + 'is_over_21', + 'is_over_18', + 'birthdate', + 'email', + 'given_name', + 'family_name', + 'phone_number', + ], + _sd_decoy: 2, + address: { + _sd: ['country', 'region', 'locality', 'street_address'], + _sd_decoy: 2, + }, }, }) @@ -170,11 +175,10 @@ describe('sd-jwt-vc end to end test', () => { nonce: await verifier.wallet.generateNonce(), } - const presentation = await holder.sdJwtVc.present({ + const presentation = await holder.sdJwtVc.present({ compactSdJwtVc: compact, verifierMetadata, presentationFrame: { - vct: true, given_name: true, family_name: true, email: true, @@ -201,10 +205,11 @@ describe('sd-jwt-vc end to end test', () => { 'is_over_18', 'birthdate', 'email', - 'country', - 'region', - 'locality', - 'street_address', + 'address.country', + 'address.region', + 'address.locality', + 'address', + 'address.street_address', 'given_name', 'family_name', 'phone_number', diff --git a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts index 1cbb1b112c..e633c3bb3f 100644 --- a/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts +++ b/packages/core/src/modules/sd-jwt-vc/__tests__/sdjwtvc.fixtures.ts @@ -1,17 +1,441 @@ +/**simpleJwtVc + { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532 + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [] + } + */ export const simpleJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~' +/**simpleJwtVcPresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "claim": "some-claim", + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532 + }, + "signature": "vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg" + }, + "disclosures": [], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "f48YBevUG5JVuAHMryWQ4i2OF7XJoI-dL-jjYx-HqxQ" + }, + "signature": "skMqC7ej50kOeGEJZ_8J5eK1YqKN7vkqS_t8DQ4Y3i6DdN20eAXbaGMU4G4AOGk_hAYctTZwxaeQQEBX8pu5Cg" + } + } + */ export const simpleJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.hcNF6_PnQO4Gm0vqD_iblyBknUG0PeQLbIpPJ5s0P4UCQ7YdSSNCNL7VNOfzzAxZRWbH5knhje0_xYl6OXQ-CA~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IkN4SnFuQ1Btd0d6bjg4YTlDdGhta2pHZXFXbnlKVTVKc2NLMXJ1VThOS28ifQ.0QaDyJrvZO91o7gdKPduKQIj5Z1gBAdWPNE8-PFqhj_rC56_I5aL8QtlwL8Mdl6iSjpUPDQ4LAN2JgB2nNOFBw' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJjbGFpbSI6InNvbWUtY2xhaW0iLCJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzJ9.vLkigrBr1IIVRJeYE5DQx0rKUVzO3KT9T0XBATWJE89pWCyvB3Rzs8VD7qfi0vDk_QVCPIiHq1U1PsmSe4ZqCg~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiZjQ4WUJldlVHNUpWdUFITXJ5V1E0aTJPRjdYSm9JLWRMLWpqWXgtSHF4USJ9.skMqC7ej50kOeGEJZ_8J5eK1YqKN7vkqS_t8DQ4Y3i6DdN20eAXbaGMU4G4AOGk_hAYctTZwxaeQQEBX8pu5Cg' +/**sdJwtVcWithSingleDisclosure + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg" + ], + "_sd_alg": "sha-256" + }, + "signature": "wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ" + }, + "disclosures": [ + { + "_digest": "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg", + "_encoded": "WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0", + "salt": "salt", + "key": "claim", + "value": "some-claim" + } + ] +} + * + * claim: +{ + vct: 'IdentityCredential', + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + claim: 'some-claim' +} + */ export const sdJwtVcWithSingleDisclosure = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~' +/**sdJwtVcWithSingleDisclosurePresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg" + ], + "_sd_alg": "sha-256" + }, + "signature": "wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ" + }, + "disclosures": [ + { + "_digest": "vcvFU4DsFKTqQ1vl4nelJWXTb_-0dNoBks6iqNFptyg", + "_encoded": "WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0", + "salt": "salt", + "key": "claim", + "value": "some-claim" + } + ], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "9F5VQwSVO7ZAwIgyh1jrwnJWgy7fTId1mj1MRp41nM8" + }, + "signature": "9TcpFkSLYMbsQzkPMyqrT5kMk8sobEvTzfkwym5HvbTfEMa_J23LB-UFhY0FsBhe-1rYqnAykGuimQNaWIwODw" + } +} + + * claims +{ + vct: 'IdentityCredential', + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + claim: 'some-claim' +} + */ export const sdJwtVcWithSingleDisclosurePresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbInZjdkZVNERzRktUcVExdmw0bmVsSldYVGJfLTBkTm9Ca3M2aXFORnB0eWciXX0.Op3rwd7t6ZsdMSMa1EchAm31bP5aqLF6pB-Z1y-h3CFJYGmhNTkMpTeft1I3hSWq7QmbqBo1GKBEiZc7D9B9DA~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6IlBNbEo3bjhjdVdvUU9YTFZ4cTRhaWRaNHJTY2FrVUtMT1hUaUtWYjYtYTQifQ.5iYVLw6U7NIdW7Eoo2jYYBsR3fSJZ-ocOtI6rxl-GYUj8ZeCx_-IZ2rbwCMf71tq6M16x4ROooKGAdfWUSWQAg' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJjbmYiOnsiandrIjp7Imt0eSI6Ik9LUCIsImNydiI6IkVkMjU1MTkiLCJ4Ijoib0VOVnN4T1VpSDU0WDh3SkxhVmtpY0NSazAwd0JJUTRzUmdiazU0TjhNbyJ9fSwiaXNzIjoiZGlkOmtleTp6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlciLCJpYXQiOjE2OTgxNTE1MzIsIl9zZCI6WyJ2Y3ZGVTREc0ZLVHFRMXZsNG5lbEpXWFRiXy0wZE5vQmtzNmlxTkZwdHlnIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.wX-7AyTsGMFDpgaw-TMjFK2zyywB94lKAwXlc4DtNoYjhnvKEe6eln1YhKTD_IIPNyTDOCT-TgtzA-8tCg9NCQ~WyJzYWx0IiwiY2xhaW0iLCJzb21lLWNsYWltIl0~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiOUY1VlF3U1ZPN1pBd0lneWgxanJ3bkpXZ3k3ZlRJZDFtajFNUnA0MW5NOCJ9.9TcpFkSLYMbsQzkPMyqrT5kMk8sobEvTzfkwym5HvbTfEMa_J23LB-UFhY0FsBhe-1rYqnAykGuimQNaWIwODw' + +/**complexSdJwtVc + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "family_name": "Doe", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "_sd": [ + "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4" + ] + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI" + ], + "_sd_alg": "sha-256" + }, + "signature": "Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg" + }, + "disclosures": [ + { + "_digest": "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "_encoded": "WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ", + "salt": "salt", + "key": "region", + "value": "Anystate" + }, + { + "_digest": "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4", + "_encoded": "WyJzYWx0IiwiY291bnRyeSIsIlVTIl0", + "salt": "salt", + "key": "country", + "value": "US" + }, + { + "_digest": "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "_encoded": "WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ", + "salt": "salt", + "key": "given_name", + "value": "John" + }, + { + "_digest": "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "_encoded": "WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0", + "salt": "salt", + "key": "email", + "value": "johndoe@example.com" + }, + { + "_digest": "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "_encoded": "WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd", + "salt": "salt", + "key": "birthdate", + "value": "1940-01-01" + }, + { + "_digest": "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_18", + "value": true + }, + { + "_digest": "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_21", + "value": true + }, + { + "_digest": "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_65", + "value": true + } + ] +} + * claims +{ + vct: 'IdentityCredential', + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US' + }, + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + is_over_18: true, + is_over_21: true, + given_name: 'John', + birthdate: '1940-01-01', + email: 'johndoe@example.com', + is_over_65: true +} + */ export const complexSdJwtVc = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg~WyJzYWx0IiwicmVnaW9uIiwiQW55c3RhdGUiXQ~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd~WyJzYWx0IiwiaXNfb3Zlcl8xOCIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~' +/**complexSdJwtVcPresentation + * { + "jwt": { + "header": { + "typ": "vc+sd-jwt", + "alg": "EdDSA", + "kid": "#z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW" + }, + "payload": { + "vct": "IdentityCredential", + "family_name": "Doe", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "_sd": [ + "NJnmct0BqBME1JfBlC6jRQVRuevpEONiYw7A7MHuJyQ", + "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4" + ] + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo" + } + }, + "iss": "did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW", + "iat": 1698151532, + "_sd": [ + "1Cur2k2A2oIB5CshSIf_A_Kg-l26u_qKuWQ79P0Vdas", + "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "pdDk2_XAKHo7gOAfwF1b7OdCUVTit2kJHaxSECQ9xfc", + "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI" + ], + "_sd_alg": "sha-256" + }, + "signature": "Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg" + }, + "disclosures": [ + { + "_digest": "om5ZztZHB-Gd00LG21CV_xM4FaENSoiaOXnTAJNczB4", + "_encoded": "WyJzYWx0IiwiY291bnRyeSIsIlVTIl0", + "salt": "salt", + "key": "country", + "value": "US" + }, + { + "_digest": "psauKUNWEi09nu3Cl89xKXgmpWENZl5uy1N1nyn_jMk", + "_encoded": "WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0", + "salt": "salt", + "key": "email", + "value": "johndoe@example.com" + }, + { + "_digest": "eDqQpdTXJXbWhf-EsI7zw5X6OvYmFN-UZQQMesXwKPw", + "_encoded": "WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ", + "salt": "salt", + "key": "given_name", + "value": "John" + }, + { + "_digest": "R1zTUvOYHgcepj0jHypGHz9EHttVKft0yswbc9ETPbU", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_21", + "value": true + }, + { + "_digest": "sN_ge0pHXF6qmsYnX1A9SdwJ8ch8aENkxbODsT74YwI", + "_encoded": "WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0", + "salt": "salt", + "key": "is_over_65", + "value": true + } + ], + "kbJwt": { + "header": { + "typ": "kb+jwt", + "alg": "EdDSA" + }, + "payload": { + "iat": 1698151532, + "nonce": "salt", + "aud": "did:key:zUC74VEqqhEHQcgv4zagSPkqFJxuNWuoBPKjJuHETEUeHLoSqWt92viSsmaWjy82y", + "sd_hash": "8qgm3cypUxDaa_grER613U9UNETnbLragU6UVwJ4HlM" + }, + "signature": "62HzMUsjlMq3BWyEBZwCuQnR5LzouSZKWh6es5CtC9HphOrh0ps1Lj_2iiZHfMv_lVF5Np_ZOiZNqsHfPL3GAA" + } +} + * claims +{ + vct: 'IdentityCredential', + family_name: 'Doe', + phone_number: '+1-202-555-0101', + address: { street_address: '123 Main St', locality: 'Anytown', country: 'US' }, + cnf: { + jwk: { + kty: 'OKP', + crv: 'Ed25519', + x: 'oENVsxOUiH54X8wJLaVkicCRk00wBIQ4sRgbk54N8Mo' + } + }, + iss: 'did:key:z6MktqtXNG8CDUY9PrrtoStFzeCnhpMmgxYL1gikcW3BzvNW', + iat: 1698151532, + is_over_21: true, + given_name: 'John', + email: 'johndoe@example.com', + is_over_65: true +} + */ export const complexSdJwtVcPresentation = - 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyIxQ3VyMmsyQTJvSUI1Q3NoU0lmX0FfS2ctbDI2dV9xS3VXUTc5UDBWZGFzIiwiUjF6VFV2T1lIZ2NlcGowakh5cEdIejlFSHR0VktmdDB5c3diYzlFVFBiVSIsImVEcVFwZFRYSlhiV2hmLUVzSTd6dzVYNk92WW1GTi1VWlFRTWVzWHdLUHciLCJwZERrMl9YQUtIbzdnT0Fmd0YxYjdPZENVVlRpdDJrSkhheFNFQ1E5eGZjIiwicHNhdUtVTldFaTA5bnUzQ2w4OXhLWGdtcFdFTlpsNXV5MU4xbnluX2pNayIsInNOX2dlMHBIWEY2cW1zWW5YMUE5U2R3SjhjaDhhRU5reGJPRHNUNzRZd0kiXX0.coOK8NzJmEWz4qx-qRhjo-RK7aejrSkQM9La9Cw3eWmzcja9DXrkBoQZKbIJtNoSzSPLjwK2V71W78z0miZsDQ~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJhbGciOiJFZERTQSIsInR5cCI6ImtiK2p3dCJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJfc2RfaGFzaCI6Ii1kTUd4OGZhUnpOQm91a2EwU0R6V2JkS3JYckw1TFVmUlNQTHN2Q2xPMFkifQ.TQQLqc4ZzoKjQfAghAzC_4aaU3KCS8YqzxAJtzT124guzkv9XSHtPN8d3z181_v-ca2ATXjTRoRciozitE6wBA' + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSIsImtpZCI6IiN6Nk1rdHF0WE5HOENEVVk5UHJydG9TdEZ6ZUNuaHBNbWd4WUwxZ2lrY1czQnp2TlcifQ.eyJ2Y3QiOiJJZGVudGl0eUNyZWRlbnRpYWwiLCJmYW1pbHlfbmFtZSI6IkRvZSIsInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsImFkZHJlc3MiOnsic3RyZWV0X2FkZHJlc3MiOiIxMjMgTWFpbiBTdCIsImxvY2FsaXR5IjoiQW55dG93biIsIl9zZCI6WyJOSm5tY3QwQnFCTUUxSmZCbEM2alJRVlJ1ZXZwRU9OaVl3N0E3TUh1SnlRIiwib201Wnp0WkhCLUdkMDBMRzIxQ1ZfeE00RmFFTlNvaWFPWG5UQUpOY3pCNCJdfSwiY25mIjp7Imp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im9FTlZzeE9VaUg1NFg4d0pMYVZraWNDUmswMHdCSVE0c1JnYms1NE44TW8ifX0sImlzcyI6ImRpZDprZXk6ejZNa3RxdFhORzhDRFVZOVBycnRvU3RGemVDbmhwTW1neFlMMWdpa2NXM0J6dk5XIiwiaWF0IjoxNjk4MTUxNTMyLCJfc2QiOlsiMUN1cjJrMkEyb0lCNUNzaFNJZl9BX0tnLWwyNnVfcUt1V1E3OVAwVmRhcyIsIlIxelRVdk9ZSGdjZXBqMGpIeXBHSHo5RUh0dFZLZnQweXN3YmM5RVRQYlUiLCJlRHFRcGRUWEpYYldoZi1Fc0k3enc1WDZPdlltRk4tVVpRUU1lc1h3S1B3IiwicGREazJfWEFLSG83Z09BZndGMWI3T2RDVVZUaXQya0pIYXhTRUNROXhmYyIsInBzYXVLVU5XRWkwOW51M0NsODl4S1hnbXBXRU5abDV1eTFOMW55bl9qTWsiLCJzTl9nZTBwSFhGNnFtc1luWDFBOVNkd0o4Y2g4YUVOa3hiT0RzVDc0WXdJIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.Kkhrxy2acd52JTl4g_0x25D5d1QNCTbqHrD9Qu9HzXMxPMu_5T4z-cSiutDYb5cIdi9NzMXPe4MXax-fUymEDg~WyJzYWx0IiwiaXNfb3Zlcl82NSIsdHJ1ZV0~WyJzYWx0IiwiaXNfb3Zlcl8yMSIsdHJ1ZV0~WyJzYWx0IiwiZW1haWwiLCJqb2huZG9lQGV4YW1wbGUuY29tIl0~WyJzYWx0IiwiY291bnRyeSIsIlVTIl0~WyJzYWx0IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE2OTgxNTE1MzIsIm5vbmNlIjoic2FsdCIsImF1ZCI6ImRpZDprZXk6elVDNzRWRXFxaEVIUWNndjR6YWdTUGtxRkp4dU5XdW9CUEtqSnVIRVRFVWVITG9TcVd0OTJ2aVNzbWFXank4MnkiLCJzZF9oYXNoIjoiaFRtUklwNFQ1Y2ZqQlUxbTVvcXNNWDZuUlFObGpEdXZSSThTWnlTeWhsZyJ9.D0G1__PslfgjkwTC1082x3r8Wp5mf13977y7Ef2xhvDrOO7V3zio5BZzqrDwzXIi3Y5GA1Vv3ptqpUKMn14EBA' diff --git a/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts index e2a77a73bb..2892ae124f 100644 --- a/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts +++ b/packages/core/src/modules/sd-jwt-vc/repository/SdJwtVcRecord.ts @@ -2,10 +2,10 @@ import type { JwaSignatureAlgorithm } from '../../../crypto' import type { TagsBase } from '../../../storage/BaseRecord' import type { Constructable } from '../../../utils/mixins' -import { SdJwtVc } from '@sd-jwt/core' +import { decodeSdJwtSync } from '@sd-jwt/decode' import { BaseRecord } from '../../../storage/BaseRecord' -import { JsonTransformer } from '../../../utils' +import { Hasher, JsonTransformer } from '../../../utils' import { uuid } from '../../../utils/uuid' export type DefaultSdJwtVcRecordTags = { @@ -50,13 +50,16 @@ export class SdJwtVcRecord extends BaseRecord { } public getTags() { - const sdJwtVc = SdJwtVc.fromCompact(this.compactSdJwtVc) + const sdjwt = decodeSdJwtSync(this.compactSdJwtVc, Hasher.hash) + const vct = sdjwt.jwt.payload['vct'] as string + const sdAlg = sdjwt.jwt.payload['_sd_alg'] as string | undefined + const alg = sdjwt.jwt.header['alg'] as JwaSignatureAlgorithm return { ...this._tags, - vct: sdJwtVc.getClaimInPayload('vct'), - sdAlg: (sdJwtVc.payload._sd_alg as string | undefined) ?? 'sha-256', - alg: sdJwtVc.getClaimInHeader('alg'), + vct, + sdAlg: sdAlg ?? 'sha-256', + alg, } } diff --git a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts index 778c96b4ac..6c44458591 100644 --- a/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts +++ b/packages/core/src/modules/vc/models/credential/W3cCredentialSubject.ts @@ -4,7 +4,6 @@ import { Transform, TransformationType } from 'class-transformer' import { IsOptional, ValidateBy, buildMessage, isInstance } from 'class-validator' import { CredoError } from '../../../../error' -import { IsUri, isUri } from '../../../../utils/validators' /** * @see https://www.w3.org/TR/vc-data-model/#credential-subject diff --git a/packages/drpc/src/messages/DrpcRequestMessage.ts b/packages/drpc/src/messages/DrpcRequestMessage.ts index 254244e1f5..6e8ae1ea6a 100644 --- a/packages/drpc/src/messages/DrpcRequestMessage.ts +++ b/packages/drpc/src/messages/DrpcRequestMessage.ts @@ -6,6 +6,7 @@ import { IsValidDrpcRequest } from '../models' export interface DrpcRequestObject { jsonrpc: string method: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any params?: any[] | object id: string | number | null } diff --git a/packages/drpc/src/messages/DrpcResponseMessage.ts b/packages/drpc/src/messages/DrpcResponseMessage.ts index ee548e6784..a148760bfd 100644 --- a/packages/drpc/src/messages/DrpcResponseMessage.ts +++ b/packages/drpc/src/messages/DrpcResponseMessage.ts @@ -10,11 +10,13 @@ export type DrpcResponse = DrpcResponseObject | (DrpcResponseObject | Record { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate: (value: any): boolean => { // Check if value is a DrpcRequestObject or an array of DrpcRequestObject let isValid = false if (!Array.isArray(value)) { diff --git a/packages/drpc/src/models/ValidResponse.ts b/packages/drpc/src/models/ValidResponse.ts index 26a6b56b7b..4ca51890d8 100644 --- a/packages/drpc/src/models/ValidResponse.ts +++ b/packages/drpc/src/models/ValidResponse.ts @@ -1,14 +1,16 @@ -import type { ValidationArguments, ValidationOptions } from 'class-validator' +import type { ValidationOptions } from 'class-validator' import { ValidateBy, ValidationError, buildMessage } from 'class-validator' export function IsValidDrpcResponse(validationOptions?: ValidationOptions): PropertyDecorator { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return function (target: any, propertyKey: string | symbol) { ValidateBy( { name: 'isValidDrpcResponse', validator: { - validate: (value: any, _: ValidationArguments): boolean => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validate: (value: any): boolean => { // Check if value is a valid DrpcResponseObject, an array of DrpcResponseObject (possibly mixed with empty objects), or an empty object let isValid = false if (Array.isArray(value)) { diff --git a/packages/drpc/tests/drpc-messages.e2e.test.ts b/packages/drpc/tests/drpc-messages.e2e.test.ts index ddbbd1973a..002d8d98d8 100644 --- a/packages/drpc/tests/drpc-messages.e2e.test.ts +++ b/packages/drpc/tests/drpc-messages.e2e.test.ts @@ -209,7 +209,7 @@ describe('Drpc Messages E2E', () => { test('Alice sends Faber Drpc notification', async () => { testLogger.test('Alice sends notification to Faber') let notified = false - messageHandlers.set('notify', async (_) => { + messageHandlers.set('notify', async () => { notified = true return {} }) @@ -254,7 +254,7 @@ describe('Drpc Messages E2E', () => { }) test('Alice sends Faber invalid Drpc message | Faber responds with invalid Drpc message', async () => { - messageHandlers.set('hello', async (_) => { + messageHandlers.set('hello', async () => { return [] as unknown as DrpcResponseObject }) let error = false diff --git a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts index 961af8e282..cb5f52cd5c 100644 --- a/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts +++ b/packages/openid4vc/src/openid4vc-issuer/__tests__/openid4vc-issuer.test.ts @@ -340,7 +340,7 @@ describe('OpenId4VcIssuer', () => { payload: { vct: 'UniversityDegreeCredential', university: 'innsbruck', degree: 'bachelor' }, issuer: { method: 'did', didUrl: issuerVerificationMethod.id }, holder: { method: 'did', didUrl: holderVerificationMethod.id }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] }, credentialSupportedId: universityDegreeCredentialSdJwt.id, }), }) diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index 8013e8b917..42675fcdc1 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -14,7 +14,7 @@ import type { RecordSavedEvent, RecordUpdatedEvent, } from '@credo-ts/core' -import type { PresentationVerificationCallback, SigningAlgo } from '@sphereon/did-auth-siop' +import type { PresentationVerificationCallback } from '@sphereon/did-auth-siop' import { EventEmitter, diff --git a/packages/openid4vc/tests/openid4vc.e2e.test.ts b/packages/openid4vc/tests/openid4vc.e2e.test.ts index 9f40b9c69d..a062f45d6b 100644 --- a/packages/openid4vc/tests/openid4vc.e2e.test.ts +++ b/packages/openid4vc/tests/openid4vc.e2e.test.ts @@ -1,6 +1,7 @@ import type { AgentType, TenantType } from './utils' import type { OpenId4VciCredentialBindingResolver } from '../src/openid4vc-holder' -import type { DifPresentationExchangeDefinitionV2, SdJwtVc } from '@credo-ts/core' +import type { DifPresentationExchangeDefinitionV2, SdJwtVc, SdJwtVcPayload } from '@credo-ts/core' +import type { DisclosureFrame } from '@sd-jwt/types' import type { Server } from 'http' import { @@ -101,7 +102,7 @@ describe('OpenId4Vc', () => { method: 'did', didUrl: verificationMethod.id, }, - disclosureFrame: { university: true, degree: true }, + disclosureFrame: { _sd: ['university', 'degree'] } as DisclosureFrame, } } @@ -663,8 +664,7 @@ describe('OpenId4Vc', () => { name: 'John Doe', }, disclosureFrame: { - university: true, - name: true, + _sd: ['university', 'name'], }, }) diff --git a/yarn.lock b/yarn.lock index 16fa1b44ae..7e3cc04ff9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2373,23 +2373,15 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@sd-jwt/core@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.2.1.tgz#75b0b273758e6be050e042a75bd6a0c4a2a7258e" - integrity sha512-8auyt3mfzgAK+IP9mNc3kSONdo5x2Y8ypNj5gHKP7N81nVeyI+DHethoPQv84JVcqYYcNwHwyrc2Z5k7rg2lFQ== - dependencies: - "@sd-jwt/decode" "0.2.1" - "@sd-jwt/present" "0.2.1" - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" - -"@sd-jwt/decode@0.2.1", "@sd-jwt/decode@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/decode/-/decode-0.2.1.tgz#e0fb32dd2a95440ad69237e66ea2cd4770ec7e09" - integrity sha512-rs55WB3llrMObxN8jeMl06km/h0WivO9jSWNubO9JUIdlfrVhssU38xoXakvQeSDjAJkUUhfZcvmC2vNo1X6Wg== +"@sd-jwt/core@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.6.1.tgz#d28be10d0f4b672636fcf7ad71737cb08e5dae96" + integrity sha512-egFTb23o6BGWF93vnjopN02rSiC1HOOnkk9BI8Kao3jz9ipZAHdO6wF7gwfZm5Nlol/Kd1/KSLhbOUPYt++FjA== dependencies: - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" + "@sd-jwt/decode" "0.6.1" + "@sd-jwt/present" "0.6.1" + "@sd-jwt/types" "0.6.1" + "@sd-jwt/utils" "0.6.1" "@sd-jwt/decode@0.6.1", "@sd-jwt/decode@^0.6.1": version "0.6.1" @@ -2407,15 +2399,7 @@ "@sd-jwt/types" "0.2.0" "@sd-jwt/utils" "0.2.0" -"@sd-jwt/present@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.2.1.tgz#ff9958626b271a60d539dd1e601763ff33c024e8" - integrity sha512-yWIAR2C/q1jNUwzAeUlUcf3WCTEcSSGo9pltHW5AXptELjyaWGSmC5p6o9ucDXHvBnicfPONhe5OdUCSpiCntw== - dependencies: - "@sd-jwt/types" "0.2.1" - "@sd-jwt/utils" "0.2.1" - -"@sd-jwt/present@^0.6.1": +"@sd-jwt/present@0.6.1", "@sd-jwt/present@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.6.1.tgz#82b9188becb0fa240897c397d84a54d55c7d169e" integrity sha512-QRD3TUDLj4PqQNZ70bBxh8FLLrOE9mY8V9qiZrJSsaDOLFs2p1CtZG+v9ig62fxFYJZMf4bWKwYjz+qqGAtxCg== @@ -2429,11 +2413,6 @@ resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.0.tgz#3cb50392e1b76ce69453f403c71c937a6e202352" integrity sha512-16WFRcL/maG0/JxN9UCSx07/vJ2SDbGscv9gDLmFLgJzhJcGPer41XfI6aDfVARYP430wHFixChfY/n7qC1L/Q== -"@sd-jwt/types@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.2.1.tgz#e1e6b47728dffa90ed244e15e2253bd01793cb96" - integrity sha512-nbNik/cq6UIMsN144FcgPZQzaqIsjEEj307j3ZSFORkQBR4Tsmcj54aswTuNh0Z0z/4aSbfw14vOKBZvRWyVLQ== - "@sd-jwt/types@0.6.1", "@sd-jwt/types@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.6.1.tgz#fc4235e00cf40d35a21d6bc02e44e12d7162aa9b" @@ -2447,15 +2426,7 @@ "@sd-jwt/types" "0.2.0" buffer "*" -"@sd-jwt/utils@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.2.1.tgz#35ad83232eab2de911e765d93222acd871982a5e" - integrity sha512-9eRrge44dhE3fenawR/RZGxP5iuW9DtgdOVANu/JK5PEl80r0fDsMwm/gDjuv8OgLDCmQ6uSaVte1lYaTG71bQ== - dependencies: - "@sd-jwt/types" "0.2.1" - buffer "*" - -"@sd-jwt/utils@0.6.1": +"@sd-jwt/utils@0.6.1", "@sd-jwt/utils@^0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@sd-jwt/utils/-/utils-0.6.1.tgz#33273b20c9eb1954e4eab34118158b646b574ff9" integrity sha512-1NHZ//+GecGQJb+gSdDicnrHG0DvACUk9jTnXA5yLZhlRjgkjyfJLNsCZesYeCyVp/SiyvIC9B+JwoY4kI0TwQ==