Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@digitalbazaar/did-method-key

digitalbazaar19.9kBSD-3-Clause5.2.0

A did:key method resolver.

Decentralized, Identifier, DID

readme

did:key method driver (@digitalbazaar/did-method-key)

Node.js CI Coverage status NPM Version

A DID (Decentralized Identifier) method driver for the did-io library and for standalone use

Table of Contents

Background

See also (related specs):

A did:key method driver for the did-io client library and for standalone use.

The did:key method is used to express public keys in a way that doesn't require a DID Registry of any kind. Its general format is:

did:key:<multibase encoded, multicodec identified, public key>

So, for example, the following DID would be derived from a base-58 encoded ed25519 public key:

did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH

That DID would correspond to the following DID Document:

Example DID Document

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1",
    "https://w3id.org/security/suites/x25519-2020/v1"
  ],
  "id": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
  "verificationMethod": [
    {
      "id": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
      "type": "Ed25519VerificationKey2020",
      "controller": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
      "publicKeyMultibase": "z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
    }
  ],
  "authentication": [
    "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
  ],
  "assertionMethod": [
    "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
  ],
  "capabilityDelegation": [
    "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
  ],
  "capabilityInvocation": [
    "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
  ],
  "keyAgreement": [
    {
      "id": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc",
      "type": "X25519KeyAgreementKey2020",
      "controller": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
      "publicKeyMultibase": "z6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc"
    }
  ]
}

Security

The keyAgreement key is a Curve25519 public key (suitable for Diffie-Hellman key exchange) that is deterministically derived from the source Ed25519 key, using ed2curve-js.

Note that this derived key is optional -- there's at least one proof that this is safe to do.

Install

Requires Node.js 16+

To install from npm:

npm install --save @digitalbazaar/did-method-key

To install locally (for development):

git clone https://github.com/digitalbazaar/did-method-key.git
cd did-method-key
npm install

Usage

use()

This method registers a multibase-multikey header and a multibase-multikey deserializer and configures a driver to use a multibase-multikey deserializer to handle data using that multibase-multikey header.

import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey';
import {driver} from '@digitalbazaar/did-method-key';

const didKeyDriverMultikey = driver();

didKeyDriverMultikey.use({
  multibaseMultikeyHeader: 'zDna',
  fromMultibase: EcdsaMultikey.from
});

createFromMultibase()

This utility function can be used to adapt legacy verification suites such as Ed25519VerificationSuite2018 to work properly with fromMultibase() calls in DidKeyDriver.

import {driver} from '@digitalbazaar/did-method-key';
import {Ed25519VerificationKey2018} from
  '@digitalbazaar/ed25519-verification-key-2018';

const didKeyDriver2018 = driver();

didKeyDriver2018.use({
  multibaseMultikeyHeader: header,
  fromMultibase: createFromMultibase(Ed25519VerificationKey2018)
});

fromKeyPair()

To generate a new key and get its corresponding did:key method DID Document from a verification keypair.

import {driver} from '@digitalbazaar/did-method-key';
import {Ed25519VerificationKey2020} from
  '@digitalbazaar/ed25519-verification-key-2020';

const didKeyDriver = driver();

didKeyDriver.use({
  multibaseMultikeyHeader: 'z6Mk',
  fromMultibase: Ed25519VerificationKey2020.from
});

const publicKeyMultibase = 'z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH';
const verificationKeyPair = await Ed25519VerificationKey2020.from({
  publicKeyMultibase
});
// or perhaps:
// const verificationKeyPair = await Ed25519VerificationKey2020.generate();

const {didDocument, keyPairs, methodFor} = await didKeyDriver.fromKeyPair({
  verificationKeyPair
});

// print the DID Document above
console.log(JSON.stringify(didDocument, null, 2));

// keyPairs will be set like so =>
Map(2) {
  'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T#z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T' => Ed25519VerificationKey2020 {
    id: 'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T#z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T',
    controller: 'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T',
    revoked: undefined,
    type: 'Ed25519VerificationKey2020',
    publicKeyMultibase: 'z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T',
    privateKeyMultibase: undefined
  },
  'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T#z6LSotGbgPCJD2Y6TSvvgxERLTfVZxCh9KSrez3WNrNp7vKW' => X25519KeyAgreementKey2020 {
    id: 'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T#z6LSotGbgPCJD2Y6TSvvgxERLTfVZxCh9KSrez3WNrNp7vKW',
    controller: 'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T',
    revoked: undefined,
    type: 'X25519KeyAgreementKey2020',
    publicKeyMultibase: 'z6LSotGbgPCJD2Y6TSvvgxERLTfVZxCh9KSrez3WNrNp7vKW',
    privateKeyMultibase: undefined
  }
}

methodFor is a convenience function that returns a key pair instance that contains publicKeyMultibase for given purpose. For example, a verification key (containing a signer() and verifier() functions) are frequently useful for jsonld-signatures or vc-js operations. After generating a new did:key DID, you can do:

