Comparing version 5.10.0 to 5.11.0
@@ -8,2 +8,3 @@ import * as u8a from 'uint8arrays'; | ||
import canonicalizeData from 'canonicalize'; | ||
import { bech32 } from 'bech32'; | ||
import { sharedKey, generateKeyPair } from '@stablelib/x25519'; | ||
@@ -23,2 +24,5 @@ import { XChaCha20Poly1305 } from '@stablelib/xchacha20poly1305'; | ||
} | ||
function bytesToBase58(b) { | ||
return u8a.toString(b, 'base58btc'); | ||
} | ||
function hexToBytes(s) { | ||
@@ -340,2 +344,237 @@ const input = s.startsWith('0x') ? s.substring(2) : s; | ||
// https://github.com/crypto-browserify/ripemd160/blob/master/index.js | ||
const zl = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]; | ||
const zr = [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]; | ||
const sl = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]; | ||
const sr = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]; | ||
const hl = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]; | ||
const hr = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]; | ||
function rotl(x, n) { | ||
return x << n | x >>> 32 - n; | ||
} | ||
function fn1(a, b, c, d, e, m, k, s) { | ||
return rotl(a + (b ^ c ^ d) + m + k | 0, s) + e | 0; | ||
} | ||
function fn2(a, b, c, d, e, m, k, s) { | ||
return rotl(a + (b & c | ~b & d) + m + k | 0, s) + e | 0; | ||
} | ||
function fn3(a, b, c, d, e, m, k, s) { | ||
return rotl(a + ((b | ~c) ^ d) + m + k | 0, s) + e | 0; | ||
} | ||
function fn4(a, b, c, d, e, m, k, s) { | ||
return rotl(a + (b & d | c & ~d) + m + k | 0, s) + e | 0; | ||
} | ||
function fn5(a, b, c, d, e, m, k, s) { | ||
return rotl(a + (b ^ (c | ~d)) + m + k | 0, s) + e | 0; | ||
} | ||
class Ripemd160 { | ||
// state | ||
constructor(blockSize = 64) { | ||
this._a = 0x67452301; | ||
this._b = 0xefcdab89; | ||
this._c = 0x98badcfe; | ||
this._d = 0x10325476; | ||
this._e = 0xc3d2e1f0; | ||
this._blockOffset = 0; | ||
this._block = void 0; | ||
this._blockSize = void 0; | ||
this._length = [0, 0, 0, 0]; | ||
this._finalized = void 0; | ||
this.update = data => { | ||
if (this._finalized) throw new Error('Digest already called'); // consume data | ||
const block = this._block; | ||
let offset = 0; | ||
while (this._blockOffset + data.length - offset >= this._blockSize) { | ||
for (let i = this._blockOffset; i < this._blockSize;) block[i++] = data[offset++]; | ||
this._update(); | ||
this._blockOffset = 0; | ||
} | ||
while (offset < data.length) block[this._blockOffset++] = data[offset++]; // update length | ||
for (let j = 0, carry = data.length * 8; carry > 0; ++j) { | ||
this._length[j] += carry; | ||
carry = this._length[j] / 0x0100000000 | 0; | ||
if (carry > 0) this._length[j] -= 0x0100000000 * carry; | ||
} | ||
return this; | ||
}; | ||
this.digest = () => { | ||
if (this._finalized) throw new Error('Digest already called'); | ||
this._finalized = true; | ||
const digest = this._digest(); // reset state | ||
this._block.fill(0); | ||
this._blockOffset = 0; | ||
for (let i = 0; i < 4; ++i) this._length[i] = 0; | ||
return digest; | ||
}; | ||
this._update = () => { | ||
const words = new Array(16); | ||
const temp = new DataView(this._block.buffer); | ||
for (let j = 0; j < 16; ++j) words[j] = words[j] = temp.getInt32(j * 4, true); | ||
let al = this._a | 0; | ||
let bl = this._b | 0; | ||
let cl = this._c | 0; | ||
let dl = this._d | 0; | ||
let el = this._e | 0; | ||
let ar = this._a | 0; | ||
let br = this._b | 0; | ||
let cr = this._c | 0; | ||
let dr = this._d | 0; | ||
let er = this._e | 0; // computation | ||
for (let i = 0; i < 80; i += 1) { | ||
let tl; | ||
let tr; | ||
if (i < 16) { | ||
tl = fn1(al, bl, cl, dl, el, words[zl[i]], hl[0], sl[i]); | ||
tr = fn5(ar, br, cr, dr, er, words[zr[i]], hr[0], sr[i]); | ||
} else if (i < 32) { | ||
tl = fn2(al, bl, cl, dl, el, words[zl[i]], hl[1], sl[i]); | ||
tr = fn4(ar, br, cr, dr, er, words[zr[i]], hr[1], sr[i]); | ||
} else if (i < 48) { | ||
tl = fn3(al, bl, cl, dl, el, words[zl[i]], hl[2], sl[i]); | ||
tr = fn3(ar, br, cr, dr, er, words[zr[i]], hr[2], sr[i]); | ||
} else if (i < 64) { | ||
tl = fn4(al, bl, cl, dl, el, words[zl[i]], hl[3], sl[i]); | ||
tr = fn2(ar, br, cr, dr, er, words[zr[i]], hr[3], sr[i]); | ||
} else { | ||
// if (i<80) { | ||
tl = fn5(al, bl, cl, dl, el, words[zl[i]], hl[4], sl[i]); | ||
tr = fn1(ar, br, cr, dr, er, words[zr[i]], hr[4], sr[i]); | ||
} | ||
al = el; | ||
el = dl; | ||
dl = rotl(cl, 10); | ||
cl = bl; | ||
bl = tl; | ||
ar = er; | ||
er = dr; | ||
dr = rotl(cr, 10); | ||
cr = br; | ||
br = tr; | ||
} // update state | ||
const t = this._b + cl + dr | 0; | ||
this._b = this._c + dl + er | 0; | ||
this._c = this._d + el + ar | 0; | ||
this._d = this._e + al + br | 0; | ||
this._e = this._a + bl + cr | 0; | ||
this._a = t; | ||
}; | ||
this._digest = () => { | ||
// create padding and handle blocks | ||
this._block[this._blockOffset++] = 0x80; | ||
if (this._blockOffset > 56) { | ||
this._block.fill(0, this._blockOffset, 64); | ||
this._update(); | ||
this._blockOffset = 0; | ||
} | ||
this._block.fill(0, this._blockOffset, 56); | ||
const temp = new DataView(this._block.buffer); | ||
temp.setUint32(56, this._length[0], true); | ||
temp.setUint32(60, this._length[1], true); | ||
this._block = new Uint8Array(temp.buffer); | ||
this._update(); // produce result | ||
const buffer = new DataView(new Uint8Array(20).buffer); | ||
buffer.setInt32(0, this._a, true); | ||
buffer.setInt32(4, this._b, true); | ||
buffer.setInt32(8, this._c, true); | ||
buffer.setInt32(12, this._d, true); | ||
buffer.setInt32(16, this._e, true); | ||
return new Uint8Array(buffer.buffer); | ||
}; | ||
this._block = new Uint8Array(blockSize); | ||
this._blockSize = blockSize; | ||
this._blockOffset = 0; | ||
this._length = [0, 0, 0, 0]; | ||
this._finalized = false; | ||
} | ||
} | ||
const publicKeyToAddress$1 = publicKey => { | ||
const publicKeyBuffer = u8a.fromString(publicKey, 'hex'); | ||
const publicKeyHash = new Ripemd160().update(sha256(publicKeyBuffer)).digest(); | ||
const step1 = '00' + u8a.toString(publicKeyHash, 'hex'); | ||
const step2 = sha256(u8a.fromString(step1, 'hex')); | ||
const step3 = sha256(step2); | ||
const checksum = u8a.toString(step3, 'hex').substring(0, 8); | ||
const step4 = step1 + checksum; | ||
return bytesToBase58(u8a.fromString(step4, 'hex')); | ||
}; | ||
const publicKeyToAddress = (publicKey, prefix) => { | ||
const ec$1 = new ec('secp256k1'); | ||
const compressedPublicKey = ec$1.keyFromPublic(publicKey, 'hex').getPublic().encode('hex', true); | ||
const publicKeyBuffer = u8a.fromString(compressedPublicKey, 'hex'); | ||
const hash = new Ripemd160().update(sha256(publicKeyBuffer)).digest(); | ||
const words = bech32.toWords(hash); | ||
return bech32.encode(prefix, words).replace(prefix, ''); | ||
}; | ||
const verifyBlockchainAccountId = (publicKey, blockchainAccountId) => { | ||
if (blockchainAccountId) { | ||
const chain = blockchainAccountId.split(':'); | ||
switch (chain[0]) { | ||
case 'bip122': | ||
chain[chain.length - 1] = publicKeyToAddress$1(publicKey); | ||
break; | ||
case 'cosmos': | ||
chain[chain.length - 1] = publicKeyToAddress(publicKey, chain[1]); | ||
break; | ||
case 'eip155': | ||
chain[chain.length - 1] = toEthereumAddress(publicKey); | ||
break; | ||
default: | ||
return false; | ||
} | ||
return chain.join(':') === blockchainAccountId; | ||
} | ||
return false; | ||
}; | ||
const secp256k1 = new ec('secp256k1'); // converts a JOSE signature to it's components | ||
@@ -399,3 +638,3 @@ | ||
}); | ||
const ethAddressKeys = authenticators.filter(({ | ||
const blockchainAddressKeys = authenticators.filter(({ | ||
ethereumAddress, | ||
@@ -415,4 +654,4 @@ blockchainAccountId | ||
if (!signer && ethAddressKeys.length > 0) { | ||
signer = verifyRecoverableES256K(data, signature, ethAddressKeys); | ||
if (!signer && blockchainAddressKeys.length > 0) { | ||
signer = verifyRecoverableES256K(data, signature, blockchainAddressKeys); | ||
} | ||
@@ -448,3 +687,5 @@ | ||
const keyHex = bytesToHex(extractPublicKeyBytes(pk)); | ||
return keyHex === recoveredPublicKeyHex || keyHex === recoveredCompressedPublicKeyHex || ((_pk$ethereumAddress = pk.ethereumAddress) == null ? void 0 : _pk$ethereumAddress.toLowerCase()) === recoveredAddress || ((_pk$blockchainAccount = pk.blockchainAccountId) == null ? void 0 : (_pk$blockchainAccount2 = _pk$blockchainAccount.split('@eip155')) == null ? void 0 : _pk$blockchainAccount2[0].toLowerCase()) === recoveredAddress; | ||
return keyHex === recoveredPublicKeyHex || keyHex === recoveredCompressedPublicKeyHex || ((_pk$ethereumAddress = pk.ethereumAddress) == null ? void 0 : _pk$ethereumAddress.toLowerCase()) === recoveredAddress || ((_pk$blockchainAccount = pk.blockchainAccountId) == null ? void 0 : (_pk$blockchainAccount2 = _pk$blockchainAccount.split('@eip155')) == null ? void 0 : _pk$blockchainAccount2[0].toLowerCase()) === recoveredAddress || // CAIP-2 | ||
verifyBlockchainAccountId(recoveredPublicKeyHex, pk.blockchainAccountId) // CAIP-10 | ||
; | ||
}); | ||
@@ -451,0 +692,0 @@ return signer; |
{ | ||
"name": "did-jwt", | ||
"version": "5.10.0", | ||
"version": "5.11.0", | ||
"description": "Library for Signing and Verifying JWTs that use DIDs as issuers and JWEs that use DIDs as recipients", | ||
@@ -86,2 +86,3 @@ "source": "src/index.ts", | ||
"@stablelib/xchacha20poly1305": "^1.0.1", | ||
"bech32": "^2.0.0", | ||
"canonicalize": "^1.0.5", | ||
@@ -88,0 +89,0 @@ "did-resolver": "^3.1.1", |
import VerifierAlgorithm from '../VerifierAlgorithm' | ||
import { createJWT } from '../JWT' | ||
import { toEthereumAddress } from '../Digest' | ||
import nacl from 'tweetnacl' | ||
@@ -10,2 +9,5 @@ import { ec as EC } from 'elliptic' | ||
import { ES256KSigner } from '../signers/ES256KSigner' | ||
import { toEthereumAddress } from '../Digest' | ||
import { publicKeyToAddress as toBip122Address } from '../blockchains/bip122' | ||
import { publicKeyToAddress as toCosmosAddressWithoutPrefix } from '../blockchains/cosmos' | ||
@@ -47,3 +49,6 @@ const secp256k1 = new EC('secp256k1') | ||
const publicKeyMultibase = bytesToMultibase(hexToBytes(publicKey), 'base58btc') | ||
const address = toEthereumAddress(publicKey) | ||
const eip155 = toEthereumAddress(publicKey) | ||
const bip122 = toBip122Address(publicKey) | ||
const cosmosPrefix = 'example' | ||
const cosmos = toCosmosAddressWithoutPrefix(publicKey, cosmosPrefix) | ||
const signer = ES256KSigner(privateKey) | ||
@@ -77,3 +82,3 @@ const recoverySigner = ES256KSigner(privateKey, true) | ||
controller: did, | ||
ethereumAddress: address, | ||
ethereumAddress: eip155, | ||
} | ||
@@ -85,5 +90,26 @@ | ||
controller: did, | ||
blockchainAccountId: `${address}@eip155:1`, | ||
blockchainAccountId: `${eip155}@eip155:1`, | ||
} | ||
const blockchainAddressCaip10 = { | ||
id: `${did}#keys-blockchain`, | ||
type: 'EcdsaSecp256k1RecoveryMethod2020', | ||
controller: did, | ||
blockchainAccountId: `eip155:1:${eip155}`, | ||
} | ||
const blockchainAddressBip122 = { | ||
id: `${did}#keys-blockchain`, | ||
type: 'EcdsaSecp256k1RecoveryMethod2020', | ||
controller: did, | ||
blockchainAccountId: `bip122:000000000019d6689c085ae165831e93:${bip122}`, | ||
} | ||
const blockchainAddressCosmos = { | ||
id: `${did}#keys-blockchain`, | ||
type: 'EcdsaSecp256k1RecoveryMethod2020', | ||
controller: did, | ||
blockchainAccountId: `cosmos:${cosmosPrefix}:${cosmos}`, | ||
} | ||
const compressedKey = { | ||
@@ -100,3 +126,3 @@ id: `${did}#keys-4`, | ||
controller: did, | ||
ethereumAddress: address, | ||
ethereumAddress: eip155, | ||
} | ||
@@ -232,2 +258,23 @@ | ||
it('validates signature produced by blockchainAccountId - CAIP 10 (EIP 155)', async () => { | ||
expect.assertions(1) | ||
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) | ||
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) | ||
return expect(verifier(parts[1], parts[2], [blockchainAddressCaip10])).toEqual(blockchainAddressCaip10) | ||
}) | ||
it('validates signature produced by blockchainAccountId - CAIP 10 (BIP 122)', async () => { | ||
expect.assertions(1) | ||
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) | ||
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) | ||
return expect(verifier(parts[1], parts[2], [blockchainAddressBip122])).toEqual(blockchainAddressBip122) | ||
}) | ||
it('validates signature produced by blockchainAccountId - CAIP 10 (Cosmos)', async () => { | ||
expect.assertions(1) | ||
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer }) | ||
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) | ||
return expect(verifier(parts[1], parts[2], [blockchainAddressCosmos])).toEqual(blockchainAddressCosmos) | ||
}) | ||
it('validates signature produced by EcdsaSecp256k1RecoveryMethod2020 - github #152', async () => { | ||
@@ -272,2 +319,23 @@ expect.assertions(1) | ||
it('validates signature with blockchainAccountId - CAIP 10 (EIP 155)', async () => { | ||
expect.assertions(1) | ||
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) | ||
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) | ||
return expect(verifier(parts[1], parts[2], [ecKey1, blockchainAddressCaip10])).toEqual(blockchainAddressCaip10) | ||
}) | ||
it('validates signature with blockchainAccountId - CAIP 10 (BIP 122)', async () => { | ||
expect.assertions(1) | ||
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) | ||
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) | ||
return expect(verifier(parts[1], parts[2], [ecKey1, blockchainAddressBip122])).toEqual(blockchainAddressBip122) | ||
}) | ||
it('validates signature with blockchainAccountId - CAIP 10 (COSMOS)', async () => { | ||
expect.assertions(1) | ||
const jwt = await createJWT({ bla: 'bla' }, { issuer: did, signer: recoverySigner, alg: 'ES256K-R' }) | ||
const parts = jwt.match(/^([a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/) | ||
return expect(verifier(parts[1], parts[2], [ecKey1, blockchainAddressCosmos])).toEqual(blockchainAddressCosmos) | ||
}) | ||
it('validates signature with EcdsaSecp256k1RecoveryMethod2020 - github #152', async () => { | ||
@@ -274,0 +342,0 @@ expect.assertions(1) |
@@ -7,2 +7,3 @@ import { ec as EC, SignatureInput } from 'elliptic' | ||
import { hexToBytes, base58ToBytes, base64ToBytes, bytesToHex, EcdsaSignature, stringToBytes } from './util' | ||
import { verifyBlockchainAccountId } from './blockchains' | ||
@@ -64,3 +65,3 @@ const secp256k1 = new EC('secp256k1') | ||
}) | ||
const ethAddressKeys = authenticators.filter(({ ethereumAddress, blockchainAccountId }) => { | ||
const blockchainAddressKeys = authenticators.filter(({ ethereumAddress, blockchainAccountId }) => { | ||
return typeof ethereumAddress !== 'undefined' || typeof blockchainAccountId !== undefined | ||
@@ -78,4 +79,4 @@ }) | ||
if (!signer && ethAddressKeys.length > 0) { | ||
signer = verifyRecoverableES256K(data, signature, ethAddressKeys) | ||
if (!signer && blockchainAddressKeys.length > 0) { | ||
signer = verifyRecoverableES256K(data, signature, blockchainAddressKeys) | ||
} | ||
@@ -117,3 +118,4 @@ | ||
pk.ethereumAddress?.toLowerCase() === recoveredAddress || | ||
pk.blockchainAccountId?.split('@eip155')?.[0].toLowerCase() === recoveredAddress | ||
pk.blockchainAccountId?.split('@eip155')?.[0].toLowerCase() === recoveredAddress || // CAIP-2 | ||
verifyBlockchainAccountId(recoveredPublicKeyHex, pk.blockchainAccountId) // CAIP-10 | ||
) | ||
@@ -120,0 +122,0 @@ }) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1302992
83
11953
12
+ Addedbech32@^2.0.0
+ Addedbech32@2.0.0(transitive)