diff --git a/.changeset/modern-cobras-serve.md b/.changeset/modern-cobras-serve.md new file mode 100644 index 000000000..e8244e7aa --- /dev/null +++ b/.changeset/modern-cobras-serve.md @@ -0,0 +1,8 @@ +--- +"@web5/dids": minor +--- + +1. Vector 3 compliance +2. X25519 support +3. Previous DID link support +4. DNS record chunking support for record > 255 characters (only in context of vector 3 compliance) diff --git a/packages/api/src/web-features.ts b/packages/api/src/web-features.ts index e264b0b08..99d5b9856 100644 --- a/packages/api/src/web-features.ts +++ b/packages/api/src/web-features.ts @@ -1,6 +1,9 @@ declare const ServiceWorkerGlobalScope: any; +/** + * Installs the DWeb networking features in the current environment. + */ export function installNetworkingFeatures(path: string): void { const workerSelf = self as any; diff --git a/packages/dids/package.json b/packages/dids/package.json index 8a3fd0356..78dcd60a3 100644 --- a/packages/dids/package.json +++ b/packages/dids/package.json @@ -89,7 +89,8 @@ "devDependencies": { "@playwright/test": "1.40.1", "@types/bencode": "2.0.4", - "@types/chai": "4.3.6", + "@types/chai": "4.3.16", + "@types/chai-as-promised": "7.1.8", "@types/eslint": "8.56.10", "@types/mocha": "10.0.6", "@types/ms": "0.7.34", @@ -101,6 +102,7 @@ "@web/test-runner-playwright": "0.11.0", "c8": "9.1.0", "chai": "5.1.1", + "chai-as-promised": "7.1.2", "esbuild": "0.19.8", "eslint": "9.3.0", "eslint-plugin-mocha": "10.4.3", diff --git a/packages/dids/src/did-error.ts b/packages/dids/src/did-error.ts index efc0b93d5..687b004cf 100644 --- a/packages/dids/src/did-error.ts +++ b/packages/dids/src/did-error.ts @@ -9,7 +9,7 @@ export class DidError extends Error { * @param message - A human-readable description of the error. */ constructor(public code: DidErrorCode, message: string) { - super(message); + super(`${code}: ${message}`); this.name = 'DidError'; // Ensures that instanceof works properly, the correct prototype chain when using inheritance, @@ -46,6 +46,9 @@ export enum DidErrorCode { /** The DID URL supplied to the dereferencing function does not conform to valid syntax. */ InvalidDidUrl = 'invalidDidUrl', + /** The given proof of a previous DID is invalid */ + InvalidPreviousDidProof = 'invalidPreviousDidProof', + /** An invalid public key is detected during a DID operation. */ InvalidPublicKey = 'invalidPublicKey', diff --git a/packages/dids/src/methods/did-dht.ts b/packages/dids/src/methods/did-dht.ts index 26f5a8ada..4bb3df7f8 100644 --- a/packages/dids/src/methods/did-dht.ts +++ b/packages/dids/src/methods/did-dht.ts @@ -12,7 +12,7 @@ import type { import bencode from 'bencode'; import { Convert } from '@web5/common'; -import { computeJwkThumbprint, Ed25519, LocalKeyManager, Secp256k1, Secp256r1 } from '@web5/crypto'; +import { computeJwkThumbprint, Ed25519, LocalKeyManager, Secp256k1, Secp256r1, X25519 } from '@web5/crypto'; import { AUTHORITATIVE_ANSWER, decode as dnsPacketDecode, encode as dnsPacketEncode } from '@dnsquery/dns-packet'; import type { DidMetadata, PortableDid } from '../types/portable-did.js'; @@ -206,6 +206,17 @@ export interface DidDhtCreateOptions extends DidCreateOptions { verificationMethods?: DidCreateVerificationMethod[]; } +/** + * Proof to used to construct the `_prv._did.` DNS record as described in https://did-dht.com/#rotation to link a DID to a previous DID. + */ +export type PreviousDidProof = { + /** The previous DID. */ + previousDid: string; + + /** The signature signed using the private Identity Key of the previous DID in Base64URL format. */ + signature: string; +}; + /** * The default DID DHT Gateway or Pkarr Relay server to use when publishing and resolving DID * documents. @@ -332,7 +343,7 @@ export enum DidDhtRegisteredKeyType { * Ed25519: A public-key signature system using the EdDSA (Edwards-curve Digital Signature * Algorithm) and Curve25519. */ - Ed25519 = 0, + Ed25519 = 0, /** * secp256k1: A cryptographic curve used for digital signatures in a range of decentralized @@ -344,7 +355,12 @@ export enum DidDhtRegisteredKeyType { * secp256r1: Also known as P-256 or prime256v1, this curve is used for cryptographic operations * and is widely supported in various cryptographic libraries and standards. */ - secp256r1 = 2 + secp256r1 = 2, + + /** + * X25519: A public key used for Diffie-Hellman key exchange using Curve25519. + */ + X25519 = 3, } /** @@ -391,7 +407,8 @@ const AlgorithmToKeyTypeMap = { ES256 : DidDhtRegisteredKeyType.secp256r1, 'P-256' : DidDhtRegisteredKeyType.secp256r1, secp256k1 : DidDhtRegisteredKeyType.secp256k1, - secp256r1 : DidDhtRegisteredKeyType.secp256r1 + secp256r1 : DidDhtRegisteredKeyType.secp256r1, + X25519 : DidDhtRegisteredKeyType.X25519, } as const; /** @@ -401,6 +418,7 @@ const KeyTypeToDefaultAlgorithmMap = { [DidDhtRegisteredKeyType.Ed25519] : 'Ed25519', [DidDhtRegisteredKeyType.secp256k1] : 'ES256K', [DidDhtRegisteredKeyType.secp256r1] : 'ES256', + [DidDhtRegisteredKeyType.X25519] : 'ECDH-ES+A256KW', }; /** @@ -1068,8 +1086,10 @@ export class DidDhtDocument { // other properties from the decoded TXT record data. const { id, t, se, ...customProperties } = DidDhtUtils.parseTxtDataToObject(answer.data); - // The service endpoint can either be a string or an array of strings. - const serviceEndpoint = se.includes(VALUE_SEPARATOR) ? se.split(VALUE_SEPARATOR) : se; + // if multi-values: 'a,b,c' -> ['a', 'b', 'c'], if single-value: 'a' -> ['a'] + // NOTE: The service endpoint technically can either be a string or an array of strings, + // we enforce an array for single-value to simplify verification of vector 3 in the spec: https://did-dht.com/#vector-3 + const serviceEndpoint = se.includes(VALUE_SEPARATOR) ? se.split(VALUE_SEPARATOR) : [se]; // Convert custom property values to either a string or an array of strings. const serviceProperties = Object.fromEntries(Object.entries(customProperties).map( @@ -1135,12 +1155,14 @@ export class DidDhtDocument { * @param params.didDocument - The DID document to convert to a DNS packet. * @param params.didMetadata - The DID metadata to include in the DNS packet. * @param params.authoritativeGatewayUris - The URIs of the Authoritative Gateways to generate NS records from. + * @param params.previousDidProof - The signature proof that this DID is linked to the given previous DID. * @returns A promise that resolves to a DNS packet. */ - public static async toDnsPacket({ didDocument, didMetadata, authoritativeGatewayUris }: { + public static async toDnsPacket({ didDocument, didMetadata, authoritativeGatewayUris, previousDidProof }: { didDocument: DidDocument; didMetadata: DidMetadata; authoritativeGatewayUris?: string[]; + previousDidProof?: PreviousDidProof; }): Promise { const txtRecords: TxtAnswer[] = []; const nsRecords: StringAnswer[] = []; @@ -1148,6 +1170,23 @@ export class DidDhtDocument { const serviceIds: string[] = []; const verificationMethodIds: string[] = []; + // Add `_prv._did.` TXT record if previous DID proof is provided and valid. + if (previousDidProof !== undefined) { + const { signature, previousDid } = previousDidProof; + + await DidDhtUtils.validatePreviousDidProof({ + newDid: didDocument.id, + previousDidProof + }); + + txtRecords.push({ + type : 'TXT', + name : '_prv._did.', + ttl : DNS_RECORD_TTL, + data : `id=${previousDid};s=${signature}` + }); + } + // Add DNS TXT records if the DID document contains an `alsoKnownAs` property. if (didDocument.alsoKnownAs) { txtRecords.push({ @@ -1231,12 +1270,15 @@ export class DidDhtDocument { ([key, value]) => `${key}=${value}` ); + const txtDataString = txtData.join(PROPERTY_SEPARATOR); + const data = DidDhtUtils.chunkDataIfNeeded(txtDataString); + // Add a TXT record for the verification method. txtRecords.push({ type : 'TXT', name : `_${dnsRecordId}._did.`, ttl : DNS_RECORD_TTL, - data : txtData.join(PROPERTY_SEPARATOR) + data }); }); @@ -1475,7 +1517,8 @@ export class DidDhtUtils { bytesToPublicKey : Secp256k1.bytesToPublicKey, privateKeyToBytes : Secp256k1.privateKeyToBytes, bytesToPrivateKey : Secp256k1.bytesToPrivateKey, - } + }, + X25519: X25519, }; const converter = converters[curve]; @@ -1546,4 +1589,44 @@ export class DidDhtUtils { throw new DidError(DidErrorCode.InternalError, 'Pkarr returned DNS TXT record with invalid data type'); } } + + /** + * Validates the proof of previous DID given. + * + * @param params - The parameters to validate the previous DID proof. + * @param params.newDid - The new DID that the previous DID is linking to. + * @param params.previousDidProof - The proof of the previous DID, containing the previous DID and signature signed by the previous DID. + */ + public static async validatePreviousDidProof({ newDid, previousDidProof }: { + newDid: string, + previousDidProof: PreviousDidProof, + }): Promise { + const key = await DidDhtUtils.identifierToIdentityKey({ didUri: previousDidProof.previousDid }); + const data = DidDhtUtils.identifierToIdentityKeyBytes({ didUri: newDid }); + const signature = Convert.base64Url(previousDidProof.signature).toUint8Array(); + const isValid = await Ed25519.verify({ key, data, signature }); + + if (!isValid) { + throw new DidError(DidErrorCode.InvalidPreviousDidProof, 'The previous DID proof is invalid.'); + } + } + + /** + * Splits a string into chunks of length 255 if the string exceeds length 255. + * @param data - The string to split into chunks. + * @returns The original string if its length is less than or equal to 255, otherwise an array of chunked strings. + */ + public static chunkDataIfNeeded(data: string): string | string[] { + if (data.length <= 255) { + return data; + } + + // Split the data into chunks of 255 characters. + const chunks: string[] = []; + for (let i = 0; i < data.length; i += 255) { + chunks.push(data.slice(i, i + 255)); // end index is ignored if it exceeds the length of the string + } + + return chunks; + } } \ No newline at end of file diff --git a/packages/dids/tests/fixtures/test-vectors/did-dht/vector-2.json b/packages/dids/tests/fixtures/test-vectors/did-dht/vector-2.json index 72a212672..81957eed4 100644 --- a/packages/dids/tests/fixtures/test-vectors/did-dht/vector-2.json +++ b/packages/dids/tests/fixtures/test-vectors/did-dht/vector-2.json @@ -52,6 +52,9 @@ } ] }, + "authoritativeGatewayUris": [ + "gateway1.example-did-dht-gateway.com" + ], "dnsRecords": [ { "name": "_did.cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo.", diff --git a/packages/dids/tests/fixtures/test-vectors/did-dht/vector-3.json b/packages/dids/tests/fixtures/test-vectors/did-dht/vector-3.json new file mode 100644 index 000000000..eb2b3bdf3 --- /dev/null +++ b/packages/dids/tests/fixtures/test-vectors/did-dht/vector-3.json @@ -0,0 +1,108 @@ +{ + "didDocument": { + "id": "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy", + "verificationMethod": [ + { + "id": "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#0", + "type": "JsonWebKey", + "controller": "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy", + "publicKeyJwk": { + "kid": "0", + "alg": "Ed25519", + "crv": "Ed25519", + "kty": "OKP", + "x": "sTyTLYw-n1NI9X-84NaCuis1wZjAA8lku6f6Et5201g" + } + }, + { + "id": "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#WVy5IWMa36AoyAXZDvPd5j9zxt2t-GjifDEV-DwgIdQ", + "type": "JsonWebKey", + "controller": "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy", + "publicKeyJwk": { + "kid": "WVy5IWMa36AoyAXZDvPd5j9zxt2t-GjifDEV-DwgIdQ", + "alg": "ECDH-ES+A128KW", + "crv": "X25519", + "kty": "OKP", + "x": "3POE0_i2mGeZ2qiQCA3KcLfi1fZo0311CXFSIwt1nB4" + } + } + ], + "authentication": [ + "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#0" + ], + "assertionMethod": [ + "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#0" + ], + "keyAgreement": [ + "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#WVy5IWMa36AoyAXZDvPd5j9zxt2t-GjifDEV-DwgIdQ" + ], + "capabilityInvocation": [ + "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#0" + ], + "capabilityDelegation": [ + "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#0" + ], + "service": [ + { + "id": "did:dht:sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy#service-1", + "type": "TestLongService", + "serviceEndpoint": ["https://test-lllllllllllllllllllllllllllllllllllooooooooooooooooooooonnnnnnnnnnnnnnnnnnngggggggggggggggggggggggggggggggggggggsssssssssssssssssssssssssseeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrvvvvvvvvvvvvvvvvvvvviiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiccccccccccccccccccccccccccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.com/1"] + } + ] + }, + "authoritativeGatewayUris": [ + "gateway1.example-did-dht-gateway.com", + "gateway2.example-did-dht-gateway.com" + ], + "previousDidProof": { + "previousDid": "did:dht:x3heus3ke8fhgb5pbecday9wtbfynd6m19q4pm6gcf5j356qhjzo", + "signature": "Tt9DRT6J32v7O2lzbfasW63_FfagiMHTHxtaEOD7p85zHE0r_EfiNleyL6BZGyB1P-oQ5p6_7KONaHAjr2K6Bw" + }, + "dnsRecords": [ + { + "name": "_prv._did.", + "type": "TXT", + "ttl": 7200, + "rdata": "id=did:dht:x3heus3ke8fhgb5pbecday9wtbfynd6m19q4pm6gcf5j356qhjzo;s=Tt9DRT6J32v7O2lzbfasW63_FfagiMHTHxtaEOD7p85zHE0r_EfiNleyL6BZGyB1P-oQ5p6_7KONaHAjr2K6Bw" + }, + { + "name": "_did.sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy.", + "type": "NS", + "ttl": 7200, + "rdata": "gateway1.example-did-dht-gateway.com." + }, + { + "name": "_did.sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy.", + "type": "NS", + "ttl": 7200, + "rdata": "gateway2.example-did-dht-gateway.com." + }, + { + "name": "_did.sr6jgmcc84xig18ix66qbiwnzeiumocaaybh13f5w97bfzus4pcy.", + "type": "TXT", + "ttl": 7200, + "rdata": "v=0;vm=k0,k1;auth=k0;asm=k0;agm=k1;inv=k0;del=k0;svc=s0" + }, + { + "name": "_k0._did.", + "type": "TXT", + "ttl": 7200, + "rdata": "t=0;k=sTyTLYw-n1NI9X-84NaCuis1wZjAA8lku6f6Et5201g" + }, + { + "name": "_k1._did.", + "type": "TXT", + "ttl": 7200, + "rdata": "t=3;k=3POE0_i2mGeZ2qiQCA3KcLfi1fZo0311CXFSIwt1nB4;a=ECDH-ES+A128KW" + }, + { + "name": "_s0._did.", + "type": "TXT", + "ttl": 7200, + "rdata": [ + "id=service-1;t=TestLongService;se=https://test-lllllllllllllllllllllllllllllllllllooooooooooooooooooooonnnnnnnnnnnnnnnnnnngggggggggggggggggggggggggggggggggggggsssssssssssssssssssssssssseeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrvvvvvvvvvvvvvvvvvvvviiiiiiiiiiiiiiii", + "iiiiiiiiiiiiiiiccccccccccccccccccccccccccccccceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.com/1" + ] + } + ] +} \ No newline at end of file diff --git a/packages/dids/tests/methods/did-dht.spec.ts b/packages/dids/tests/methods/did-dht.spec.ts index d08b3586a..edd956f4e 100644 --- a/packages/dids/tests/methods/did-dht.spec.ts +++ b/packages/dids/tests/methods/did-dht.spec.ts @@ -1,16 +1,20 @@ import type { PortableDid } from '../../src/types/portable-did.js'; +import chaiAsPromised from 'chai-as-promised'; import sinon from 'sinon'; import resolveTestVectors from '../../../../web5-spec/test-vectors/did_dht/resolve.json' assert { type: 'json' }; import officialTestVector1 from '../fixtures/test-vectors/did-dht/vector-1.json' assert { type: 'json' }; import officialTestVector2 from '../fixtures/test-vectors/did-dht/vector-2.json' assert { type: 'json' }; +import officialTestVector3 from '../fixtures/test-vectors/did-dht/vector-3.json' assert { type: 'json' }; -import { expect } from 'chai'; import { Answer } from '@dnsquery/dns-packet'; import { Convert } from '@web5/common'; import { DidDocument } from '../../src/index.js'; import { DidErrorCode } from '../../src/did-error.js'; -import { DidDht, DidDhtDocument, DidDhtRegisteredDidType } from '../../src/methods/did-dht.js'; +import { DidDht, DidDhtDocument, DidDhtRegisteredDidType, DidDhtUtils } from '../../src/methods/did-dht.js'; +import { expect, use } from 'chai'; + +use(chaiAsPromised); // Helper function to create a mocked fetch response that fails and returns a 404 Not Found. const fetchNotFoundResponse = () => ({ @@ -1279,6 +1283,16 @@ describe('DidDhtDocument', () => { }); }); +describe('DidDhtUtils', () => { + it('validatePreviousDidProof()', async () => { + // reuse an existing previous proof from the official test vector 3, but use a different new DID + const previousDidProof = { ...officialTestVector3.previousDidProof }; + const newDid = (await DidDht.create()).document.id; + + await expect(DidDhtUtils.validatePreviousDidProof({ newDid, previousDidProof })).to.be.rejectedWith(DidErrorCode.InvalidPreviousDidProof); + }); +}); + // vectors come from https://did-dht.com/#test-vectors describe('Official DID:DHT Vector tests', () => { // Temporarily disabling due to inability to add a custom `kid` to our JWK in the current deployment. @@ -1315,7 +1329,7 @@ describe('Official DID:DHT Vector tests', () => { published : false, types : [DidDhtRegisteredDidType.Organization, DidDhtRegisteredDidType.Government, DidDhtRegisteredDidType.Corporation] }, - authoritativeGatewayUris: ['gateway1.example-did-dht-gateway.com'] + authoritativeGatewayUris: officialTestVector2.authoritativeGatewayUris, }); expect(dnsPacket.answers).to.have.length(officialTestVector2.dnsRecords.length); @@ -1330,6 +1344,30 @@ describe('Official DID:DHT Vector tests', () => { expect(didResolutionResult.didDocument).to.deep.equal(inputDidDocument); }); + + it('vector 3', async () => { + const inputDidDocument = officialTestVector3.didDocument as DidDocument; + const dnsPacket = await DidDhtDocument.toDnsPacket({ + didDocument : inputDidDocument, + didMetadata : { + published: false + }, + authoritativeGatewayUris : officialTestVector3.authoritativeGatewayUris, + previousDidProof : officialTestVector3.previousDidProof, + }); + + expect(dnsPacket.answers).to.have.length(officialTestVector3.dnsRecords.length); + + const normalizedConstructedRecords = normalizeDnsRecords(dnsPacket.answers!); + expect(normalizedConstructedRecords).to.deep.include.members(officialTestVector3.dnsRecords); + + const didResolutionResult = await DidDhtDocument.fromDnsPacket({ + didUri : inputDidDocument.id, + dnsPacket : dnsPacket + }); + + expect(didResolutionResult.didDocument).to.deep.equal(inputDidDocument); + }); }); /** diff --git a/packages/dids/tests/methods/did-ion.spec.ts b/packages/dids/tests/methods/did-ion.spec.ts index ffcdf5574..2e64d5f87 100644 --- a/packages/dids/tests/methods/did-ion.spec.ts +++ b/packages/dids/tests/methods/did-ion.spec.ts @@ -1,16 +1,16 @@ +import type { DidDocument } from '../../src/types/did-core.js'; import type { Jwk } from '@web5/crypto'; +import type { PortableDid } from '../../src/types/portable-did.js'; +import chaiAsPromised from 'chai-as-promised'; import sinon from 'sinon'; -import { expect } from 'chai'; import { computeJwkThumbprint } from '@web5/crypto'; - -import type { DidDocument } from '../../src/types/did-core.js'; -import type { PortableDid } from '../../src/types/portable-did.js'; - import { DidIon } from '../../src/methods/did-ion.js'; import { vectors as CreateTestVector } from '../fixtures/test-vectors/did-ion/create.js'; import { vectors as ResolveTestVector } from '../fixtures/test-vectors/did-ion/resolve.js'; +import { expect, use } from 'chai'; +use(chaiAsPromised); // Helper function to create a mocked fetch response that fails and returns a 404 Not Found. const fetchNotFoundResponse = () => ({ status : 404, @@ -520,12 +520,7 @@ describe('DidIon', () => { ], }; - try { - await DidIon.getSigningMethod({ didDocument }); - expect.fail('Error should have been thrown'); - } catch (error: any) { - expect(error.message).to.equal('Method not supported: example'); - } + await expect(DidIon.getSigningMethod({ didDocument })).to.be.rejectedWith('Method not supported: example'); }); }); diff --git a/packages/dids/tests/methods/did-jwk.spec.ts b/packages/dids/tests/methods/did-jwk.spec.ts index 46bbeb0e8..38eb84cf2 100644 --- a/packages/dids/tests/methods/did-jwk.spec.ts +++ b/packages/dids/tests/methods/did-jwk.spec.ts @@ -1,15 +1,17 @@ +import type { DidDocument } from '../../src/types/did-core.js'; import type { Jwk } from '@web5/crypto'; +import type { PortableDid } from '../../src/types/portable-did.js'; import type { UnwrapPromise } from '@web5/common'; -import { expect } from 'chai'; -import { LocalKeyManager } from '@web5/crypto'; - -import type { DidDocument } from '../../src/types/did-core.js'; -import type { PortableDid } from '../../src/types/portable-did.js'; +import chaiAsPromised from 'chai-as-promised'; +import DidJwkResolveTestVector from '../../../../web5-spec/test-vectors/did_jwk/resolve.json' assert { type: 'json' }; import { DidErrorCode } from '../../src/did-error.js'; import { DidJwk } from '../../src/methods/did-jwk.js'; -import DidJwkResolveTestVector from '../../../../web5-spec/test-vectors/did_jwk/resolve.json' assert { type: 'json' }; +import { LocalKeyManager } from '@web5/crypto'; +import { expect, use } from 'chai'; + +use(chaiAsPromised); describe('DidJwk', () => { let keyManager: LocalKeyManager; @@ -184,12 +186,7 @@ describe('DidJwk', () => { ], }; - try { - await DidJwk.getSigningMethod({ didDocument }); - expect.fail('Error should have been thrown'); - } catch (error: any) { - expect(error.message).to.equal('Method not supported: example'); - } + await expect(DidJwk.getSigningMethod({ didDocument })).to.eventually.be.rejectedWith('Method not supported: example'); }); }); diff --git a/packages/dids/tests/methods/did-key.spec.ts b/packages/dids/tests/methods/did-key.spec.ts index cfc7b4661..38807122e 100644 --- a/packages/dids/tests/methods/did-key.spec.ts +++ b/packages/dids/tests/methods/did-key.spec.ts @@ -1,13 +1,14 @@ -import type { Jwk } from '@web5/crypto'; - -import { expect } from 'chai'; -import { LocalKeyManager } from '@web5/crypto'; - import type { DidDocument } from '../../src/types/did-core.js'; +import type { Jwk } from '@web5/crypto'; import type { PortableDid } from '../../src/types/portable-did.js'; +import chaiAsPromised from 'chai-as-promised'; import { DidErrorCode } from '../../src/did-error.js'; +import { LocalKeyManager } from '@web5/crypto'; import { DidKey, DidKeyUtils } from '../../src/methods/did-key.js'; +import { expect, use } from 'chai'; + +use(chaiAsPromised); describe('DidKey', () => { let keyManager: LocalKeyManager; @@ -276,12 +277,7 @@ describe('DidKey', () => { ], }; - try { - await DidKey.getSigningMethod({ didDocument }); - expect.fail('Error should have been thrown'); - } catch (error: any) { - expect(error.message).to.equal('Method not supported: example'); - } + await expect(DidKey.getSigningMethod({ didDocument })).to.be.rejectedWith('Method not supported: example'); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d24d8f90f..6dafac49c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -622,8 +622,11 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/chai': - specifier: 4.3.6 - version: 4.3.6 + specifier: 4.3.16 + version: 4.3.16 + '@types/chai-as-promised': + specifier: 7.1.8 + version: 7.1.8 '@types/eslint': specifier: 8.56.10 version: 8.56.10 @@ -657,6 +660,9 @@ importers: chai: specifier: 5.1.1 version: 5.1.1 + chai-as-promised: + specifier: 7.1.2 + version: 7.1.2(chai@5.1.1) esbuild: specifier: 0.19.8 version: 0.19.8