@digitalbazaar/x25519-key-agreement-key-2020
Advanced tools
Comparing version 1.2.1 to 2.0.0
# x25519-key-agreement-key-2020 Changelog | ||
## 2.0.0 - 2021-06-19 | ||
### Changed | ||
- **BREAKING**: Upgrade to @digitalbazaar/ed25519-verification-key-2020@3 | ||
which changes the key format to multicodec. | ||
## 1.2.1 - 2021-05-06 | ||
### Fixed | ||
- Fix `package.json` browser section alias for `crypto.js` (was causing downstream | ||
webpack errors). | ||
- Fix `package.json` browser section alias for `crypto.js` (was causing | ||
downstream webpack errors). | ||
@@ -9,0 +15,0 @@ ## 1.2.0 - 2021-04-02 |
@@ -11,2 +11,12 @@ /*! | ||
const SUITE_ID = 'X25519KeyAgreementKey2020'; | ||
// multibase base58-btc header | ||
const MULTIBASE_BASE58BTC_HEADER = 'z'; | ||
// multicodec ed25519-pub header as varint | ||
const MULTICODEC_ED25519_PUB_HEADER = new Uint8Array([0xed, 0x01]); | ||
// multicodec ed25519-priv header as varint | ||
const MULTICODEC_ED25519_PRIV_HEADER = new Uint8Array([0x80, 0x26]); | ||
// multicodec ed25519-pub header as varint | ||
const MULTICODEC_X25519_PUB_HEADER = new Uint8Array([0xec, 0x01]); | ||
// multicodec ed25519-priv header as varint | ||
const MULTICODEC_X25519_PRIV_HEADER = new Uint8Array([0x82, 0x26]); | ||
@@ -30,7 +40,24 @@ export class X25519KeyAgreementKey2020 extends LDKeyPair { | ||
this.type = SUITE_ID; | ||
this.publicKeyMultibase = options.publicKeyMultibase; | ||
if(!this.publicKeyMultibase) { | ||
throw TypeError('The "publicKeyMultibase" parameter is required.'); | ||
const {publicKeyMultibase, privateKeyMultibase} = options; | ||
if(!publicKeyMultibase) { | ||
throw new TypeError('The "publicKeyMultibase" property is required.'); | ||
} | ||
this.privateKeyMultibase = options.privateKeyMultibase; | ||
if(!publicKeyMultibase || !_isValidKeyHeader( | ||
publicKeyMultibase, MULTICODEC_X25519_PUB_HEADER)) { | ||
throw new Error( | ||
'"publicKeyMultibase" has invalid header bytes: ' + | ||
`"${publicKeyMultibase}".`); | ||
} | ||
if(privateKeyMultibase && !_isValidKeyHeader( | ||
privateKeyMultibase, MULTICODEC_X25519_PRIV_HEADER)) { | ||
throw new Error('"privateKeyMultibase" has invalid header bytes.'); | ||
} | ||
// assign valid key values | ||
this.publicKeyMultibase = publicKeyMultibase; | ||
this.privateKeyMultibase = privateKeyMultibase; | ||
if(this.controller && !this.id) { | ||
@@ -52,4 +79,6 @@ this.id = `${this.controller}#${this.fingerprint()}`; | ||
return new X25519KeyAgreementKey2020({ | ||
publicKeyMultibase: _multibaseEncode({bytes: publicKey}), | ||
privateKeyMultibase: _multibaseEncode({bytes: privateKey}), | ||
publicKeyMultibase: | ||
_multibaseEncode(MULTICODEC_X25519_PUB_HEADER, publicKey), | ||
privateKeyMultibase: | ||
_multibaseEncode(MULTICODEC_X25519_PRIV_HEADER, privateKey), | ||
...options | ||
@@ -95,7 +124,9 @@ }); | ||
// prefix with `z` to indicate multi-base base58btc encoding | ||
publicKeyMultibase = `z${publicKeyBase58}`; | ||
publicKeyMultibase = _multibaseEncode( | ||
MULTICODEC_X25519_PUB_HEADER, base58btc.decode(publicKeyBase58)); | ||
} | ||
if(privateKeyBase58) { | ||
// prefix with `z` to indicate multi-base base58btc encoding | ||
privateKeyMultibase = `z${privateKeyBase58}`; | ||
privateKeyMultibase = _multibaseEncode( | ||
MULTICODEC_X25519_PRIV_HEADER, base58btc.decode(privateKeyBase58)); | ||
} | ||
@@ -126,6 +157,6 @@ return new X25519KeyAgreementKey2020({ | ||
if(!keyPair.publicKeyMultibase.startsWith('z')) { | ||
if(!keyPair.publicKeyMultibase.startsWith(MULTIBASE_BASE58BTC_HEADER)) { | ||
throw new TypeError( | ||
// eslint-disable-next-line max-len | ||
'Expecting "publicKeyMultibase" value to be multibase base58btc encoded (must start with "z").' | ||
'Expecting "publicKeyMultibase" value to be multibase base58btc ' + | ||
'encoded (must start with "z").' | ||
); | ||
@@ -141,6 +172,6 @@ } | ||
if(keyPair.privateKeyMultibase) { | ||
if(!keyPair.privateKeyMultibase.startsWith('z')) { | ||
if(!keyPair.privateKeyMultibase.startsWith(MULTIBASE_BASE58BTC_HEADER)) { | ||
throw new TypeError( | ||
// eslint-disable-next-line max-len | ||
'Expecting "privateKeyMultibase" value to be multibase base58btc encoded (must start with "z").' | ||
'Expecting "privateKeyMultibase" value to be multibase base58btc ' + | ||
'encoded (must start with "z").' | ||
); | ||
@@ -168,3 +199,4 @@ } | ||
const edPubkeyBytes = _multibaseDecode({text: publicKeyMultibase}); | ||
const edPubkeyBytes = | ||
_multibaseDecode(MULTICODEC_ED25519_PUB_HEADER, publicKeyMultibase); | ||
@@ -178,3 +210,3 @@ // Converts a 32-byte Ed25519 public key into a 32-byte Curve25519 key | ||
} | ||
return _multibaseEncode({bytes: dhPubkeyBytes}); | ||
return _multibaseEncode(MULTICODEC_X25519_PUB_HEADER, dhPubkeyBytes); | ||
} | ||
@@ -194,3 +226,4 @@ | ||
const edPrivkeyBytes = _multibaseDecode({text: privateKeyMultibase}); | ||
const edPrivkeyBytes = | ||
_multibaseDecode(MULTICODEC_ED25519_PRIV_HEADER, privateKeyMultibase); | ||
// Converts a 64-byte Ed25519 secret key (or just the first 32-byte part of | ||
@@ -203,3 +236,3 @@ // it, which is the secret value) into a 32-byte Curve25519 secret key | ||
} | ||
return _multibaseEncode({bytes: dhPrivkeyBytes}); | ||
return _multibaseEncode(MULTICODEC_X25519_PRIV_HEADER, dhPrivkeyBytes); | ||
} | ||
@@ -261,9 +294,4 @@ | ||
} | ||
const publicKeyBytes = _multibaseDecode({text: publicKeyMultibase}); | ||
// X25519 cryptonyms are multicodec formatted values, specifically: | ||
// (multicodec('x25519-pub') + key bytes) | ||
const fingerprintBytes = _multicodecFormatFingerprint({publicKeyBytes}); | ||
const encodedFingerprint = _multibaseEncode({bytes: fingerprintBytes}); | ||
return encodedFingerprint; | ||
return publicKeyMultibase; | ||
} | ||
@@ -281,14 +309,4 @@ | ||
static fromFingerprint({fingerprint} = {}) { | ||
if(!fingerprint || | ||
!(typeof fingerprint === 'string' && fingerprint[0] === 'z')) { | ||
throw new Error('`fingerprint` must be a multibase encoded string.'); | ||
} | ||
const fingerprintBytes = _multibaseDecode({text: fingerprint}); | ||
const publicKeyBytes = _multicodecDecodeFingerprint({ | ||
bytes: fingerprintBytes | ||
}); | ||
return new X25519KeyAgreementKey2020({ | ||
publicKeyMultibase: _multibaseEncode({bytes: publicKeyBytes}) | ||
publicKeyMultibase: fingerprint | ||
}); | ||
@@ -309,8 +327,6 @@ } | ||
async deriveSecret({publicKey}) { | ||
const remotePublicKey = _multibaseDecode({ | ||
text: publicKey.publicKeyMultibase | ||
}); | ||
const privateKey = _multibaseDecode({ | ||
text: this.privateKeyMultibase | ||
}); | ||
const remotePublicKey = _multibaseDecode( | ||
MULTICODEC_X25519_PUB_HEADER, publicKey.publicKeyMultibase); | ||
const privateKey = _multibaseDecode( | ||
MULTICODEC_X25519_PRIV_HEADER, this.privateKeyMultibase); | ||
@@ -329,5 +345,3 @@ return deriveSecret({privateKey, remotePublicKey}); | ||
fingerprint() { | ||
const {publicKeyMultibase} = this; | ||
return X25519KeyAgreementKey2020 | ||
.fingerprintFromPublicKey({publicKeyMultibase}); | ||
return this.publicKeyMultibase; | ||
} | ||
@@ -351,29 +365,7 @@ | ||
// that it's base58btc multibase encoded | ||
if(!(typeof fingerprint === 'string' && fingerprint[0] === 'z')) { | ||
return { | ||
// eslint-disable-next-line max-len | ||
error: new Error('`fingerprint` must be a multibase base58btc encoded string (must start with a "z").'), | ||
valid: false | ||
}; | ||
if(!_isValidKeyHeader(fingerprint, MULTICODEC_X25519_PUB_HEADER)) { | ||
throw new Error( | ||
`"fingerprint" has invalid header bytes: "${fingerprint}".`); | ||
} | ||
let fingerprintBytes; | ||
let fingerprintKeyBytes; | ||
try { | ||
fingerprintBytes = _multibaseDecode({text: fingerprint}); | ||
fingerprintKeyBytes = _multicodecDecodeFingerprint({ | ||
bytes: fingerprintBytes | ||
}); | ||
} catch(e) { | ||
return {valid: false, error: new Error('Error decoding fingerprint.')}; | ||
} | ||
const publicKeyBytes = _multibaseDecode({text: this.publicKeyMultibase}); | ||
const valid = publicKeyBytes.toString() === fingerprintKeyBytes.toString(); | ||
if(!valid) { | ||
return { | ||
error: new Error('The fingerprint does not match the public key.'), | ||
valid: false | ||
}; | ||
} | ||
return {valid: true}; | ||
@@ -390,67 +382,49 @@ } | ||
/** | ||
* Checks to see if the given value is a valid multibase encoded key. | ||
* | ||
* @param {Uint8Array} multibaseKey - The multibase-encoded key value. | ||
* @param {Uint8Array} expectedHeader - The expected header for the key value. | ||
* @returns {boolean} Returns true if the header is valid, false otherwise. | ||
*/ | ||
function _isValidKeyHeader(multibaseKey, expectedHeader) { | ||
if(!(typeof multibaseKey === 'string' && | ||
multibaseKey[0] === MULTIBASE_BASE58BTC_HEADER)) { | ||
return false; | ||
} | ||
const keyBytes = base58btc.decode(multibaseKey.slice(1)); | ||
return expectedHeader.every((val, i) => keyBytes[i] === val); | ||
} | ||
/** | ||
* Encodes a given Uint8Array to multibase-encoded string. | ||
* | ||
* @param {Uint8Array} header - Multicodec header to prepend to the bytes. | ||
* @param {Uint8Array} bytes - Bytes to encode. | ||
* @returns {string} Multibase-encoded string. | ||
*/ | ||
function _multibaseEncode({bytes}) { | ||
// prefix with `z` to indicate multi-base base58btc encoding | ||
return `z${base58btc.encode(bytes)}`; | ||
function _multibaseEncode(header, bytes) { | ||
const mcBytes = new Uint8Array(header.length + bytes.length); | ||
mcBytes.set(header); | ||
mcBytes.set(bytes, header.length); | ||
return MULTIBASE_BASE58BTC_HEADER + base58btc.encode(mcBytes); | ||
} | ||
/** | ||
* Decodes a given multibase-encoded string. | ||
* Decodes a given string as a multibase-encoded multicodec value. | ||
* | ||
* @param {Uint8Array} header - Expected header bytes for the multicodec value. | ||
* @param {string} text - Multibase encoded string to decode. | ||
* @returns {Uint8Array} Decoded bytes. | ||
*/ | ||
function _multibaseDecode({text}) { | ||
// drop the initial multibase 'z' prefix | ||
return base58btc.decode(text.substr(1)); | ||
} | ||
function _multibaseDecode(header, text) { | ||
const mcValue = base58btc.decode(text.substr(1)); | ||
/** | ||
* Returns raw fingerprint bytes, by adding a multicodec prefix for an | ||
* X25519 public key. | ||
* | ||
* @see https://github.com/multiformats/multicodec/blob/master/table.csv | ||
* 0xec is the value for X25519 public key | ||
* 0x01 is from varint.encode(0xec) -> [0xec, 0x01] | ||
* @see https://github.com/multiformats/unsigned-varint | ||
* | ||
* @param {object} [options={}] - Options hashmap. | ||
* @param {Uint8Array} options.publicKeyBytes - Public key raw bytes. | ||
* | ||
* @returns {Uint8Array} Multicodec formatted public key fingerprint bytes. | ||
*/ | ||
function _multicodecFormatFingerprint({publicKeyBytes} = {}) { | ||
const fingerprintBytes = new Uint8Array(2 + publicKeyBytes.length); | ||
fingerprintBytes[0] = 0xec; | ||
fingerprintBytes[1] = 0x01; | ||
fingerprintBytes.set(publicKeyBytes, 2); | ||
return fingerprintBytes; | ||
} | ||
if(!header.every((val, i) => mcValue[i] === val)) { | ||
throw new Error('Multibase value does not have expected header.'); | ||
} | ||
/** | ||
* Decodes a multicodec-formatted Uint8Array containing an x25519 public key. | ||
* | ||
* @see https://github.com/multiformats/multicodec/blob/master/table.csv | ||
* 0xec is the value for X25519 public key | ||
* 0x01 is from varint.encode(0xec) -> [0xec, 0x01] | ||
* @see https://github.com/multiformats/unsigned-varint | ||
* | ||
* @param {object} [options={}] - Options hashmap. | ||
* @param {Uint8Array} options.bytes - Key fingerprint decoded from multibase. | ||
* | ||
* @returns {{publicKeyBytes: Uint8Array}} The public key bytes (without the | ||
* multicodec prefix). | ||
*/ | ||
function _multicodecDecodeFingerprint({bytes} = {}) { | ||
if(!(bytes && bytes[0] === 0xec && bytes[1] === 0x01)) { | ||
// eslint-disable-next-line max-len | ||
throw new Error('Expecting public key to be "x255519-pub" multicodec formatted [0xec, 0x01, <public key bytes>]'); | ||
} | ||
// Remove the multicodec prefix | ||
const publicKeyBytes = bytes.slice(2); | ||
return publicKeyBytes; | ||
return mcValue.slice(header.length); | ||
} |
{ | ||
"name": "@digitalbazaar/x25519-key-agreement-key-2020", | ||
"version": "1.2.1", | ||
"version": "2.0.0", | ||
"description": "An X25519 (Curve25519) DH (Diffie-Hellman) key implementation to work with the X25519 2020 Crypto suite.", | ||
@@ -38,3 +38,3 @@ "homepage": "https://github.com/digitalbazaar/x25519-key-agreement-key-2020", | ||
"@babel/runtime": "^7.13.9", | ||
"@digitalbazaar/ed25519-verification-key-2020": "^2.0.0", | ||
"@digitalbazaar/ed25519-verification-key-2020": "^3.0.0", | ||
"@digitalbazaar/x25519-key-agreement-key-2019": "^5.0.1", | ||
@@ -41,0 +41,0 @@ "babel-loader": "^8.2.2", |
@@ -69,7 +69,7 @@ # X25519KeyAgreementKey2020 _(@digitalbazaar/x25519-key-agreement-key-2020)_ | ||
{ | ||
"id": "did:example:1234#z6LShXSgPY6JKdbMmiLYaQ8JGjJFRrb4TsByj3dz5sSyQLUp", | ||
"id": "did:example:1234#z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM", | ||
"controller": "did:example:1234", | ||
"type": "X25519KeyAgreementKey2020", | ||
"publicKeyMultibase": "z6rGWsEHSEAscgKxn3kcLx95mai3wmG1pr4vJbQoSgxi4", | ||
"privateKeyMultibase": "z8aAkJ9NcWKhSdQXvz1Eh2bDhptgSKAYn8ECvPVcAYjrp" | ||
"publicKeyMultibase": "z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM", | ||
"privateKeyMultibase": "z3weeMD56C1T347EmB6kYNS7trpQwjvtQCpCYRpqGz6mcemT" | ||
} | ||
@@ -85,6 +85,6 @@ | ||
{ | ||
"id": "did:example:1234#z6LShXSgPY6JKdbMmiLYaQ8JGjJFRrb4TsByj3dz5sSyQLUp", | ||
"id": "did:example:1234#z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM", | ||
"controller": "did:example:1234", | ||
"type": "X25519KeyAgreementKey2020", | ||
"publicKeyMultibase": "z6rGWsEHSEAscgKxn3kcLx95mai3wmG1pr4vJbQoSgxi4" | ||
"publicKeyMultibase": "z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM" | ||
} | ||
@@ -100,7 +100,7 @@ ``` | ||
{ | ||
"id": "did:example:1234#z6LShXSgPY6JKdbMmiLYaQ8JGjJFRrb4TsByj3dz5sSyQLUp", | ||
"id": "did:example:1234#z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM", | ||
"controller": "did:example:1234", | ||
"type": "X25519KeyAgreementKey2020", | ||
"publicKeyMultibase": "z6rGWsEHSEAscgKxn3kcLx95mai3wmG1pr4vJbQoSgxi4", | ||
"privateKeyMultibase": "z8aAkJ9NcWKhSdQXvz1Eh2bDhptgSKAYn8ECvPVcAYjrp" | ||
"publicKeyMultibase": "z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM", | ||
"privateKeyMultibase": "z3weeMD56C1T347EmB6kYNS7trpQwjvtQCpCYRpqGz6mcemT" | ||
} | ||
@@ -114,6 +114,6 @@ ``` | ||
const keyPair = await X25519KeyAgreementKey2020.from({ | ||
"id": "did:example:1234#z6LShXSgPY6JKdbMmiLYaQ8JGjJFRrb4TsByj3dz5sSyQLUp", | ||
"id": "did:example:1234#z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM", | ||
"controller": "did:example:1234", | ||
"type": "X25519KeyAgreementKey2020", | ||
"publicKeyMultibase": "z6rGWsEHSEAscgKxn3kcLx95mai3wmG1pr4vJbQoSgxi4" | ||
"publicKeyMultibase": "z6LSeRSE5Em5oJpwdk3NBaLVERBS332ULC7EQq5EtMsmXhsM" | ||
}); | ||
@@ -120,0 +120,0 @@ ``` |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
25925
453
1