Skip to content
This repository has been archived by the owner on Dec 3, 2024. It is now read-only.

Commit

Permalink
Generated a documentation and tested the generation of a distribution.
Browse files Browse the repository at this point in the history
  • Loading branch information
iherman committed Aug 14, 2024
1 parent e416501 commit 69b07cd
Show file tree
Hide file tree
Showing 76 changed files with 3,906 additions and 5 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
# Multikey <-> WebCrypto and JWK conversions


Conversion to and from [Multikey format](https://www.w3.org/TR/controller-document/#multikey) from WebCrypto and JWK for the three EC curves that are defined for Verifiable Credentials:
Conversion of cryptographic keys in [Multikey format](https://www.w3.org/TR/controller-document/#multikey) to and
from [WebCrypto](https://www.w3.org/TR/WebCryptoAPI/) and [JWK](https://datatracker.ietf.org/doc/html/rfc7517). The conversions are available for the three EC curves that are defined for Verifiable Credentials:
[ECDSA with P-256 and P-384](https://www.w3.org/TR/vc-di-ecdsa/#multikey) and [EDDSA](https://www.w3.org/TR/vc-di-eddsa/#multikey).

This is really a proof-of-concept implementation, not sure it has a wide interest out there. But it shows that such conversion can indeed be done, which is an important feature in practice where multikey implementations are rare.
This is really a proof-of-concept implementation. It shows that such conversion _can indeed be done_,
which is an important in proving the practical usability of multikeys.

The package has been written in TypeScript+Node.js
The package has been written in TypeScript+Node.js. (There is also a Deno version.)

The interface is pretty straightforward, see `index.ts` for now. More documentation may come.
For a more detailed documentation, see the [code documentation](https://iherman.github.io/multikey-webcrypto/), generated by typedoc.

[Examples to come]

37 changes: 37 additions & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Conversion to and from [Multikey format](https://www.w3.org/TR/controller-document/#multikey) from
* JWK for the three EC curves that are defined for Verifiable Credentials: [ECDSA with P-256 and P-384](https://www.w3.org/TR/vc-di-ecdsa/#multikey)
* and [EDDSA](https://www.w3.org/TR/vc-di-eddsa/#multikey).
*
* @package
*/
import { JWKKeyPair, MultikeyPair, Multikey } from './lib/common';
export type { JWKKeyPair, MultikeyPair, Multikey } from './lib/common';
/**
* Generic function to convert a multikey pair to JWK. This function decodes the multikey data
* into a binary buffer, checks the preambles and invokes the crypto specific converter functions
* (depending on the preamble values) that do the final
* conversion from the binary data to JWK.
*
* Works for ecdsa (both P-384 and P-256), and eddsa.
*
* @param keys
* @throws - exceptions if something is incorrect in the incoming data
*/
export declare function multikeyToJWK(keys: Multikey): JsonWebKey;
export declare function multikeyToJWK(keys: MultikeyPair): JWKKeyPair;
/**
* Convert JWK Key pair to Multikeys. This function decodes the JWK keys, finds out which binary key it encodes
* and converts the key to the multikey versions depending on the exact curve.
*
* Note that the code does not check (yet?) all combination of the JWK pairs and fields for possible errors, only
* those that would lead to error in this package. E.g., it does not check whether the x (and possibly y) values
* are identical in the secret and private JWK keys.
*
* Works for ecdsa (both P-384 and P-256), and eddsa.
*
* @param keys
* @throws - exceptions if something is incorrect in the incoming data
*/
export declare function JWKToMultikey(keys: JsonWebKey): Multikey;
export declare function JWKToMultikey(keys: JWKKeyPair): MultikeyPair;
33 changes: 33 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use strict";
/**
* Conversion to and from [Multikey format](https://www.w3.org/TR/controller-document/#multikey) from
* JWK for the three EC curves that are defined for Verifiable Credentials: [ECDSA with P-256 and P-384](https://www.w3.org/TR/vc-di-ecdsa/#multikey)
* and [EDDSA](https://www.w3.org/TR/vc-di-eddsa/#multikey).
*
* @package
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.multikeyToJWK = multikeyToJWK;
exports.JWKToMultikey = JWKToMultikey;
const convert = require("./lib/convert");
const common_1 = require("./lib/common");
function multikeyToJWK(keys) {
const input = (0, common_1.isMultikeyPair)(keys) ? keys : { publicKeyMultibase: keys };
const jwk_keys = convert.multikeyToJWK(input);
if ((0, common_1.isMultikeyPair)(keys)) {
return jwk_keys;
}
else {
return jwk_keys.public;
}
}
function JWKToMultikey(keys) {
const input = (0, common_1.isJWKKeyPair)(keys) ? keys : { public: keys };
const m_keys = convert.JWKToMultikey(input);
if ((0, common_1.isJWKKeyPair)(keys)) {
return m_keys;
}
else {
return m_keys.publicKeyMultibase;
}
}
137 changes: 137 additions & 0 deletions dist/lib/common.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
export interface JWKKeyPair {
public: JsonWebKey;
secret?: JsonWebKey;
}
/**
* Typeguard for JWK Key Pair.
* It is not really elaborate, it only tries to differentiate between a JWK Single Key and a Key Pair.
*
* @param obj
* @returns is it a JWKKeyPair?
*/
export declare function isJWKKeyPair(obj: any): obj is JWKKeyPair;
/**
* Type for a Multikey
*
* One day this could become a string with a fixed regexp...
*/
export type Multikey = string;
/**
* The specification is a bit fuzzy and talks about Multikey for a pair, and for individual constituents.
* We need to differentiate those two...
*/
export interface MultikeyPair {
publicKeyMultibase: Multikey;
secretKeyMultibase?: Multikey;
}
/**
* Typeguard for a Multikey Pair.
* It is not really elaborate, it only tries to differentiate between a single Multikey and a Key Pair.
*
* @param obj
* @returns is it a MultikeyPair?
*/
export declare function isMultikeyPair(obj: any): obj is MultikeyPair;
/**
* Same as the Multikey Pair, but decoded and without the preambles. Just the bare key values.
*/
export interface MultikeyPairBinary {
public: Uint8Array;
secret?: Uint8Array;
}
/************************************************************************* */
/************************************************************************* */
/**
* Names for the various crypto curves
*/
export declare enum CryptoCurves {
ECDSA_384 = "secp384r1",
ECDSA_256 = "secp256r1",
EDDSA = "ed25519"
}
/**
* Names for the key types
*/
export declare enum CryptoKeyTypes {
PUBLIC = "public",
SECRET = "secret"
}
/************************************* Preambles ***************************/
/**
* Type used for preambles, which are, so far, a single pair of numbers.
*/
export type Preamble<T> = [T, T];
/**
* Each crypto class has two preamble, on for the public and one for the secret keys
*/
interface MultikeyPreambles {
public: Preamble<number>;
secret: Preamble<number>;
}
/**
* Preamble value for ECDSA, a.k.a. ed25519 curve
*/
export declare const EddsaPreambles: MultikeyPreambles;
/**
* Preamble for ECDSA P-256, a.k.a. secp256r1 curve
*/
export declare const Ecdsa256Preambles: MultikeyPreambles;
/**
* Preamble for ECDSA P-256, a.k.a. secp384r1 curve
*/
export declare const Ecdsa384Preambles: MultikeyPreambles;
/************************************ Converter tables **********************************/
/**
* What preambles must be used for a Curve (mapping type?
*/
export type ClassToPreamble = {
[key in CryptoCurves]: MultikeyPreambles;
};
/**
* What preambles must be used for a Curve (data)?
*/
export declare const classToPreamble: ClassToPreamble;
/**
* What coder function must be used to convert from Multikey to JWK (type)?
*/
export type ClassToDecoder = {
[key in CryptoCurves]: (keyCurve: CryptoCurves, x: Uint8Array, d?: Uint8Array) => JWKKeyPair;
};
/**
* hat coder function must be used to convert from Multikey to JWK (data)?
*/
export declare const classToDecoder: ClassToDecoder;
/**
* What coder function must be used to convert from JWK to Multikey (type)?
*/
export type ClassToEncoder = {
[key in CryptoCurves]: (keyCurve: CryptoCurves, x: Uint8Array, d: Uint8Array | undefined, _y?: Uint8Array) => MultikeyPairBinary;
};
/**
* What coder function must be used to convert from JWK to Multikey (data)?
*/
export declare const classToEncoder: ClassToEncoder;
/**
* List of possible ECDSA Curves. Having this here declaratively may make it easier if
* in the future, a new curve is added to the family (P-512)?
*/
export declare const ECDSACurves: CryptoCurves[];
/**
* This is an internal type, used for the implementation: return the crypto curve and type from a preamble.
*
* So far, I have not yet found a way to encode that in a simple table, hence the separate function.
*/
export interface CryptoKeyData {
crCurve: CryptoCurves;
crType: CryptoKeyTypes;
}
/**
* Classify the crypto key based on the multikey preamble characters that are at the start of the code.
* These are two binary numbers, signalling the crypto class (ecdsa or eddsa) and, in the former case,
* the hash function.
*
* @param preamble
* @returns
*/
export declare function preambleToCryptoData(preamble: Preamble<number>): CryptoKeyData;
export {};
155 changes: 155 additions & 0 deletions dist/lib/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ECDSACurves = exports.classToEncoder = exports.classToDecoder = exports.classToPreamble = exports.Ecdsa384Preambles = exports.Ecdsa256Preambles = exports.EddsaPreambles = exports.CryptoKeyTypes = exports.CryptoCurves = void 0;
exports.isJWKKeyPair = isJWKKeyPair;
exports.isMultikeyPair = isMultikeyPair;
exports.preambleToCryptoData = preambleToCryptoData;
const eddsa = require("./eddsa");
const ecdsa = require("./ecdsa");
/**
* Typeguard for JWK Key Pair.
* It is not really elaborate, it only tries to differentiate between a JWK Single Key and a Key Pair.
*
* @param obj
* @returns is it a JWKKeyPair?
*/
// deno-lint-ignore no-explicit-any
function isJWKKeyPair(obj) {
return obj.public !== undefined;
}
/**
* Typeguard for a Multikey Pair.
* It is not really elaborate, it only tries to differentiate between a single Multikey and a Key Pair.
*
* @param obj
* @returns is it a MultikeyPair?
*/
// deno-lint-ignore no-explicit-any
function isMultikeyPair(obj) {
return obj.publicKeyMultibase !== undefined;
}
/************************************************************************* */
/* Values to handle the various preamble bytes for the different key types */
/************************************************************************* */
/**
* Names for the various crypto curves
*/
var CryptoCurves;
(function (CryptoCurves) {
CryptoCurves["ECDSA_384"] = "secp384r1";
CryptoCurves["ECDSA_256"] = "secp256r1";
CryptoCurves["EDDSA"] = "ed25519";
})(CryptoCurves || (exports.CryptoCurves = CryptoCurves = {}));
/**
* Names for the key types
*/
var CryptoKeyTypes;
(function (CryptoKeyTypes) {
CryptoKeyTypes["PUBLIC"] = "public";
CryptoKeyTypes["SECRET"] = "secret";
})(CryptoKeyTypes || (exports.CryptoKeyTypes = CryptoKeyTypes = {}));
/**
* Preamble value for ECDSA, a.k.a. ed25519 curve
*/
exports.EddsaPreambles = {
public: [0xed, 0x01],
secret: [0x80, 0x26],
};
/**
* Preamble for ECDSA P-256, a.k.a. secp256r1 curve
*/
exports.Ecdsa256Preambles = {
public: [0x80, 0x24],
secret: [0x86, 0x26],
};
/**
* Preamble for ECDSA P-256, a.k.a. secp384r1 curve
*/
exports.Ecdsa384Preambles = {
public: [0x81, 0x24],
secret: [0x87, 0x26],
};
/**
* What preambles must be used for a Curve (data)?
*/
exports.classToPreamble = {
[CryptoCurves.EDDSA]: exports.EddsaPreambles,
[CryptoCurves.ECDSA_256]: exports.Ecdsa256Preambles,
[CryptoCurves.ECDSA_384]: exports.Ecdsa384Preambles,
};
/**
* hat coder function must be used to convert from Multikey to JWK (data)?
*/
exports.classToDecoder = {
[CryptoCurves.EDDSA]: eddsa.multikeyBinaryToJWK,
[CryptoCurves.ECDSA_256]: ecdsa.multikeyBinaryToJWK,
[CryptoCurves.ECDSA_384]: ecdsa.multikeyBinaryToJWK,
};
/**
* What coder function must be used to convert from JWK to Multikey (data)?
*/
exports.classToEncoder = {
[CryptoCurves.EDDSA]: eddsa.JWKToMultikeyBinary,
[CryptoCurves.ECDSA_256]: ecdsa.JWKToMultikeyBinary,
[CryptoCurves.ECDSA_384]: ecdsa.JWKToMultikeyBinary,
};
/**
* List of possible ECDSA Curves. Having this here declaratively may make it easier if
* in the future, a new curve is added to the family (P-512)?
*/
exports.ECDSACurves = [CryptoCurves.ECDSA_256, CryptoCurves.ECDSA_384];
/**
* Classify the crypto key based on the multikey preamble characters that are at the start of the code.
* These are two binary numbers, signalling the crypto class (ecdsa or eddsa) and, in the former case,
* the hash function.
*
* @param preamble
* @returns
*/
function preambleToCryptoData(preamble) {
// Ugly but effective and simple trick to compare two arrays
const eq = (a, b) => JSON.stringify(a) === JSON.stringify(b);
if (preamble.length !== 2) {
throw new Error(`${preamble} is not valid, it should have a size of exactly 2.`);
}
// The real classification...
if (eq(preamble, exports.Ecdsa256Preambles.secret)) {
return {
crCurve: CryptoCurves.ECDSA_256,
crType: CryptoKeyTypes.SECRET,
};
}
else if (eq(preamble, exports.Ecdsa256Preambles.public)) {
return {
crCurve: CryptoCurves.ECDSA_256,
crType: CryptoKeyTypes.PUBLIC,
};
}
else if (eq(preamble, exports.Ecdsa384Preambles.secret)) {
return {
crCurve: CryptoCurves.ECDSA_384,
crType: CryptoKeyTypes.SECRET,
};
}
else if (eq(preamble, exports.Ecdsa384Preambles.public)) {
return {
crCurve: CryptoCurves.ECDSA_384,
crType: CryptoKeyTypes.PUBLIC,
};
}
else if (eq(preamble, exports.EddsaPreambles.secret)) {
return {
crCurve: CryptoCurves.EDDSA,
crType: CryptoKeyTypes.SECRET,
};
}
else if (eq(preamble, exports.EddsaPreambles.public)) {
return {
crCurve: CryptoCurves.EDDSA,
crType: CryptoKeyTypes.PUBLIC,
};
}
else {
throw new Error(`${preamble} is unknown. Should refer to secret or private eddsa or ecdsa (the latter with P-256 or P-384)`);
}
}
Loading

0 comments on commit 69b07cd

Please sign in to comment.