From d2d817fa12f254c0797ea0a23e2fd273c64fa1b4 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Wed, 28 May 2025 18:07:39 +0530 Subject: [PATCH] fix(cheqd): Include removed authentication signatures on did update Signed-off-by: DaevMithran --- packages/cheqd/src/dids/CheqdDidRegistrar.ts | 224 ++++++++++--------- packages/cheqd/src/dids/didCheqdUtil.ts | 6 +- 2 files changed, 123 insertions(+), 107 deletions(-) diff --git a/packages/cheqd/src/dids/CheqdDidRegistrar.ts b/packages/cheqd/src/dids/CheqdDidRegistrar.ts index b1c2118014..5394568ebf 100644 --- a/packages/cheqd/src/dids/CheqdDidRegistrar.ts +++ b/packages/cheqd/src/dids/CheqdDidRegistrar.ts @@ -42,6 +42,7 @@ import { createMsgCreateDidDocPayloadToSign, createMsgDeactivateDidDocPayloadToSign, generateDidDoc, + getVerificationMethodId, validateSpecCompliantPayload, } from './didCheqdUtil' @@ -146,6 +147,7 @@ export class CheqdDidRegistrar implements DidRegistrar { } } else if (options.options.createKey || options.options.keyId) { const methodSpecificIdAlgo = options.options.methodSpecificIdAlgo + const verificationMethod = options.options.verificationMethodType || VerificationMethods.JWK const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) let publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } @@ -194,8 +196,6 @@ export class CheqdDidRegistrar implements DidRegistrar { } } - // TODO: make this configureable - const verificationMethod = VerificationMethods.JWK const jwk = Kms.PublicJwk.fromPublicJwk(publicJwk) didDocument = generateDidDoc({ @@ -286,126 +286,125 @@ export class CheqdDidRegistrar implements DidRegistrar { let didRecord: DidRecord | null try { - if (options.didDocument) { - const isSpecCompliantPayload = validateSpecCompliantPayload(options.didDocument) - if (!isSpecCompliantPayload.valid) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `Invalid did document provided. ${isSpecCompliantPayload.error}`, - }, - } + if (!options.didDocument) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Provide a valid didDocument', + }, } + } - didDocument = options.didDocument - const resolvedDocument = await cheqdLedgerService.resolve(didDocument.id) - didRecord = await didRepository.findCreatedDid(agentContext, didDocument.id) - if (!resolvedDocument.didDocument || resolvedDocument.didDocumentMetadata.deactivated || !didRecord) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Did not found', - }, - } + const isSpecCompliantPayload = validateSpecCompliantPayload(options.didDocument) + if (!isSpecCompliantPayload.valid) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Invalid did document provided. ${isSpecCompliantPayload.error}`, + }, } + } - const keys = didRecord.keys ?? [] - if (options.options?.createKey || options.options?.keyId) { - const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) - let createdKey: DidDocumentKey + didDocument = options.didDocument + const resolvedDocument = await cheqdLedgerService.resolve(didDocument.id) + didRecord = await didRepository.findCreatedDid(agentContext, didDocument.id) + if (!resolvedDocument.didDocument || resolvedDocument.didDocumentMetadata.deactivated || !didRecord) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did not found', + }, + } + } - let publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } - if (options.options.createKey) { - const createKeyResult = await kms.createKey(options.options.createKey) - publicJwk = createKeyResult.publicJwk + const keys = didRecord.keys ?? [] + if (options.options?.createKey || options.options?.keyId) { + const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) + let createdKey: DidDocumentKey - createdKey = { - didDocumentRelativeKeyId: `#${utils.uuid()}-1`, - kmsKeyId: createKeyResult.keyId, - } - } else if (options.options.keyId) { - const _publicJwk = await kms.getPublicKey({ - keyId: options.options.keyId, - }) - createdKey = { - didDocumentRelativeKeyId: `#${utils.uuid()}-1`, - kmsKeyId: options.options.keyId, - } - if (!_publicJwk) { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `notFound: key with key id '${options.options.keyId}' not found`, - }, - } - } + let publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } + if (options.options.createKey) { + const createKeyResult = await kms.createKey(options.options.createKey) + publicJwk = createKeyResult.publicJwk - if (_publicJwk.kty !== 'OKP' || _publicJwk.crv !== 'Ed25519') { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: `key with key id '${options.options.keyId}' uses unsupported ${Kms.getJwkHumanDescription( - _publicJwk - )} for did:cheqd`, - }, - } + createdKey = { + didDocumentRelativeKeyId: `#${utils.uuid()}-1`, + kmsKeyId: createKeyResult.keyId, + } + } else if (options.options.keyId) { + const _publicJwk = await kms.getPublicKey({ + keyId: options.options.keyId, + }) + createdKey = { + didDocumentRelativeKeyId: `#${utils.uuid()}-1`, + kmsKeyId: options.options.keyId, + } + if (!_publicJwk) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `notFound: key with key id '${options.options.keyId}' not found`, + }, } + } - publicJwk = { - ..._publicJwk, - crv: _publicJwk.crv, - } - } else { - // This will never happen, but to make TS happy + if (_publicJwk.kty !== 'OKP' || _publicJwk.crv !== 'Ed25519') { return { didDocumentMetadata: {}, didRegistrationMetadata: {}, didState: { state: 'failed', - reason: 'Expect options.createKey or options.keyId', + reason: `key with key id '${options.options.keyId}' uses unsupported ${Kms.getJwkHumanDescription( + _publicJwk + )} for did:cheqd`, }, } } - // TODO: make this configureable - const verificationMethod = VerificationMethods.JWK - const jwk = Kms.PublicJwk.fromPublicJwk(publicJwk) - - keys.push(createdKey) - didDocument.verificationMethod?.concat( - JsonTransformer.fromJSON( - createDidVerificationMethod( - [verificationMethod], - [ - { - methodSpecificId: didDocument.id.split(':')[3], - didUrl: didDocument.id, - keyId: `${didDocument.id}${createdKey.didDocumentRelativeKeyId}` as `${string}#${string}-${number}`, - publicKey: TypedArrayEncoder.toHex(jwk.publicKey.publicKey), - }, - ] - ), - VerificationMethod - ) - ) - } - } else { - return { - didDocumentMetadata: {}, - didRegistrationMetadata: {}, - didState: { - state: 'failed', - reason: 'Provide a valid didDocument', - }, + publicJwk = { + ..._publicJwk, + crv: _publicJwk.crv, + } + } else { + // This will never happen, but to make TS happy + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Expect options.createKey or options.keyId', + }, + } } + + const verificationMethod = options.options.verificationMethodType || VerificationMethods.JWK + const jwk = Kms.PublicJwk.fromPublicJwk(publicJwk) + + keys.push(createdKey) + didDocument.verificationMethod?.concat( + JsonTransformer.fromJSON( + createDidVerificationMethod( + [verificationMethod], + [ + { + methodSpecificId: didDocument.id.split(':')[3], + didUrl: didDocument.id, + keyId: `${didDocument.id}${createdKey.didDocumentRelativeKeyId}` as `${string}#${string}-${number}`, + publicKey: TypedArrayEncoder.toHex(jwk.publicKey.publicKey), + }, + ] + ), + VerificationMethod + ) + ) } // Filter out all keys that are not present in the did document anymore @@ -437,9 +436,20 @@ export class CheqdDidRegistrar implements DidRegistrar { const payloadToSign = await createMsgCreateDidDocPayloadToSign(didDocument.toJSON() as DIDDocument, versionId) - const authentication = didDocument.authentication?.map((authentication) => - typeof authentication === 'string' ? didDocument.dereferenceVerificationMethod(authentication) : authentication - ) + const authSet = new Set() + + const authentication: VerificationMethod[] = [ + ...(didDocument.authentication || []), + ...(resolvedDocument.didDocument.authentication || []), + ].reduce((uniqueAuths, auth) => { + const id = getVerificationMethodId(auth) + if (!authSet.has(id)) { + authSet.add(id) + uniqueAuths.push(typeof auth === 'string' ? didDocument.dereferenceVerificationMethod(auth) : auth) + } + return uniqueAuths + }, []) + if (!authentication || authentication.length === 0) { return { didDocumentMetadata: {}, @@ -685,6 +695,7 @@ export interface CheqdDidCreateWithoutDidDocumentOptions extends DidCreateOption fee?: DidStdFee versionId?: string methodSpecificIdAlgo?: `${MethodSpecificIdAlgo}` + verificationMethodType?: VerificationMethods } & XOR<{ createKey: KmsCreateKeyOptionsOkpEd25519 }, { keyId: string }> } @@ -716,6 +727,7 @@ export interface CheqdDidUpdateOptions extends DidUpdateOptions { * in the did document anymore, and this new list will be merged into it. */ keys?: DidDocumentKey[] + verificationMethodType?: VerificationMethods fee?: DidStdFee versionId?: string diff --git a/packages/cheqd/src/dids/didCheqdUtil.ts b/packages/cheqd/src/dids/didCheqdUtil.ts index c0dc82f30b..f83b8ed5ab 100644 --- a/packages/cheqd/src/dids/didCheqdUtil.ts +++ b/packages/cheqd/src/dids/didCheqdUtil.ts @@ -1,4 +1,4 @@ -import type { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, TVerificationKey } from '@cheqd/sdk' +import type { CheqdNetwork, DIDDocument, MethodSpecificIdAlgo, TVerificationKey, VerificationMethod } from '@cheqd/sdk' import type { Metadata } from '@cheqd/ts-proto/cheqd/resource/v2' import { @@ -160,3 +160,7 @@ export function getCosmosPayerWallet(cosmosPayerSeed?: string) { ? DirectSecp256k1HdWallet.fromMnemonic(cosmosPayerSeed, { prefix: 'cheqd' }) : DirectSecp256k1Wallet.fromKey(TypedArrayEncoder.fromString(cosmosPayerSeed.replace(/^0x/, '')), 'cheqd') } + +export function getVerificationMethodId(vm: string | VerificationMethod) { + return typeof vm === 'string' ? vm : vm.id +}