Skip to content

Commit 857d160

Browse files
authored
Updated DID:DHT implementation to be compliant with vector 1 (#504)
- Updated DID:DHT implementation to be compliant with vector 1 - Added self to default owners in CODEOWNERS - Opportunistic QoL update: excluded generated files from search results - Opportunistic QoL update:: allow one click debugging for DID project
1 parent ac1e6f1 commit 857d160

File tree

9 files changed

+115
-24
lines changed

9 files changed

+115
-24
lines changed

.changeset/two-moons-agree.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@web5/dids": patch
3+
---
4+
5+
DID:DHT - Only have <ID>. suffix for Root and Gateway Record names

CODEOWNERS

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# The format is described: https://github.blog/2017-07-06-introducing-code-owners/
77

88
# These owners will be the default owners for everything in the repo.
9-
* @frankhinek @csuwildcat @mistermoe
9+
* @frankhinek @csuwildcat @mistermoe @thehenrytsai
1010

1111
# These are owners of any file in the `common`, `crypto`, `crypto-aws-kms`, `dids`, and
1212
# `credentials` packages and their sub-directories.

packages/dids/.vscode/launch.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"type": "node",
99
"request": "launch",
1010
"name": "test:node",
11-
"runtimeExecutable": "${workspaceFolder:dids}/node_modules/.bin/mocha",
11+
"runtimeExecutable": "${workspaceFolder:dids}/../../node_modules/.bin/mocha",
1212
"console": "internalConsole",
1313
"preLaunchTask": "build tests",
1414
"skipFiles": [

packages/dids/src/methods/did-dht.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -1255,16 +1255,11 @@ export class DidDhtDocument {
12551255
// Add a DNS TXT record for the root record.
12561256
dnsAnswerRecords.push({
12571257
type : 'TXT',
1258-
name : '_did.',
1258+
name : '_did.' + DidDhtDocument.getUniqueDidSuffix(didDocument.id) + '.', // name of a Root Record MUST end in `<ID>.`
12591259
ttl : DNS_RECORD_TTL,
12601260
data : rootRecord.join(PROPERTY_SEPARATOR)
12611261
});
12621262

1263-
// Per the DID DHT specification, the method-specific identifier must be appended as the
1264-
// Origin of all records.
1265-
const [, , identifier] = didDocument.id.split(':');
1266-
dnsAnswerRecords.forEach(record => record.name += identifier);
1267-
12681263
// Create a DNS response packet with the authoritative answer flag set.
12691264
const dnsPacket: Packet = {
12701265
id : 0,
@@ -1275,6 +1270,16 @@ export class DidDhtDocument {
12751270

12761271
return dnsPacket;
12771272
}
1273+
1274+
/**
1275+
* Gets the unique portion of the DID identifier after the last `:` character.
1276+
* e.g. `did:dht:example` -> `example`
1277+
*
1278+
* @param did - The DID to extract the unique suffix from.
1279+
*/
1280+
private static getUniqueDidSuffix(did: string ): string {
1281+
return did.split(':')[2];
1282+
}
12781283
}
12791284

12801285
/**

packages/dids/src/types/did-core.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -563,18 +563,18 @@ export enum DidVerificationRelationship {
563563
keyAgreement = 'keyAgreement',
564564

565565
/**
566-
* Specifies a mechanism used by the DID subject to delegate a cryptographic capability to another
567-
* party. This can include delegating access to a specific resource or API.
566+
* Specifies a verification method used by the DID subject to invoke a cryptographic capability.
567+
* This is frequently associated with authorization actions, like updating the DID Document.
568568
*
569-
* @see {@link https://www.w3.org/TR/did-core/#capability-delegation | DID Core Specification, § Capability Delegation}
569+
* @see {@link https://www.w3.org/TR/did-core/#capability-invocation | DID Core Specification, § Capability Invocation}
570570
*/
571-
capabilityDelegation = 'capabilityDelegation',
571+
capabilityInvocation = 'capabilityInvocation',
572572

573573
/**
574-
* Specifies a verification method used by the DID subject to invoke a cryptographic capability.
575-
* This is frequently associated with authorization actions, like updating the DID Document.
574+
* Specifies a mechanism used by the DID subject to delegate a cryptographic capability to another
575+
* party. This can include delegating access to a specific resource or API.
576576
*
577-
* @see {@link https://www.w3.org/TR/did-core/#capability-invocation | DID Core Specification, § Capability Invocation}
577+
* @see {@link https://www.w3.org/TR/did-core/#capability-delegation | DID Core Specification, § Capability Delegation}
578578
*/
579-
capabilityInvocation = 'capabilityInvocation'
579+
capabilityDelegation = 'capabilityDelegation',
580580
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo",
3+
"verificationMethod": [
4+
{
5+
"id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0",
6+
"type": "JsonWebKey",
7+
"controller": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo",
8+
"publicKeyJwk": {
9+
"kid": "0",
10+
"alg": "Ed25519",
11+
"crv": "Ed25519",
12+
"kty": "OKP",
13+
"x": "YCcHYL2sYNPDlKaALcEmll2HHyT968M4UWbr-9CFGWE"
14+
}
15+
}
16+
],
17+
"authentication": [
18+
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
19+
],
20+
"assertionMethod": [
21+
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
22+
],
23+
"capabilityInvocation": [
24+
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
25+
],
26+
"capabilityDelegation": [
27+
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
28+
]
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"name": "_did.cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo.",
4+
"type": "TXT",
5+
"ttl": 7200,
6+
"rdata": "v=0;vm=k0;auth=k0;asm=k0;inv=k0;del=k0"
7+
},
8+
{
9+
"name": "_k0._did.",
10+
"type": "TXT",
11+
"ttl": 7200,
12+
"rdata": "id=0;t=0;k=YCcHYL2sYNPDlKaALcEmll2HHyT968M4UWbr-9CFGWE"
13+
}
14+
]

packages/dids/tests/methods/did-dht.spec.ts

+39-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
import type { PortableDid } from '../../src/types/portable-did.js';
2+
13
import sinon from 'sinon';
4+
import resolveTestVectors from '../../../../web5-spec/test-vectors/did_dht/resolve.json' assert { type: 'json' };
5+
import officialTestVector1DidDocument from '../fixtures/test-vectors/did-dht/vector-1-did-document.json' assert { type: 'json' };
6+
import officialTestVector1DnsRecords from '../fixtures/test-vectors/did-dht/vector-1-dns-records.json' assert { type: 'json' };
7+
28
import { expect } from 'chai';
39
import { Convert } from '@web5/common';
4-
5-
import type { PortableDid } from '../../src/types/portable-did.js';
6-
10+
import { DidDocument } from '../../src/index.js';
711
import { DidErrorCode } from '../../src/did-error.js';
812
import { DidDht, DidDhtDocument, DidDhtRegisteredDidType } from '../../src/methods/did-dht.js';
913

10-
import DidDhtResolveTestVector from '../../../../web5-spec/test-vectors/did_dht/resolve.json' assert { type: 'json' };
11-
1214
// Helper function to create a mocked fetch response that fails and returns a 404 Not Found.
1315
const fetchNotFoundResponse = () => ({
1416
status : 404,
@@ -431,10 +433,15 @@ describe('DidDht', () => {
431433

432434
it('throws an error if the resulting DID document would exceed the 1000 byte maximum', async () => {
433435
try {
434-
// Attempt to create a DID with 6 verification methods (Identity Key plus 5 additional).
436+
// Attempt to create a DID with DID Document that exceeds the maximum size.
435437
await DidDht.create({
436438
options: {
437439
verificationMethods: [
440+
{ algorithm: 'Ed25519' },
441+
{ algorithm: 'Ed25519' },
442+
{ algorithm: 'Ed25519' },
443+
{ algorithm: 'Ed25519' },
444+
{ algorithm: 'Ed25519' },
438445
{ algorithm: 'Ed25519' },
439446
{ algorithm: 'Ed25519' },
440447
{ algorithm: 'Ed25519' },
@@ -1153,10 +1160,35 @@ describe('DidDhtDocument', () => {
11531160

11541161
describe('Web5TestVectorsDidDht', () => {
11551162
it('resolve', async () => {
1156-
for (const vector of DidDhtResolveTestVector.vectors as any[]) {
1163+
for (const vector of resolveTestVectors.vectors as any[]) {
11571164
const didResolutionResult = await DidDht.resolve(vector.input.didUri);
11581165
expect(didResolutionResult.didResolutionMetadata.error).to.equal(vector.output.didResolutionMetadata.error);
11591166
}
11601167
}).timeout(30000); // Set timeout to 30 seconds for this test for did:dht resolution timeout test
11611168
});
11621169
});
1170+
1171+
// vectors come from https://did-dht.com/#test-vectors
1172+
describe('Official DID:DHT Vector tests', () => {
1173+
it('vector 1', async () => {
1174+
const dnsPacket = await DidDhtDocument.toDnsPacket({
1175+
didDocument : officialTestVector1DidDocument as DidDocument,
1176+
didMetadata : { published: false }
1177+
});
1178+
1179+
expect(dnsPacket.answers).to.have.length(officialTestVector1DnsRecords.length);
1180+
1181+
// NOTE: the DNS library we use uses name `data` instead of `rdata` used in DID:DHT spec,
1182+
// but prefer to keep the naming in test vector files identical to that of the DID:DHT spec,
1183+
// hence this additional normalization step
1184+
const normalizedConstructedRecords = dnsPacket.answers!.map(record => {
1185+
const { data: rdata, ...otherProperties } = record;
1186+
return {
1187+
...otherProperties,
1188+
rdata
1189+
};
1190+
});
1191+
1192+
expect(normalizedConstructedRecords).to.deep.include.members(officialTestVector1DnsRecords);
1193+
});
1194+
});

web5-js.code-workspace

+7-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,13 @@
6666
"editor.codeActionsOnSave": {
6767
"source.fixAll": "always"
6868
},
69-
"typescript.tsdk": "root/node_modules/typescript/lib"
69+
"typescript.tsdk": "root/node_modules/typescript/lib",
70+
"search.exclude": {
71+
"**/dist/**": true,
72+
"**/coverage/**": true,
73+
"**/compiled/**": true
74+
75+
}
7076
},
7177
"launch": {
7278
"version": "0.2.0",

0 commit comments

Comments
 (0)