cardano-crypto.js
Advanced tools
Comparing version 5.3.5 to 5.3.6-rc.0
383
index.js
@@ -0,80 +1,15 @@ | ||
const {validateBuffer, validateDerivationIndex, validateDerivationScheme, validateMnemonic, validateArray, validateString, validatePaperWalletMnemonic} = require("./utils/validation") | ||
const address = require("./address"); | ||
const crypto = require("./crypto-primitives") | ||
const bech32 = require("bech32") | ||
const bip39 = require('bip39') | ||
const cbor = require('borc') | ||
const Module = require('./lib.js') | ||
const crc32 = require('./utils/crc32') | ||
const base58 = require('./utils/base58') | ||
const scrypt = require('./utils/scrypt-async') | ||
const pbkdf2 = require('./utils/pbkdf2') | ||
const CborIndefiniteLengthArray = require('./utils/CborIndefiniteLengthArray') | ||
const HARDENED_THRESHOLD = 0x80000000 | ||
function validateDerivationScheme(input) { | ||
if (input !== 1 && input !== 2) { | ||
throw new Error('invalid derivation scheme!') | ||
} | ||
} | ||
function validateBuffer(input, expectedLength) { | ||
if (!Buffer.isBuffer(input)) { | ||
throw new Error('not buffer!') | ||
} | ||
if (expectedLength && input.length !== expectedLength) { | ||
throw new Error('Invalid buffer length') | ||
} | ||
} | ||
function validateArray(input) { | ||
if (typeof input !== typeof []) { | ||
throw new Error('not an array!') | ||
} | ||
} | ||
function validateDerivationIndex(input) { | ||
if (!Number.isInteger(input)) { | ||
throw new Error('invalid derivation index!') | ||
} | ||
} | ||
function validateString(input) { | ||
if (typeof input !== typeof 'aa') { | ||
throw new Error('not a string!') | ||
} | ||
} | ||
function validateMnemonic(input) { | ||
if (!bip39.validateMnemonic(input)) { | ||
const e = new Error('Invalid or unsupported mnemonic format:') | ||
e.name = 'InvalidArgumentException' | ||
throw e | ||
} | ||
} | ||
function validateMnemonicWords(input) { | ||
const wordlist = bip39.wordlists.EN | ||
const words = input.split(' ') | ||
const valid = words.reduce((result, word) => { | ||
return result && wordlist.indexOf(word) !== -1 | ||
}, true) | ||
if (!valid) { | ||
throw new Error('Invalid mnemonic words') | ||
} | ||
} | ||
function validatePaperWalletMnemonic(input) { | ||
validateMnemonicWords(input) | ||
const mnemonicLength = input.split(' ').length | ||
if (mnemonicLength !== 27) { | ||
throw Error( | ||
`Paper Wallet Mnemonic must be 27 words, got ${mnemonicLength} instead` | ||
) | ||
} | ||
} | ||
function cborEncodeBuffer(input) { | ||
@@ -166,4 +101,4 @@ validateBuffer(input) | ||
const entropy = Buffer.from(bip39.mnemonicToEntropy(mnemonic), 'hex') | ||
return cborEncodeBuffer(blake2b(cborEncodeBuffer(entropy), 32)) | ||
return cborEncodeBuffer(crypto.blake2b(cborEncodeBuffer(entropy), 32)) | ||
} | ||
@@ -175,3 +110,3 @@ | ||
try { | ||
const digest = hmac_sha512(seed, [Buffer.from(`Root Seed Chain ${i}`, 'ascii')]) | ||
const digest = crypto.hmac_sha512(seed, [Buffer.from(`Root Seed Chain ${i}`, 'ascii')]) | ||
const tempSeed = digest.slice(0, 32) | ||
@@ -326,83 +261,2 @@ const chainCode = digest.slice(32, 64) | ||
function blake2b(input, outputLen) { | ||
validateBuffer(input) | ||
const inputLen = input.length | ||
const inputArrPtr = Module._malloc(inputLen) | ||
const inputArr = new Uint8Array(Module.HEAPU8.buffer, inputArrPtr, inputLen) | ||
const outputArrPtr = Module._malloc(outputLen) | ||
const outputArr = new Uint8Array(Module.HEAPU8.buffer, outputArrPtr, outputLen) | ||
inputArr.set(input) | ||
Module._emscripten_blake2b(inputArrPtr, inputLen, outputArrPtr, outputLen) | ||
Module._free(inputArrPtr) | ||
Module._free(outputArrPtr) | ||
return Buffer.from(outputArr) | ||
} | ||
function sha3_256(input) { | ||
validateBuffer(input) | ||
const inputLen = input.length | ||
const inputArrPtr = Module._malloc(inputLen) | ||
const inputArr = new Uint8Array(Module.HEAPU8.buffer, inputArrPtr, inputLen) | ||
const outputLen = 32 | ||
const outputArrPtr = Module._malloc(outputLen) | ||
const outputArr = new Uint8Array(Module.HEAPU8.buffer, outputArrPtr, outputLen) | ||
inputArr.set(input) | ||
Module._emscripten_sha3_256(inputArrPtr, inputLen, outputArrPtr) | ||
Module._free(inputArrPtr) | ||
Module._free(outputArrPtr) | ||
return Buffer.from(outputArr) | ||
} | ||
function hmac_sha512(initKey, inputs) { | ||
validateBuffer(initKey) | ||
validateArray(inputs) | ||
inputs.map(validateBuffer) | ||
const ctxLen = Module._emscripten_size_of_hmac_sha512_ctx() | ||
const ctxArrPtr = Module._malloc(ctxLen) | ||
const ctxArr = new Uint8Array(Module.HEAPU8.buffer, ctxArrPtr, ctxLen) | ||
const initKeyLen = initKey.length | ||
const initKeyArrPtr = Module._malloc(initKeyLen) | ||
const initKeyArr = new Uint8Array(Module.HEAPU8.buffer, initKeyArrPtr, initKeyLen) | ||
const outputLen = 64 | ||
const outputArrPtr = Module._malloc(outputLen) | ||
const outputArr = new Uint8Array(Module.HEAPU8.buffer, outputArrPtr, outputLen) | ||
initKeyArr.set(initKey) | ||
Module._emscripten_hmac_sha512_init(ctxArrPtr, initKeyArrPtr, initKeyLen) | ||
for (let i = 0; i < inputs.length; i++) { | ||
const inputLen = inputs[i].length | ||
const inputArrPtr = Module._malloc(inputLen) | ||
const inputArr = new Uint8Array(Module.HEAPU8.buffer, inputArrPtr, inputLen) | ||
inputArr.set(inputs[i]) | ||
Module._emscripten_hmac_sha512_update(ctxArrPtr, inputArrPtr, inputLen) | ||
Module._free(inputArrPtr) | ||
} | ||
Module._emscripten_hmac_sha512_final(ctxArrPtr, outputArrPtr) | ||
Module._free(initKeyArrPtr) | ||
Module._free(ctxArrPtr) | ||
Module._free(outputArrPtr) | ||
return Buffer.from(outputArr) | ||
} | ||
function cardanoMemoryCombine(input, password) { | ||
@@ -416,3 +270,3 @@ validateString(password) | ||
const transformedPassword = blake2b(Buffer.from(password, 'utf-8'), 32) | ||
const transformedPassword = crypto.blake2b(Buffer.from(password, 'utf-8'), 32) | ||
const transformedPasswordLen = transformedPassword.length | ||
@@ -441,91 +295,2 @@ const transformedPasswordArrPtr = Module._malloc(transformedPasswordLen) | ||
function chacha20poly1305Encrypt(input, key, nonce) { | ||
validateBuffer(input) | ||
validateBuffer(key, 32) | ||
validateBuffer(nonce, 12) | ||
const inputLen = input.length | ||
const inputArrPtr = Module._malloc(inputLen) | ||
const inputArr = new Uint8Array(Module.HEAPU8.buffer, inputArrPtr, inputLen) | ||
const keyLen = key.length | ||
const keyArrPtr = Module._malloc(keyLen) | ||
const keyArr = new Uint8Array(Module.HEAPU8.buffer, keyArrPtr, keyLen) | ||
const nonceLen = nonce.length | ||
const nonceArrPtr = Module._malloc(nonceLen) | ||
const nonceArr = new Uint8Array(Module.HEAPU8.buffer, nonceArrPtr, nonceLen) | ||
const tagLen = 16 | ||
const outputLen = inputLen + tagLen | ||
const outputArrPtr = Module._malloc(outputLen) | ||
const outputArr = new Uint8Array(Module.HEAPU8.buffer, outputArrPtr, outputLen) | ||
inputArr.set(input) | ||
keyArr.set(key) | ||
nonceArr.set(nonce) | ||
const resultCode = Module._emscripten_chacha20poly1305_enc(keyArrPtr, nonceArrPtr, inputArrPtr, inputLen, outputArrPtr, outputArrPtr + inputLen, tagLen, 1) | ||
Module._free(inputArrPtr) | ||
Module._free(keyArrPtr) | ||
Module._free(nonceArrPtr) | ||
Module._free(outputArrPtr) | ||
if (resultCode !== 0) { | ||
throw Error('chacha20poly1305 encryption has failed!') | ||
} | ||
return Buffer.from(outputArr) | ||
} | ||
function chacha20poly1305Decrypt(input, key, nonce) { | ||
validateBuffer(input) | ||
validateBuffer(key, 32) | ||
validateBuffer(nonce, 12) | ||
// extract tag from input | ||
const tagLen = 16 | ||
const tag = input.slice(input.length - tagLen, input.length) | ||
input = input.slice(0, input.length - tagLen) | ||
const inputLen = input.length | ||
const inputArrPtr = Module._malloc(inputLen) | ||
const inputArr = new Uint8Array(Module.HEAPU8.buffer, inputArrPtr, inputLen) | ||
const tagArrPtr = Module._malloc(tagLen) | ||
const tagArr = new Uint8Array(Module.HEAPU8.buffer, tagArrPtr, tagLen) | ||
const keyLen = key.length | ||
const keyArrPtr = Module._malloc(keyLen) | ||
const keyArr = new Uint8Array(Module.HEAPU8.buffer, keyArrPtr, keyLen) | ||
const nonceLen = nonce.length | ||
const nonceArrPtr = Module._malloc(nonceLen) | ||
const nonceArr = new Uint8Array(Module.HEAPU8.buffer, nonceArrPtr, nonceLen) | ||
const outputLen = inputLen | ||
const outputArrPtr = Module._malloc(outputLen) | ||
const outputArr = new Uint8Array(Module.HEAPU8.buffer, outputArrPtr, outputLen) | ||
inputArr.set(input) | ||
tagArr.set(tag) | ||
keyArr.set(key) | ||
nonceArr.set(nonce) | ||
const resultCode = Module._emscripten_chacha20poly1305_enc(keyArrPtr, nonceArrPtr, inputArrPtr, inputLen, outputArrPtr, tagArrPtr, tagLen, 0) | ||
Module._free(inputArrPtr) | ||
Module._free(keyArrPtr) | ||
Module._free(nonceArrPtr) | ||
Module._free(outputArrPtr) | ||
Module._free(tagArrPtr) | ||
if (resultCode !== 0) { | ||
throw Error('chacha20poly1305 decryption has failed!') | ||
} | ||
return Buffer.from(outputArr) | ||
} | ||
async function decodePaperWalletMnemonic(paperWalletMnemonic) { | ||
@@ -575,111 +340,19 @@ validatePaperWalletMnemonic(paperWalletMnemonic) | ||
return pbkdf2(xpub, 'address-hashing', 500, 32, 'sha512') | ||
return pbkdf2(xpub, 'address-hashing', 500, 32, 'sha512') | ||
} | ||
function packAddress(derivationPath, xpub, hdPassphrase, derivationScheme) { | ||
validateBuffer(xpub, 64) | ||
validateDerivationScheme(derivationScheme) | ||
if (derivationScheme === 1) { | ||
validateArray(derivationPath) | ||
validateBuffer(hdPassphrase, 32) | ||
} | ||
let addressPayload, addressAttributes | ||
if (derivationScheme === 1 && derivationPath.length > 0) { | ||
addressPayload = encryptDerivationPath(derivationPath, hdPassphrase) | ||
addressAttributes = new Map([[1, cbor.encode(addressPayload)]]) | ||
} else { | ||
addressPayload = Buffer.from([]) | ||
addressAttributes = new Map() | ||
} | ||
const addressRoot = getAddressHash([ | ||
0, | ||
[0, xpub], | ||
addressPayload.length > 0 ? new Map([[1, cbor.encode(addressPayload)]]) : new Map(), | ||
]) | ||
const addressType = 0 // Public key address | ||
const addressData = [addressRoot, addressAttributes, addressType] | ||
const addressDataEncoded = cbor.encode(addressData) | ||
return base58.encode( | ||
cbor.encode([new cbor.Tagged(24, addressDataEncoded), crc32(addressDataEncoded)]) | ||
) | ||
function bech32Encode(prefix, data) { | ||
const words = bech32.toWords(data) | ||
// we need longer than default length for privkeys and 1000 should suffice | ||
return bech32.encode(prefix, words, 1000) | ||
} | ||
function unpackAddress(address, hdPassphrase) { | ||
// we decode the address from the base58 string | ||
// and then we strip the 24 CBOR data tags (the "[0].value" part) | ||
const addressAsBuffer = cbor.decode(base58.decode(address))[0].value | ||
const addressData = cbor.decode(addressAsBuffer) | ||
const attributes = addressData[1] | ||
const payload = cbor.decode(attributes.get(1)) | ||
let derivationPath | ||
try { | ||
derivationPath = decryptDerivationPath(payload, hdPassphrase) | ||
} catch (e) { | ||
throw new Error('Unable to get derivation path from address') | ||
} | ||
if (derivationPath && derivationPath.length > 2) { | ||
throw Error('Invalid derivation path length, should be at most 2') | ||
} | ||
function bech32Decode(str){ | ||
const tmp = bech32.decode(str, 1000) | ||
return { | ||
derivationPath, | ||
prefix: tmp.prefix, | ||
data: Buffer.from(bech32.fromWords(tmp.words)), | ||
} | ||
} | ||
function isValidAddress(address) { | ||
try { | ||
// we decode the address from the base58 string | ||
const addressAsArray = cbor.decode(base58.decode(address)) | ||
// we strip the 24 CBOR data taga by taking the "value" attribute from the "Tagged" object | ||
const addressDataEncoded = addressAsArray[0].value | ||
const crc32Checksum = addressAsArray[1] | ||
if (crc32Checksum !== crc32(addressDataEncoded)) { | ||
return false | ||
} | ||
} catch (e) { | ||
return false | ||
} | ||
return true | ||
} | ||
function getAddressHash(input) { | ||
// eslint-disable-next-line camelcase | ||
const firstHash = sha3_256(cbor.encode(input)) | ||
return blake2b(firstHash, 28) | ||
} | ||
function encryptDerivationPath(derivationPath, hdPassphrase) { | ||
const serializedDerivationPath = cbor.encode(new CborIndefiniteLengthArray(derivationPath)) | ||
return chacha20poly1305Encrypt( | ||
serializedDerivationPath, | ||
hdPassphrase, | ||
Buffer.from('serokellfore') | ||
) | ||
} | ||
function decryptDerivationPath(addressPayload, hdPassphrase) { | ||
const decipheredDerivationPath = chacha20poly1305Decrypt( | ||
addressPayload, | ||
hdPassphrase, | ||
Buffer.from('serokellfore') | ||
) | ||
try { | ||
return cbor.decode(Buffer.from(decipheredDerivationPath)) | ||
} catch (err) { | ||
throw new Error('incorrect address or passphrase') | ||
} | ||
} | ||
module.exports = { | ||
@@ -693,8 +366,14 @@ derivePublic, | ||
xpubToHdPassphrase, | ||
packAddress, | ||
unpackAddress, | ||
isValidAddress, | ||
packBootstrapAddress: address.packBootstrapAddress, | ||
packBaseAddress: address.packBaseAddress, | ||
packPointerAddress: address.packPointerAddress, | ||
packEnterpriseAddress: address.packEnterpriseAddress, | ||
getAddressInfo: address.getAddressInfo, | ||
unpackAddress: address.unpackAddress, | ||
isValidAddress: address.isValidAddress, | ||
cardanoMemoryCombine, | ||
blake2b, | ||
blake2b: crypto.blake2b, | ||
base58, | ||
bech32_encode: bech32Encode, | ||
bech32_decode: bech32Decode, | ||
scrypt, | ||
@@ -706,5 +385,5 @@ toPublic, | ||
_seedToKeypairV2: seedToKeypairV2, | ||
_sha3_256: sha3_256, | ||
_chacha20poly1305Decrypt: chacha20poly1305Decrypt, | ||
_chacha20poly1305Encrypt: chacha20poly1305Encrypt, | ||
_sha3_256: crypto.sha3_256, | ||
_chacha20poly1305Decrypt: crypto.chacha20poly1305Decrypt, | ||
_chacha20poly1305Encrypt: crypto.chacha20poly1305Encrypt, | ||
} |
{ | ||
"name": "cardano-crypto.js", | ||
"version": "5.3.5", | ||
"version": "5.3.6-rc.0", | ||
"description": "input-output-hk/cardano-crypto compiled to pure javascript using Emscripten", | ||
@@ -29,2 +29,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"bech32": "^1.1.4", | ||
"bip39": "^3.0.2", | ||
@@ -31,0 +32,0 @@ "borc": "^2.1.1", |
@@ -51,3 +51,7 @@ # cardano-crypto.js | ||
* `Buffer xpubToHdPassphrase(Buffer xpub)` | ||
* `string packAddress(Array[int] derivationPath, Buffer xpub, Buffer hdPassphrase, int derivationScheme)` | ||
* `Buffer packBootstrapAddress(Array[int] derivationPath, Buffer xpub, Buffer hdPassphrase, int derivationScheme)` | ||
* `Buffer packBaseAddress(Buffer pubKey, Buffer stakePubKey, int addressTypeId, int networkId, Bool isStakeHash = false)` | ||
* `Buffer packPointerAddress(Buffer pubKey, Object pointer, int addressTypeId, int networkId)` | ||
* `Buffer packEnterpriseAddress(Buffer pubKey, int addressTypeId, int networkId)` | ||
* `Object getAddressInfo(Buffer address)` | ||
* `string unpackAddress(string address, Buffer hdPassphrase)` | ||
@@ -57,2 +61,4 @@ * `Bool isValidAddress(string address)` | ||
* `Buffer cardanoMemoryCombine(Buffer input, String password)` | ||
* `string bech32Encode(string prefix, Buffer data)` | ||
* `Object bech32Decode(string address)` | ||
* `[base58](https://www.npmjs.com/package/base58)` | ||
@@ -59,0 +65,0 @@ * `[scrypt](https://www.npmjs.com/package/scrypt-async)` |
@@ -244,3 +244,3 @@ const test = require('tape') | ||
t.equals( | ||
lib.packAddress( | ||
lib.base58.encode(lib.packBootstrapAddress( | ||
derivationPath, | ||
@@ -250,3 +250,3 @@ sampleExtendedPublicKey, | ||
1 | ||
), | ||
)), | ||
expectedV1Address, | ||
@@ -264,3 +264,3 @@ 'should properly pack V1 address' | ||
t.equals( | ||
lib.packAddress( | ||
lib.base58.encode(lib.packBootstrapAddress( | ||
derivationPath, | ||
@@ -270,3 +270,3 @@ sampleExtendedPublicKey, | ||
2 | ||
), | ||
)), | ||
'Ae2tdPwUPEZCxt4UV1Uj2AMMRvg5pYPypqZowVptz3GYpK4pkcvn3EjkuNH', | ||
@@ -277,2 +277,59 @@ 'should properly pack V2 address' | ||
test('shelley addresses', (t) => { | ||
t.plan(5) | ||
let pubKeyCardanoTestvector = Buffer.from('73fea80d424276ad0978d4fe5310e8bc2d485f5f6bb3bf87612989f112ad5a7d', 'hex') | ||
let stakeKey = Buffer.from('2c041c9c6a676ac54d25e2fdce44c56581e316ae43adc4c7bf17f23214d8d892', 'hex') | ||
t.equals( | ||
lib.packBaseAddress(pubKeyCardanoTestvector, stakeKey, 0, 3).toString('hex'), | ||
'039493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc', | ||
'should properly derive base address' | ||
) | ||
let pubKeyTrezorTestvector = Buffer.from('7dbfba606303655d426f55cca9f77b1b9e2bca0ae69ca2ba7749d7bfc5303260', 'hex') | ||
let stakeHash = Buffer.from('122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277', 'hex') | ||
t.equals( | ||
lib.packBaseAddress(pubKeyTrezorTestvector, stakeHash, 0, 3, true).toString('hex'), | ||
'03eb0baa5e570cffbe2934db29df0b6a3d7c0430ee65d4c3a7ab2fefb9122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277', | ||
'should properly derive base address with unowned staking key hash' | ||
) | ||
t.equals( | ||
lib.packEnterpriseAddress(pubKeyCardanoTestvector, 6, 0).toString('hex'), | ||
'609493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e', | ||
'should properly derive enterprise address' | ||
) | ||
let pointer = {blockIndex: 24157, txIndex: 177, certificateIndex: 42} | ||
t.equals( | ||
lib.packPointerAddress(pubKeyCardanoTestvector, pointer, 4, 3).toString('hex'), | ||
'439493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e81bc5d81312a', | ||
'should properly derive pointer address' | ||
) | ||
t.equals( | ||
JSON.stringify(lib.getAddressInfo( | ||
Buffer.from('439493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e81bc5d81312a', 'hex') | ||
)), | ||
JSON.stringify({ | ||
addressType: 4, networkId: 3 | ||
}), | ||
'should properly decode address header' | ||
) | ||
}) | ||
test('bech32', (t) => { | ||
t.plan(2) | ||
let addressBech = 'addr1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwqcyl47r' | ||
let addressBuffer = Buffer.from('009493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc', 'hex') | ||
t.equals( | ||
lib.bech32_encode('addr', addressBuffer), | ||
addressBech, | ||
'should properly encode bech32 address' | ||
) | ||
t.equals( | ||
lib.bech32_decode(addressBech) | ||
.data.toString('hex'), | ||
addressBuffer.toString('hex'), | ||
"should properly decode bech32 address" | ||
) | ||
}) | ||
test('proper error handling by the library', (t) => { | ||
@@ -279,0 +336,0 @@ // to avoid accidentally injecting unhandledRejection handler with Emscripten |
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
289092
14
2507
128
4
2
+ Addedbech32@^1.1.4
+ Addedbech32@1.1.4(transitive)