crypto-ld
Version:
A Javascript library for generating and performing common operations on Linked Data cryptographic key pairs.
225 lines (212 loc) • 7.64 kB
JavaScript
/*!
* Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
*/
/**
* When adding support for a new suite type for `crypto-ld`, developers should
* do the following:
*
* 1. Create their own npm package / github repo, such as `example-key-pair`.
* 2. Subclass LDKeyPair.
* 3. Override relevant methods (such as `export()` and `fingerprint()`).
* 4. Add to the key type table in the `crypto-ld` README.md (that's this repo).
*/
export class LDKeyPair {
/* eslint-disable jsdoc/require-description-complete-sentence */
/**
* Creates a public/private key pair instance. This is an abstract base class,
* actual key material and suite-specific methods are handled in the subclass.
*
* To generate or import a key pair, use the `cryptoLd` instance.
*
* @see CryptoLD.js
*
* @param {object} options - The options to use.
* @param {string} options.id - The key id, typically composed of controller
* URL and key fingerprint as hash fragment.
* @param {string} options.controller - DID/URL of the person/entity
* controlling this key.
* @param {string} [options.revoked] - Timestamp of when the key has been
* revoked, in RFC3339 format. If not present, the key itself is
* considered not revoked. (Note that this mechanism is slightly different
* than DID Document key revocation, where a DID controller can revoke a
* key from that DID by removing it from the DID Document.)
*/
/* eslint-enable */
constructor({id, controller, revoked} = {}) {
this.id = id;
this.controller = controller;
this.revoked = revoked;
// this.type is set in subclass constructor
}
/* eslint-disable jsdoc/check-param-names */
/**
* Generates a new public/private key pair instance.
* Note that this method is not typically called directly by client code,
* but instead is used through a `cryptoLd` instance.
*
* @param {object} options - Suite-specific options for the KeyPair. For
* common options, see the `LDKeyPair.constructor()` docstring.
*
* @returns {Promise<LDKeyPair>} An LDKeyPair instance.
*/
/* eslint-enable */
static async generate(/* options */) {
throw new Error('Abstract method, must be implemented in subclass.');
}
/**
* Imports a key pair instance from a provided externally fetched key
* document (fetched via a secure JSON-LD `documentLoader` or via
* `cryptoLd.fromKeyId()`), optionally checking it for revocation and required
* context.
*
* @param {object} options - Options hashmap.
* @param {string} options.document - Externally fetched key document.
* @param {boolean} [options.checkContext=true] - Whether to check that the
* fetched key document contains the context required by the key's crypto
* suite.
* @param {boolean} [options.checkRevoked=true] - Whether to check the key
* object for the presence of the `revoked` timestamp.
*
* @returns {Promise<LDKeyPair>} Resolves with the resulting key pair
* instance.
*/
static async fromKeyDocument({
document, checkContext = true, checkRevoked = true
} = {}) {
if(!document) {
throw new TypeError('The "document" parameter is required.');
}
if(checkContext) {
const fetchedDocContexts = [].concat(document['@context']);
if(!fetchedDocContexts.includes(this.SUITE_CONTEXT)) {
throw new Error('Key document does not contain required context "' +
this.SUITE_CONTEXT + '".');
}
}
if(checkRevoked && document.revoked) {
throw new Error(`Key has been revoked since: "${document.revoked}".`);
}
return this.from(document);
}
/* eslint-disable jsdoc/check-param-names */
/**
* Generates a KeyPair from some options.
*
* @param {object} options - Will generate a key pair in multiple different
* formats.
* @example
* > const options = {
* type: 'Ed25519VerificationKey2020'
* };
* > const edKeyPair = await LDKeyPair.from(options);
*
* @returns {Promise<LDKeyPair>} A LDKeyPair.
* @throws Unsupported Key Type.
*/
/* eslint-enable */
static async from(/* options */) {
throw new Error('Abstract method from() must be implemented in subclass.');
}
/**
* Exports the serialized representation of the KeyPair
* and other information that json-ld Signatures can use to form a proof.
*
* NOTE: Subclasses MUST override this method (and add the exporting of
* their public and private key material).
*
* @param {object} [options={}] - Options hashmap.
* @param {boolean} [options.publicKey] - Export public key material?
* @param {boolean} [options.privateKey] - Export private key material?
*
* @returns {object} A public key object
* information used in verification methods by signatures.
*/
export({publicKey = false, privateKey = false} = {}) {
if(!publicKey && !privateKey) {
throw new Error(
'Export requires specifying either "publicKey" or "privateKey".');
}
const key = {
id: this.id,
type: this.type,
controller: this.controller
};
if(this.revoked) {
key.revoked = this.revoked;
}
return key;
}
/**
* Returns the public key fingerprint, multibase+multicodec encoded. The
* specific fingerprint method is determined by the key suite, and is often
* either a hash of the public key material (such as with RSA), or the
* full encoded public key (for key types with sufficiently short
* representations, such as ed25519).
* This is frequently used in initializing the key id, or generating some
* types of cryptonym DIDs.
*
* @returns {string} The fingerprint.
*/
fingerprint() {
throw new Error('Abstract method, must be implemented in subclass.');
}
/* eslint-disable jsdoc/check-param-names */
/**
* Verifies that a given key fingerprint matches the public key material
* belonging to this key pair.
*
* @param {string} fingerprint - Public key fingerprint.
*
* @returns {{verified: boolean}} An object with verified flag.
*/
/* eslint-enable */
verifyFingerprint(/* {fingerprint} */) {
throw new Error('Abstract method, must be implemented in subclass.');
}
/* eslint-disable max-len */
/**
* Returns a signer object for use with
* [jsonld-signatures]{@link https://github.com/digitalbazaar/jsonld-signatures}.
* NOTE: Applies only to verifier type keys (like ed25519).
*
* @example
* > const signer = keyPair.signer();
* > signer
* { sign: [AsyncFunction: sign] }
* > signer.sign({data});
*
* @returns {{sign: Function}} A signer for json-ld usage.
*/
/* eslint-enable */
signer() {
return {
async sign({/* data */}) {
throw new Error('Abstract method, must be implemented in subclass.');
}
};
}
/* eslint-disable max-len */
/**
* Returns a verifier object for use with
* [jsonld-signatures]{@link https://github.com/digitalbazaar/jsonld-signatures}.
* NOTE: Applies only to verifier type keys (like ed25519).
*
* @example
* > const verifier = keyPair.verifier();
* > verifier
* { verify: [AsyncFunction: verify] }
* > verifier.verify(key);
*
* @returns {{verify: Function}} Used to verify jsonld-signatures.
*/
/* eslint-enable */
verifier() {
return {
async verify({/* data, signature */}) {
throw new Error('Abstract method, must be implemented in subclass.');
}
};
}
}
// Implementers must override this in subclasses
LDKeyPair.SUITE_CONTEXT = 'INVALID LDKeyPair CONTEXT';