// For signing Verifiable Credentials
const assertionKeyPair = methodFor({purpose: 'assertionMethod'});
// For Authorization Capabilities (zCaps)
const invocationKeyPair = methodFor({purpose: 'capabilityInvocation'});
// For Encryption using `@digitalbazaar/minimal-cipher`
const keyAgreementPair = methodFor({purpose: 'keyAgreement'});

Note that methodFor returns a key pair that contains a publicKeyMultibase. This makes it useful for verifying and encrypting operations.

publicKeyToDidDoc()

If you already have an Ed25519VerificationKey2020 public key object (as an LDKeyPair instance, or a plain key description object), you can turn it into a DID Document:

const {didDocument} = await didKeyDriver.publicKeyToDidDoc({publicKeyDescription});

get()

Getting a full DID Document from a did:key DID

To get a DID Document for an existing did:key DID:

const did = 'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T';
const didDocument = await didKeyDriver.get({did});

(Results in the example DID Doc above).

Getting the DID Document from key id

You can also use a .get() to retrieve an individual key, if you know its id already (this is useful for constructing documentLoaders for JSON-LD Signature libs, and the resulting key does include the appropriate @context).

const verificationKeyId = 'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T#z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T';

const keyAgreementKeyId = 'did:key:z6MknCCLeeHBUaHu4aHSVLDCYQW9gjVJ7a63FpMvtuVMy53T#z6LSotGbgPCJD2Y6TSvvgxERLTfVZxCh9KSrez3WNrNp7vKW';
const didDocument = await didKeyDriver.get({url: verificationKeyId});
// OR
const didDocument = await didKeyDriver.get({url: keyAgreementKeyId});

// DID Document ->
console.log(JSON.stringify(didDocument, null, 2));

publicMethodFor()

Often, you have just a did:key DID, and you need to get a key for a particular purpose from it, such as an assertionMethod key to verify a VC signature, or a keyAgreement key to encrypt a document for that DID's controller.

For that purpose, you can use a combination of get() and publicMethodFor:

// Start with the DID
const didDocument = await didKeyDriver.get({did});
// This lets you use `publicMethodFor()` to get a key for a specific purpose
const keyAgreementMethod = didKeyDriver.publicMethodFor({
  didDocument, purpose: 'keyAgreement'
});
const assertionMethod = didKeyDriver.publicMethodFor({
  didDocument, purpose: 'assertionMethod'
});

// If you have a known key type, for example, `Ed25519VerificationKey2020`,
// you can create key instances which allow you to get access to a
// `verify()` function.
const assertionMethodPublicKey = await Ed25519VerificationKey2020.from(
  assertionMethod);
const {verify} = assertionMethodPublicKey.verifier();

publicMethodFor will throw an error if no key is found for a given purpose.

Contribute

See the contribute file!

PRs accepted.

If editing the Readme, please conform to the standard-readme specification.

Commercial Support

Commercial support for this library is available upon request from Digital Bazaar: support@digitalbazaar.com

License

New BSD License (3-clause) © Digital Bazaar

changelog

did:key driver ChangeLog

5.2.0 - 2024-01-17

Added

  • Allow fromKeyPair to be called with only a key agreement key pair.

5.1.0 - 2023-04-27

Changed

  • Update get() and publicKeyToDidDoc() to support keyAgreement-only DIDs.

5.0.0 - 2023-04-11

Added

  • Add use() method that allows multibase-multikey headers and a multibase-multikey deserializer (which replaces verificationSuite previously) to handle data using that header.
  • Add fromKeyPair() that generates the DID Document along with the corresponding key pairs from a verificationKeyPair. fromKeyPair() also optionally takes a keyAgreementKeypair param.

Changed

  • BREAKING Renamed createVerificationSuite() to createFromMultibase() which now no longer takes a generate param and is adapted to convert the legacy verification suites to provide proper .fromMultibase() method.

Removed

  • BREAKING: DidKeyDriver no longer takes a verificationSuite param in the constructor.
  • BREAKING: generate() method has now been replaced by a new method fromKeyPair(). This approach externalizes key pair generation to allow whatever parameters are necessary / possible (such as random bytes / seeds) to be provided in the key-pair-specific implementation. This also greatly reduces the complexity of this library and the need to import key pair libraries generally.

4.0.0 - 2023-04-03

Added

  • Added ECDSA Multikey support. Exports createVerificationSuite() utility function that can be used to create a verificationSuite from a multikey library such as @digitalbazaar/ecdsa-multikey when creating a DidKeyDriver instance.

Changed

  • BREAKING: Remove support for node <= 14.

3.0.0 - 2022-06-02

Changed

  • BREAKING: Convert to module (ESM).
  • BREAKING: Require Node.js >=14.
  • Update dependencies.
  • Lint module.

2.0.0 - 2021-06-19

Changed

  • BREAKING: Update 2020 cryptosuites to use multicodec encoding for keys.

1.2.0 - 2021-05-26

Added

  • Add backwards compatibility, allow returning the did:key document using the Ed25519VerificationKey2018 and X25519KeyAgreementKey2019 suites.

