@dwn-protocol/id-sdk
Version:
SDK for accessing the features and capabilities
249 lines • 11 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DidDhtMethod = void 0;
const dht_js_1 = require("./dht.js");
const index_js_1 = require("../crypto/index.js");
const utils_js_1 = require("./utils.js");
// for base32
const z32_1 = __importDefault(require("z32"));
const SupportedCryptoKeyTypes = [
'Ed25519',
'secp256k1'
];
class DidDhtMethod {
/**
* Creates a new DID Document according to the did:dht spec.
* @param options The options to use when creating the DID Document, including whether to publish it.
* @returns A promise that resolves to a PortableDid object.
*/
static async create(options) {
const { publish = false, keySet: initialKeySet, services } = options !== null && options !== void 0 ? options : {};
// Generate missing keys, if not provided in the options.
const keySet = await this.generateKeySet({ keySet: initialKeySet });
// Get the identifier and set it.
const identityKey = keySet.verificationMethodKeys.find(key => key.publicKeyJwk.kid === '0');
const id = await this.getDidIdentifier({ key: identityKey.publicKeyJwk });
// Add all other keys to the verificationMethod and relationship arrays.
const relationshipsMap = {};
const verificationMethods = keySet.verificationMethodKeys.map(key => {
for (const relationship of key.relationships) {
if (relationshipsMap[relationship]) {
relationshipsMap[relationship].push(`#${key.publicKeyJwk.kid}`);
}
else {
relationshipsMap[relationship] = [`#${key.publicKeyJwk.kid}`];
}
}
return {
id: `${id}#${key.publicKeyJwk.kid}`,
type: 'JsonWebKey2020',
controller: id,
publicKeyJwk: key.publicKeyJwk
};
});
// Add DID identifier to the service IDs.
services === null || services === void 0 ? void 0 : services.map(service => {
service.id = `${id}#${service.id}`;
});
// Assemble the DID Document.
const document = Object.assign(Object.assign({ id, verificationMethod: [...verificationMethods] }, relationshipsMap), services && { service: services });
// If the publish flag is set, publish the DID Document to the DHT.
if (publish) {
await this.publish({ identityKey, didDocument: document });
}
return {
did: document.id,
document: document,
keySet: keySet
};
}
/**
* Generates a JWK key pair.
* @param options The key algorithm and key ID to use.
* @returns A promise that resolves to a JwkKeyPair object.
*/
static async generateJwkKeyPair(options) {
const { keyAlgorithm, keyId } = options;
let cryptoKeyPair;
switch (keyAlgorithm) {
case 'Ed25519': {
cryptoKeyPair = await new index_js_1.EdDsaAlgorithm().generateKey({
algorithm: { name: 'EdDSA', namedCurve: 'Ed25519' },
extractable: true,
keyUsages: ['sign', 'verify']
});
break;
}
case 'secp256k1': {
cryptoKeyPair = await new index_js_1.EcdsaAlgorithm().generateKey({
algorithm: { name: 'ECDSA', namedCurve: 'secp256k1' },
extractable: true,
keyUsages: ['sign', 'verify']
});
break;
}
default: {
throw new Error(`Unsupported crypto algorithm: '${keyAlgorithm}'`);
}
}
// Convert the CryptoKeyPair to JwkKeyPair.
const jwkKeyPair = await index_js_1.Jose.cryptoKeyToJwkPair({ keyPair: cryptoKeyPair });
// Set kid values.
if (keyId) {
jwkKeyPair.privateKeyJwk.kid = keyId;
jwkKeyPair.publicKeyJwk.kid = keyId;
}
else {
// If a key ID is not specified, generate RFC 7638 JWK thumbprint.
const jwkThumbprint = await index_js_1.Jose.jwkThumbprint({ key: jwkKeyPair.publicKeyJwk });
jwkKeyPair.privateKeyJwk.kid = jwkThumbprint;
jwkKeyPair.publicKeyJwk.kid = jwkThumbprint;
}
return jwkKeyPair;
}
/**
* Generates a key set for a DID Document.
* @param options The key set to use when generating the key set.
* @returns A promise that resolves to a DidDhtKeySet object.
*/
static async generateKeySet(options) {
var _a, _b;
var _c, _d;
let { keySet = {} } = options !== null && options !== void 0 ? options : {};
// If the key set is missing a `verificationMethodKeys` array, create one.
if (!keySet.verificationMethodKeys)
keySet.verificationMethodKeys = [];
// If the key set lacks an identity key (`kid: 0`), generate one.
if (!keySet.verificationMethodKeys.some(key => key.publicKeyJwk.kid === '0')) {
const identityKey = await this.generateJwkKeyPair({
keyAlgorithm: 'Ed25519',
keyId: '0'
});
keySet.verificationMethodKeys.push(Object.assign(Object.assign({}, identityKey), { relationships: ['authentication', 'assertionMethod', 'capabilityInvocation', 'capabilityDelegation'] }));
}
// Generate RFC 7638 JWK thumbprints if `kid` is missing from any key.
for (const key of keySet.verificationMethodKeys) {
if (key.publicKeyJwk)
(_a = (_c = key.publicKeyJwk).kid) !== null && _a !== void 0 ? _a : (_c.kid = await index_js_1.Jose.jwkThumbprint({ key: key.publicKeyJwk }));
if (key.privateKeyJwk)
(_b = (_d = key.privateKeyJwk).kid) !== null && _b !== void 0 ? _b : (_d.kid = await index_js_1.Jose.jwkThumbprint({ key: key.privateKeyJwk }));
}
return keySet;
}
/**
* Gets the identifier fragment from a DID.
* @param options The key to get the identifier fragment from.
* @returns A promise that resolves to a string containing the identifier.
*/
static async getDidIdentifier(options) {
const { key } = options;
const cryptoKey = await index_js_1.Jose.jwkToCryptoKey({ key });
const identifier = z32_1.default.encode(cryptoKey.material);
return 'did:dht:' + identifier;
}
/**
* Gets the identifier fragment from a DID.
* @param options The key to get the identifier fragment from.
* @returns A promise that resolves to a string containing the identifier fragment.
*/
static async getDidIdentifierFragment(options) {
const { key } = options;
const cryptoKey = await index_js_1.Jose.jwkToCryptoKey({ key });
return z32_1.default.encode(cryptoKey.material);
}
/**
* Publishes a DID Document to the DHT.
* @param keySet The key set to use to sign the DHT payload.
* @param didDocument The DID Document to publish.
* @returns A boolean indicating the success of the publishing operation.
*/
static async publish({ didDocument, identityKey }) {
const publicCryptoKey = await index_js_1.Jose.jwkToCryptoKey({ key: identityKey.publicKeyJwk });
const privateCryptoKey = await index_js_1.Jose.jwkToCryptoKey({ key: identityKey.privateKeyJwk });
const isPublished = await dht_js_1.DidDht.publishDidDocument({
keyPair: {
publicKey: publicCryptoKey,
privateKey: privateCryptoKey
},
didDocument
});
return isPublished;
}
/**
* Resolves a DID Document based on the specified options.
*
* @param options - Configuration for resolving a DID Document.
* @param options.didUrl - The DID URL to resolve.
* @param options.resolutionOptions - Optional settings for the DID resolution process as defined in the DID Core specification.
* @returns A Promise that resolves to a `DidResolutionResult`, containing the resolved DID Document and associated metadata.
*/
static async resolve(options) {
const { didUrl, resolutionOptions: _ } = options;
// TODO: Implement resolutionOptions as defined in https://www.w3.org/TR/did-core/#did-resolution
const parsedDid = (0, utils_js_1.parseDid)({ didUrl });
if (!parsedDid) {
return {
'@context': 'https://w3id.org/did-resolution/v1',
didDocument: null,
didDocumentMetadata: {},
didResolutionMetadata: {
contentType: 'application/did+json',
error: 'invalidDid',
errorMessage: `Cannot parse DID: ${didUrl}`
}
};
}
if (parsedDid.method !== 'dht') {
return {
'@context': 'https://w3id.org/did-resolution/v1',
didDocument: null,
didDocumentMetadata: {},
didResolutionMetadata: {
contentType: 'application/did+json',
error: 'methodNotSupported',
errorMessage: `Method not supported: ${parsedDid.method}`
}
};
}
let didDocument;
/**
* As of 5 Dec 2023, the `pkarr` library throws an error if the DID is not found. Until a
* better solution is found, catch the error and return a DID Resolution Result with an
* error message.
*/
try {
didDocument = await dht_js_1.DidDht.getDidDocument({ did: parsedDid.did });
}
catch (error) {
return {
'@context': 'https://w3id.org/did-resolution/v1',
didDocument: null,
didDocumentMetadata: {},
didResolutionMetadata: {
contentType: 'application/did+json',
error: 'internalError',
errorMessage: `An unexpected error occurred while resolving DID: ${parsedDid.did}`
}
};
}
return {
'@context': 'https://w3id.org/did-resolution/v1',
didDocument,
didDocumentMetadata: {},
didResolutionMetadata: {
contentType: 'application/did+json',
did: {
didString: parsedDid.did,
methodSpecificId: parsedDid.id,
method: parsedDid.method
}
}
};
}
}
exports.DidDhtMethod = DidDhtMethod;
DidDhtMethod.methodName = 'dht';
//# sourceMappingURL=did-dht.js.map