Skip to content

Commit

Permalink
Updated DID:DHT implementation to be compliant with vector 1 (#504)
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
thehenrytsai authored May 2, 2024
1 parent ac1e6f1 commit 857d160
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-moons-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@web5/dids": patch
---

DID:DHT - Only have <ID>. suffix for Root and Gateway Record names
2 changes: 1 addition & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# The format is described: https://github.blog/2017-07-06-introducing-code-owners/

# These owners will be the default owners for everything in the repo.
* @frankhinek @csuwildcat @mistermoe
* @frankhinek @csuwildcat @mistermoe @thehenrytsai

# These are owners of any file in the `common`, `crypto`, `crypto-aws-kms`, `dids`, and
# `credentials` packages and their sub-directories.
Expand Down
2 changes: 1 addition & 1 deletion packages/dids/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"type": "node",
"request": "launch",
"name": "test:node",
"runtimeExecutable": "${workspaceFolder:dids}/node_modules/.bin/mocha",
"runtimeExecutable": "${workspaceFolder:dids}/../../node_modules/.bin/mocha",
"console": "internalConsole",
"preLaunchTask": "build tests",
"skipFiles": [
Expand Down
17 changes: 11 additions & 6 deletions packages/dids/src/methods/did-dht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1255,16 +1255,11 @@ export class DidDhtDocument {
// Add a DNS TXT record for the root record.
dnsAnswerRecords.push({
type : 'TXT',
name : '_did.',
name : '_did.' + DidDhtDocument.getUniqueDidSuffix(didDocument.id) + '.', // name of a Root Record MUST end in `<ID>.`
ttl : DNS_RECORD_TTL,
data : rootRecord.join(PROPERTY_SEPARATOR)
});

// Per the DID DHT specification, the method-specific identifier must be appended as the
// Origin of all records.
const [, , identifier] = didDocument.id.split(':');
dnsAnswerRecords.forEach(record => record.name += identifier);

// Create a DNS response packet with the authoritative answer flag set.
const dnsPacket: Packet = {
id : 0,
Expand All @@ -1275,6 +1270,16 @@ export class DidDhtDocument {

return dnsPacket;
}

/**
* Gets the unique portion of the DID identifier after the last `:` character.
* e.g. `did:dht:example` -> `example`
*
* @param did - The DID to extract the unique suffix from.
*/
private static getUniqueDidSuffix(did: string ): string {
return did.split(':')[2];
}
}

/**
Expand Down
16 changes: 8 additions & 8 deletions packages/dids/src/types/did-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,18 +563,18 @@ export enum DidVerificationRelationship {
keyAgreement = 'keyAgreement',

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

/**
* Specifies a verification method used by the DID subject to invoke a cryptographic capability.
* This is frequently associated with authorization actions, like updating the DID Document.
* Specifies a mechanism used by the DID subject to delegate a cryptographic capability to another
* party. This can include delegating access to a specific resource or API.
*
* @see {@link https://www.w3.org/TR/did-core/#capability-invocation | DID Core Specification, § Capability Invocation}
* @see {@link https://www.w3.org/TR/did-core/#capability-delegation | DID Core Specification, § Capability Delegation}
*/
capabilityInvocation = 'capabilityInvocation'
capabilityDelegation = 'capabilityDelegation',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo",
"verificationMethod": [
{
"id": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0",
"type": "JsonWebKey",
"controller": "did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo",
"publicKeyJwk": {
"kid": "0",
"alg": "Ed25519",
"crv": "Ed25519",
"kty": "OKP",
"x": "YCcHYL2sYNPDlKaALcEmll2HHyT968M4UWbr-9CFGWE"
}
}
],
"authentication": [
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
],
"assertionMethod": [
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
],
"capabilityInvocation": [
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
],
"capabilityDelegation": [
"did:dht:cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo#0"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"name": "_did.cyuoqaf7itop8ohww4yn5ojg13qaq83r9zihgqntc5i9zwrfdfoo.",
"type": "TXT",
"ttl": 7200,
"rdata": "v=0;vm=k0;auth=k0;asm=k0;inv=k0;del=k0"
},
{
"name": "_k0._did.",
"type": "TXT",
"ttl": 7200,
"rdata": "id=0;t=0;k=YCcHYL2sYNPDlKaALcEmll2HHyT968M4UWbr-9CFGWE"
}
]
46 changes: 39 additions & 7 deletions packages/dids/tests/methods/did-dht.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type { PortableDid } from '../../src/types/portable-did.js';

import sinon from 'sinon';
import resolveTestVectors from '../../../../web5-spec/test-vectors/did_dht/resolve.json' assert { type: 'json' };
import officialTestVector1DidDocument from '../fixtures/test-vectors/did-dht/vector-1-did-document.json' assert { type: 'json' };
import officialTestVector1DnsRecords from '../fixtures/test-vectors/did-dht/vector-1-dns-records.json' assert { type: 'json' };

import { expect } from 'chai';
import { Convert } from '@web5/common';

import type { PortableDid } from '../../src/types/portable-did.js';

import { DidDocument } from '../../src/index.js';
import { DidErrorCode } from '../../src/did-error.js';
import { DidDht, DidDhtDocument, DidDhtRegisteredDidType } from '../../src/methods/did-dht.js';

import DidDhtResolveTestVector from '../../../../web5-spec/test-vectors/did_dht/resolve.json' assert { type: 'json' };

// Helper function to create a mocked fetch response that fails and returns a 404 Not Found.
const fetchNotFoundResponse = () => ({
status : 404,
Expand Down Expand Up @@ -431,10 +433,15 @@ describe('DidDht', () => {

it('throws an error if the resulting DID document would exceed the 1000 byte maximum', async () => {
try {
// Attempt to create a DID with 6 verification methods (Identity Key plus 5 additional).
// Attempt to create a DID with DID Document that exceeds the maximum size.
await DidDht.create({
options: {
verificationMethods: [
{ algorithm: 'Ed25519' },
{ algorithm: 'Ed25519' },
{ algorithm: 'Ed25519' },
{ algorithm: 'Ed25519' },
{ algorithm: 'Ed25519' },
{ algorithm: 'Ed25519' },
{ algorithm: 'Ed25519' },
{ algorithm: 'Ed25519' },
Expand Down Expand Up @@ -1153,10 +1160,35 @@ describe('DidDhtDocument', () => {

describe('Web5TestVectorsDidDht', () => {
it('resolve', async () => {
for (const vector of DidDhtResolveTestVector.vectors as any[]) {
for (const vector of resolveTestVectors.vectors as any[]) {
const didResolutionResult = await DidDht.resolve(vector.input.didUri);
expect(didResolutionResult.didResolutionMetadata.error).to.equal(vector.output.didResolutionMetadata.error);
}
}).timeout(30000); // Set timeout to 30 seconds for this test for did:dht resolution timeout test
});
});

// vectors come from https://did-dht.com/#test-vectors
describe('Official DID:DHT Vector tests', () => {
it('vector 1', async () => {
const dnsPacket = await DidDhtDocument.toDnsPacket({
didDocument : officialTestVector1DidDocument as DidDocument,
didMetadata : { published: false }
});

expect(dnsPacket.answers).to.have.length(officialTestVector1DnsRecords.length);

// NOTE: the DNS library we use uses name `data` instead of `rdata` used in DID:DHT spec,
// but prefer to keep the naming in test vector files identical to that of the DID:DHT spec,
// hence this additional normalization step
const normalizedConstructedRecords = dnsPacket.answers!.map(record => {
const { data: rdata, ...otherProperties } = record;
return {
...otherProperties,
rdata
};
});

expect(normalizedConstructedRecords).to.deep.include.members(officialTestVector1DnsRecords);
});
});
8 changes: 7 additions & 1 deletion web5-js.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@
"editor.codeActionsOnSave": {
"source.fixAll": "always"
},
"typescript.tsdk": "root/node_modules/typescript/lib"
"typescript.tsdk": "root/node_modules/typescript/lib",
"search.exclude": {
"**/dist/**": true,
"**/coverage/**": true,
"**/compiled/**": true

}
},
"launch": {
"version": "0.2.0",
Expand Down

0 comments on commit 857d160

Please sign in to comment.