1.1.0 - 2021-05-04

Added

  • Add didKeyDriver.publicKeyToDidDoc({keyPair}) method. (This used to be the keyToDidDoc() method, in <= v0.7.0, removed in v1.0 and brought back by popular demand.)

1.0.0 - 2021-04-09

Changed

  • BREAKING: Rename npm package from did-method-key to @digitalbazaar/did-method-key.
  • BREAKING: Return {didDocument, keyPairs, methodFor} from generate().
  • BREAKING: Upgrade to crypto-ld v5.0 based key suites, update to use Ed25519VerificationKey2020 and X25519KeyAgreementKey2020 crypto suites.
  • BREAKING: DID Document context changed from 'https://w3id.org/did/v0.11' to the DID WG-published https://www.w3.org/ns/did/v1, plus the contexts for the Ed25519VerificationKey2020 and X25519KeyAgreementKey2020 crypto suites. See the "Example DID Document" section of the README.
  • BREAKING: Rename computeKeyId() -> computeId().
  • Avoid mutation of ed25519 key passed into keyToDidDoc.
  • Use underscores for utility functions.
  • Add methodFor and publicMethodFor convenience functions.
  • BREAKING: Move the lru-cache to did-io's CachedResolver class.
  • BREAKING: keyToDidDoc driver method removed. (See Upgrading notes for alternatives.)
  • BREAKING: The publicKey property of the DID Document has been deprecated by the DID Data Model, and is now renamed to verificationMethod.

Upgrading from <= v.0.7.0

1) Check for the changed generate() return signature. The usage is now:

const {didDocument, keyPairs, methodFor} = await didKeyDriver.generate();

Note that keyPairs is a js Map instance containing the public/private key pairs for both the signing key and the X25519 key agreement key.

And the methodFor convenience function allows you to fetch a particular public/private key pair for a given purpose. For example:

const {didDocument, keyPairs, methodFor} = await didKeyDriver.generate();
const authenticationKeyPair = methodFor({purpose: 'authentication'});
const keyAgreementKeyPair = methodFor({purpose: 'keyAgreement'});

2) Make sure to adjust your documentLoader to handle the new contexts.

3) The keyToDidDoc function has been renamed to publicKeyToDidDoc() (as of v1.1), and the return signature has changed.

// For example, if you have a key description object (such as that returned by
// a KMS system's "generate key" operation):
const publicKeyDescription = {
  "@context": "https://w3id.org/security/suites/ed25519-2020/v1",
  "id": "did:key:z6MkuBLrjSGt1PPADAvuv6rmvj4FfSAfffJotC6K8ZEorYmv#z6MkuBLrjSGt1PPADAvuv6rmvj4FfSAfffJotC6K8ZEorYmv",
  "type": "Ed25519VerificationKey2020",
  "controller": "did:key:z6MkuBLrjSGt1PPADAvuv6rmvj4FfSAfffJotC6K8ZEorYmv",
  "publicKeyMultibase": "zFj5p9C2Sfqth6g6DEXtw5dWFqrtpFn4TCBBPJHGnwKzY"
};
const {didDocument} = await didKeyDriver.publicKeyToDidDoc({publicKeyDescription});

// Or, you can start with an `LDKeyPair` instance:
const keyPair = await Ed25529VerificationKey2020.generate();
const {didDocument} = await didKeyDriver.publicKeyToDidDoc({publicKeyDescription: keyPair});

Don't forget that you can use the didKeyDriver.publicMethodFor({purpose}) method to fetch a particular key, after creating the DID Document.

const keyAgreementKey = didKeyDriver.publicMethodFor({didDocument, purpose: 'keyAgreement'});
// Note that the resulting keyAgreementKey pair will only have the public key material, not private

0.7.0 - 2020-09-23

Added

  • Add cache with option to configure its max size.

Changed

  • BREAKING: Make keyToDidDoc asynchronous.

0.6.1 - 2020-04-20

Changed

  • Return public/private key pair from generate(), available on didDoc.keys.

0.6.0 - 2020-04-13

Changed

  • BREAKING: Use x25519-key-pair v2.0.0, changed fingerprint format for X25519 keys.
  • Use crypto-ld v0.3.7.

Added

  • Add computeKeyId() and method to driver, to work with did-io downstream.

0.5.1 - 2020-02-27

Changed

  • Use x25519-key-pair@1.

0.5.0 - 2020-02-24

Added

  • driver.get() can now also resolve individual keys.

Changed

  • BREAKING: Undo previous change, using https://w3id.org/did/v0.11 as @context, apologies for the confusion.

0.4.0 - 2020-01-29

Changed

  • BREAKING: Now using 'https://www.w3.org/ns/did/v1' as context.

0.3.0 - 2020-01-08

Changed

  • BREAKING: Fix - Use fingerprint hash fragment as key id.

0.2.0 - 2019-08-22

Added

  • Add core files.

  • See git history for changes previous to this release.