UNPKG

@credo-ts/core

Version:

<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1

1 lines • 14.5 kB
{"version":3,"file":"EddsaJcs2022Cryptosuite.mjs","names":[],"sources":["../../../../../src/modules/w3c-di/cryptosuites/eddsa-jcs-2022/EddsaJcs2022Cryptosuite.ts"],"sourcesContent":["import { canonicalizeEx } from 'json-canonicalize'\n\nimport type { AgentContext } from '../../../../agent/context'\nimport { Hasher } from '../../../../crypto'\nimport { asArray, MultiBaseEncoder, TypedArrayEncoder } from '../../../../utils'\nimport { isObject } from '../../../../utils/object'\nimport { KeyManagementApi } from '../../../kms'\nimport { isXsdDateTimeStamp } from '../../proof-processing/iso8601Datetime'\nimport { publicJwkFromVerificationMethodId, publicKeyIdFromVerificationMethodId } from '../../proof-processing/keyUtils'\nimport { omitUndefinedFields } from '../../proof-processing/normalization'\nimport { W3cDataIntegrityProcessingError, W3cDataIntegrityProcessingErrorCode } from '../../W3cDataIntegrityError'\nimport type {\n W3cDataIntegrityCryptosuiteProof,\n W3cDataIntegrityCryptosuiteProofOptions,\n W3cDataIntegrityUnsecuredDocument,\n} from '../../W3cDataIntegrityProof'\nimport type {\n W3cDataIntegrityCryptosuite,\n W3cDataIntegrityProofVerificationInput,\n W3cDataIntegrityProofVerificationResult,\n} from '../types'\n\nexport class EddsaJcs2022Cryptosuite implements W3cDataIntegrityCryptosuite {\n public readonly cryptosuite = 'eddsa-jcs-2022'\n private keyManagementApi: KeyManagementApi\n private agentContext: AgentContext\n\n public constructor(agentContext: AgentContext) {\n this.agentContext = agentContext\n this.keyManagementApi = agentContext.dependencyManager.resolve(KeyManagementApi)\n }\n\n private canonicalizeJcsStrict(value: unknown) {\n const normalized = omitUndefinedFields(value)\n assertJcsInput(normalized)\n return canonicalizeEx(normalized, {\n allowCircular: false,\n filterUndefined: true,\n undefinedInArrayToNull: false,\n })\n }\n\n /*\n * Spec: VC DI EdDSA v1.0 §§3.3.1\n * https://www.w3.org/TR/vc-di-eddsa/#create-proof-eddsa-jcs-2022\n */\n public async createProof(\n unsecuredDocument: W3cDataIntegrityUnsecuredDocument,\n options: W3cDataIntegrityCryptosuiteProofOptions\n ): Promise<W3cDataIntegrityCryptosuiteProof> {\n // Boundary contract: callers must pass DataIntegrityProof + eddsa-jcs-2022 options.\n // W3cDataIntegrityProofService is the normal enforcement gate, but this guard protects\n // direct callers as well and keeps failures close to cryptosuite entry points.\n this.assertProofTypeAndCryptosuite(options, W3cDataIntegrityProcessingErrorCode.ProofGenerationError, 'createProof')\n\n const proof: W3cDataIntegrityCryptosuiteProofOptions = { ...options } // 1\n\n if ('@context' in unsecuredDocument) {\n proof['@context'] = unsecuredDocument['@context']\n } // 2\n\n const proofConfig = this.proofConfiguration(proof) // 3\n const transformedDocument = this.transformation(unsecuredDocument, options) // 4\n const hashData = this.hashing(transformedDocument, proofConfig) // 5\n const proofBytes = await this.proofSerialization(hashData, options) // 6\n const proofValue = MultiBaseEncoder.encode(proofBytes, 'base58btc') // 7\n\n return {\n ...proof,\n proofValue,\n } // 8\n }\n\n /*\n * Spec: VC DI EdDSA v1.0 §§3.3.2\n * https://www.w3.org/TR/vc-di-eddsa/#verify-proof-eddsa-jcs-2022\n */\n public async verifyProof(\n input: W3cDataIntegrityProofVerificationInput\n ): Promise<W3cDataIntegrityProofVerificationResult> {\n const { unsecuredDocument: securedDocument, proof } = input\n const unsecuredDocument: W3cDataIntegrityUnsecuredDocument = { ...securedDocument } // 1\n const { proofValue, ...proofOptions } = proof // 2\n\n // Boundary contract: when verifyProof is called directly, validate proof suite\n // identity before running cryptographic verification.\n this.assertProofTypeAndCryptosuite(\n proofOptions,\n W3cDataIntegrityProcessingErrorCode.ProofVerificationError,\n 'verifyProof'\n )\n\n const proofBytes = MultiBaseEncoder.decode(proofValue).data // 3\n\n if ('@context' in proofOptions) {\n // 4\n const proofContext = asArray(proofOptions['@context'])\n const documentContext = asArray(unsecuredDocument['@context'])\n\n const hasMatchingContext =\n documentContext.length >= proofContext.length &&\n proofContext.every((value, index) => documentContext[index] === value) // 4.1\n\n if (!hasMatchingContext) {\n return {\n verified: false,\n verifiedDocument: null,\n } // 4.1\n }\n\n unsecuredDocument['@context'] = proofOptions['@context'] // 4.2\n }\n\n const transformedDocument = this.transformation(unsecuredDocument, proofOptions) // 5\n const proofConfig = this.proofConfiguration(proofOptions) // 6\n const hashData = this.hashing(transformedDocument, proofConfig) // 7\n const verified = await this.proofVerification(hashData, proofBytes, proofOptions) // 8\n\n return {\n verified,\n verifiedDocument: verified ? unsecuredDocument : null,\n } // 9\n }\n\n /*\n * Spec: VC DI EdDSA v1.0 §§3.3.3\n * https://www.w3.org/TR/vc-di-eddsa/#transformation-eddsa-jcs-2022\n */\n public transformation(\n unsecuredDocument: W3cDataIntegrityUnsecuredDocument,\n proofOptions: W3cDataIntegrityCryptosuiteProofOptions\n ) {\n if (proofOptions.type !== 'DataIntegrityProof' || proofOptions.cryptosuite !== 'eddsa-jcs-2022') {\n const err = `Proof type must be 'DataIntegrityProof' AND cryptosuite must be 'eddsa-jcs-2022'`\n throw new W3cDataIntegrityProcessingError(W3cDataIntegrityProcessingErrorCode.ProofTransformationError, err)\n } // 1\n\n const canonicalDocument = this.canonicalizeJcsStrict(unsecuredDocument) // 2\n return canonicalDocument // 3\n }\n\n /*\n * Spec: VC DI EdDSA v1.0 §§3.3.4\n * https://www.w3.org/TR/vc-di-eddsa/#hashing-eddsa-jcs-2022\n */\n public hashing(transformedDocument: string, canonicalProofConfig: string) {\n const transformedDocumentHash = Hasher.hash(TypedArrayEncoder.fromUtf8String(transformedDocument), 'sha-256') // 1\n const proofConfigHash = Hasher.hash(TypedArrayEncoder.fromUtf8String(canonicalProofConfig), 'sha-256') // 2\n const hashData = new Uint8Array(64) // 3 create empty 64-byte array\n hashData.set(proofConfigHash, 0) // 3 append proofConfigHash from 0-31\n hashData.set(transformedDocumentHash, 32) // 3 append transformedDocumentHash from 32-63\n return hashData // 4\n }\n\n /*\n * Spec: VC DI EdDSA v1.0 §§3.3.5\n * https://www.w3.org/TR/vc-di-eddsa/#proof-configuration-eddsa-jcs-2022\n */\n public proofConfiguration(proofOptions: W3cDataIntegrityCryptosuiteProofOptions) {\n const proofConfig = { ...proofOptions } // 1\n\n if (proofConfig.type !== 'DataIntegrityProof' || proofConfig.cryptosuite !== 'eddsa-jcs-2022') {\n const err = `Proof type must be 'DataIntegrityProof' AND cryptosuite must be 'eddsa-jcs-2022'`\n throw new W3cDataIntegrityProcessingError(W3cDataIntegrityProcessingErrorCode.ProofGenerationError, err)\n }\n\n if (typeof proofConfig.created === 'string' && !isXsdDateTimeStamp(proofConfig.created)) {\n const err = `Proof created must be a valid dateTimeStamp. Received '${proofConfig.created}'`\n throw new W3cDataIntegrityProcessingError(W3cDataIntegrityProcessingErrorCode.ProofGenerationError, err)\n }\n\n const canonicalProofConfig = this.canonicalizeJcsStrict(proofConfig) // 4\n return canonicalProofConfig // 5\n }\n\n /*\n * Spec: VC DI EdDSA v1.0 §§3.3.6\n * https://www.w3.org/TR/vc-di-eddsa/#proof-serialization-eddsa-jcs-2022\n */\n public async proofSerialization(hashData: Uint8Array, options: W3cDataIntegrityCryptosuiteProofOptions) {\n // Caller (W3cDataIntegrityProofService) is responsible for required-member and suite-shape validation before invocation.\n // This method intentionally focuses on §3.3.6 signing semantics only.\n\n const verificationMethod = options.verificationMethod\n const keyId = await publicKeyIdFromVerificationMethodId(this.agentContext, verificationMethod) // 1\n const signResult = await this.keyManagementApi.sign({\n keyId,\n algorithm: 'EdDSA',\n data: hashData,\n })\n const proofBytes = signResult.signature // 2\n if (proofBytes.length !== 64) {\n const err = `EdDSA signature must be exactly 64 bytes, got ${proofBytes.length}`\n throw new W3cDataIntegrityProcessingError(W3cDataIntegrityProcessingErrorCode.ProofGenerationError, err)\n } // 2\n return proofBytes // 3\n }\n\n /*\n * Spec: VC DI EdDSA v1.0 §§3.3.7\n * https://www.w3.org/TR/vc-di-eddsa/#proof-verification-eddsa-jcs-2022\n */\n public async proofVerification(\n hashData: Uint8Array,\n proofBytes: Uint8Array,\n options: W3cDataIntegrityCryptosuiteProofOptions\n ) {\n const verificationMethod = options.verificationMethod\n const publicKeyBytes = await publicJwkFromVerificationMethodId(this.agentContext, verificationMethod) // 1\n const verificationResult = await this.keyManagementApi.verify({\n key: { publicJwk: publicKeyBytes.toJson() },\n algorithm: 'EdDSA',\n signature: proofBytes,\n data: hashData,\n }) // 2\n return verificationResult.verified // 3\n }\n\n private assertProofTypeAndCryptosuite(\n options: Partial<W3cDataIntegrityCryptosuiteProofOptions>,\n errorCode: W3cDataIntegrityProcessingErrorCode,\n caller: 'createProof' | 'verifyProof'\n ) {\n if (options.type !== 'DataIntegrityProof' || options.cryptosuite !== 'eddsa-jcs-2022') {\n const err = `Proof type must be 'DataIntegrityProof' AND cryptosuite must be 'eddsa-jcs-2022'`\n throw new W3cDataIntegrityProcessingError(errorCode, err)\n }\n }\n}\n\nfunction assertJcsInput(value: unknown, path = '$', seen = new WeakSet<object>()) {\n if (value === undefined || typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint') {\n const err = `JCS canonicalization input contains unsupported value at ${path}`\n throw new W3cDataIntegrityProcessingError(W3cDataIntegrityProcessingErrorCode.ProofTransformationError, err)\n }\n\n if (typeof value === 'number' && !Number.isFinite(value)) {\n const err = `JCS canonicalization input contains non-finite number at ${path}`\n throw new W3cDataIntegrityProcessingError(W3cDataIntegrityProcessingErrorCode.ProofTransformationError, err)\n }\n\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n assertJcsInput(value[i], `${path}[${i}]`, seen)\n }\n return\n }\n\n if (isObject(value)) {\n const objectValue = value as object\n if (seen.has(objectValue)) {\n const err = `JCS canonicalization input contains circular reference at ${path}`\n throw new W3cDataIntegrityProcessingError(W3cDataIntegrityProcessingErrorCode.ProofTransformationError, err)\n }\n\n seen.add(objectValue)\n\n for (const [key, nestedValue] of Object.entries(value as Record<string, unknown>)) {\n assertJcsInput(nestedValue, `${path}.${key}`, seen)\n }\n }\n}\n"],"mappings":"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"}