UNPKG

bifcore-sdk-nodejs-bop

Version:
611 lines (568 loc) 18 kB
'use strict' const nacl = require('./vendor/nacl') const keystore = require('./vendor/keystore') const crypto_sm2 = require('./crypto_sm2') const sjcl = require('brdc-sjcl') const bip39 = require('bip39') const bip32 = require('bip32') // const is = require('is-type-of') const util = require('../common/util') const {boolean} = require('is-type-of') const {Slip10Curve, deriveKeyByPath} = require("bitfactory-js-hd-key"); const WORDLISTS = { english: require('bip39/src/wordlists/english.json'), chinese_simplified: require('bip39/src/wordlists/chinese_simplified.json') } const CRYPTO_SM2 = 0x7a const CRYPTO_ED25519 = 0x65 /** * Get sha256 hash string * * @param {String} bytes * @returns {String} * @private */ function _sha256 (bytes) { return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes))) } /** * Create privateKey * * @param {Array} rawPrivateKey * @returns {String} output privateKey * @private */ function _createPrivateKey (rawPrivateKey) { const prefix = [0x18, 0x9E, 0x99] const version = [0x65, 0x66] const head = sjcl.bitArray.concat(prefix, version) let mix = sjcl.bitArray.concat(head, rawPrivateKey) return sjcl.codec.base58.encode(mix) } /** * Create publicKey * * @param {Array} rawPublicKey * @returns {String} output publicKey * @private */ function _createPublicKey (rawPublicKey) { const prefix = [0xb0] const version = [0x65, 0x66] const head = sjcl.bitArray.concat(prefix, version) const mix = sjcl.bitArray.concat(head, rawPublicKey) return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(mix)) } /** * Create address * * @param {Array} rawPublicKey * @returns {String} output address * @private */ function _createAddress (rawPublicKey, chainCode) { let mix = _sha256(rawPublicKey).slice(10) let address = sjcl.codec.base58.encode(mix) if (chainCode) { return `did:bid:${chainCode}:ef${address}` } return `did:bid:ef${address}` } function getCryptoTypeFromPrivKey (key) { const decodedKey = sjcl.codec.base58.decode(key.trim()) return decodedKey[3] } function getCryptoTypeFromPubKey (key) { const decodedKey = sjcl.codec.bytes.fromBits( sjcl.codec.hex.toBits(key.trim())) return decodedKey[1] } /** * Parse privateKey, get rawPrivateKey * * @param {String} privateKey * @returns {String} rawPrivateKey * @public */ function parsePrivateKey (privateKey) { if (!isPrivateKey(privateKey)) { throw new Error('invalid privateKey') } const decodedKey = sjcl.codec.base58.decode(privateKey) return decodedKey.slice(5, decodedKey.length) } /** * Parse publicKey, get rawPublicKey * * @param {String} publicKey * @returns {String} rawPublicKey * @public */ function parsePublicKey (publicKey) { if (!isPublicKey(publicKey)) { throw new Error('invalid publicKey') } const keyBytes = sjcl.codec.bytes.fromBits( sjcl.codec.hex.toBits(publicKey) ) return keyBytes.slice(3, keyBytes.length) } /** * Create a single account, include privateKey publicKey and address * * @returns {Object} * @public */ function generate (chainCode) { const srcKeyPair = nacl.sign.keyPair() const seed = srcKeyPair.publicKey const keyPair = nacl.sign.keyPair.fromSeed(seed) const rawPrivateKey = Array.from(seed) const rawPublicKey = Array.from(keyPair.publicKey) // create privateKey const encPrivateKey = _createPrivateKey(rawPrivateKey) // create publicKey const encPublicKey = _createPublicKey(rawPublicKey) // create address const encAddress = _createAddress(rawPublicKey, chainCode) return { encPrivateKey, encPublicKey, encAddress, rawPrivateKey, rawPublicKey } } /** * Returns an account address, private and public key * * @param {String} privateKey * @public */ function generateByKey (encPrivateKey) { if (!isPrivateKey(encPrivateKey)) { throw new Error('invalid privateKey') } if (getCryptoTypeFromPrivKey(encPrivateKey) === CRYPTO_SM2) { return crypto_sm2.privateToAccount(encPrivateKey) } if (getCryptoTypeFromPrivKey(encPrivateKey) === CRYPTO_ED25519) { const encPublicKey = getEncPublicKey(encPrivateKey) const encAddress = publicToAddress(encPublicKey) const rawPrivateKey = parsePrivateKey(encPrivateKey) const rawPublicKey = parsePublicKey(encPublicKey) return { encPrivateKey, encPublicKey, encAddress, rawPrivateKey, rawPublicKey } } throw new Error('invalid privateKey') } /** * Returns the publicKey * * @param {String} privateKey * @returns {String} publicKey * @public */ function getEncPublicKey (privateKey) { if (!isPrivateKey(privateKey)) { throw new Error('invalid privateKey') } if (getCryptoTypeFromPrivKey(privateKey) === CRYPTO_SM2) { return crypto_sm2.privateToPublic(privateKey) } if (getCryptoTypeFromPrivKey(privateKey) === CRYPTO_ED25519) { const rawPrivateKey = parsePrivateKey(privateKey) const keyPair = nacl.sign.keyPair.fromSeed(rawPrivateKey) const rawPublicKey = Array.from(keyPair.publicKey) return _createPublicKey(rawPublicKey) } throw new Error('invalid privateKey') } function getEncPublicKeyByRaw (rawPublicKey, type) { if (!Array.isArray(rawPublicKey)) { throw new Error('invalid rawPublicKey') } if (type === CRYPTO_SM2) { return crypto_sm2._createPublicKey(rawPublicKey) } if (type === CRYPTO_ED25519) { return _createPublicKey(rawPublicKey) } throw new Error('invalid type') } function getEncPrivateKeyByRaw (rawPrivateKey, type) { if (!Array.isArray(rawPrivateKey)) { throw new Error('invalid rawPrivateKey') } if (type === CRYPTO_SM2) { return crypto_sm2._createPrivateKey(rawPrivateKey) } if (type === CRYPTO_ED25519) { return _createPrivateKey(rawPrivateKey) } throw new Error('invalid type') } /** * Returns the address * * @param {String} publicKey * @returns {String} * @public */ function publicToAddress (publicKey) { if (!isPublicKey(publicKey)) { throw new Error('invalid publicKey') } if (getCryptoTypeFromPubKey(publicKey) === CRYPTO_SM2) { return crypto_sm2.publicToAddress(publicKey) } if (getCryptoTypeFromPubKey(publicKey) === CRYPTO_ED25519) { const rawPublicKey = parsePublicKey(publicKey) return _createAddress(rawPublicKey) } throw new Error('invalid publicKey') } function isCryptoTagValid (asym, encode) { if (asym === CRYPTO_ED25519 && encode === 0x66) { return true } if (asym === CRYPTO_SM2 && encode === 0x66) { return true } } /** * Checks if a given string is a valid privateKey * * @param {String} key * @returns {Boolean} * @public */ function isPrivateKey (key) { try { if (!key || (typeof key !== 'string')) { return false } const decodedKey = sjcl.codec.base58.decode(key.trim()) if ( decodedKey[0] !== 0x18 || decodedKey[1] !== 0x9e || decodedKey[2] !== 0x99 ) { return false } if (!isCryptoTagValid(decodedKey[3], decodedKey[4])) { return false } return (true) } catch (err) { return false } } /** * Checks if a given string is a valid publicKey * * @param {String} key * @returns {Boolean} * @public */ function isPublicKey (key) { try { if (!key || typeof key !== 'string') { return false } const keyBytes = sjcl.codec.bytes.fromBits( sjcl.codec.hex.toBits(key.trim()) ) if ( keyBytes[0] !== 0xb0 || !isCryptoTagValid(keyBytes[1], keyBytes[2]) // keyBytes[1] > 4 || // keyBytes[1] < 1 ) { return false } return (true) } catch (err) { return false } } /** * Checks if a given string is a valid address * * @param {String} address * @returns {Boolean} * @public */ function isAddress (address) { try { if (!address || typeof address !== 'string') { return false } const startsWithDidBid = address.startsWith('did:bid:') if (!startsWithDidBid) { return false } const items = address.split(':') if (items.length !== 3 && items.length !== 4) { return false } if (items.length === 3) { address = items[2] } else { address = items[3] } const prifx = address.substring(0, 2) if (prifx !== 'ef' && prifx !== 'zf') { return false } let addr = address.substring(2, address.length) let base58_address = [] base58_address = sjcl.codec.base58.decode(addr) if (base58_address.length != 22) { return false } return (true) } catch (err) { return false } } function generateKeyStore (privatekey, password) { let regular = /[u4e00-u9fa5]/ if (!isPrivateKey(privatekey)) { throw new Error('invalid privatekey') } if (!util._isString(password) || !regular.test(password)) { throw new Error('invalid password') } let respData = keystore.create(privatekey, password) respData.address = generateByKey(privatekey).address return respData } function decipherKeyStore (keystoreContent, password) { let regular = /[u4e00-u9fa5]/ try { if (typeof keystoreContent === 'string') { keystoreContent = JSON.parse(keystoreContent) } } catch (e) { throw new Error('invalid keystoreContent') } if (!util._isString(password) || !regular.test(password)) { throw new Error('invalid password') } const address = keystore.imported(keystoreContent, password) if (address == null) { throw new Error('The password was wrong') } return keystore.imported(keystoreContent, password) } function _bytesToHex (bytes) { for (var hex = [], i = 0; i < bytes.length; i++) { hex.push((bytes[i] >>> 4).toString(16)) hex.push((bytes[i] & 0xF).toString(16)) } return hex.join('') } /** * generate child key from master key * * @param {String} privatekey * @param {String} chainCode * @param {String} serviceType * @param {Number} index * @returns {object} * @public */ function generateChild (privatekey, chainCode, serviceType, index) { if (getCryptoTypeFromPrivKey(privatekey) === CRYPTO_SM2) { return crypto_sm2.generateChild(parsePrivateKey(privatekey), chainCode, serviceType, index) } let rawPrivatekey = parsePrivateKey(privatekey) let path = '/0/' + chainCode + '/' + serviceType + '/' + index.toString() let seed = sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(_bytesToHex(rawPrivatekey) + path)) const keyPair = nacl.sign.keyPair.fromSeed(seed) const rawPrivateKey = Array.from(seed) const rawPublicKey = Array.from(keyPair.publicKey) // create privateKey const privateKey = _createPrivateKey(rawPrivateKey) // create publicKey const publicKey = _createPublicKey(rawPublicKey) // create address const address = _createAddress(rawPublicKey, chainCode) return { privateKey, publicKey, address, path } } /** * generate mnemonic codes * * @param {Buffer | String} entropy * @returns {String} * @public */ function generateMnemonicCode (entropy) { let word = WORDLISTS.english if (!util._isString(entropy)) { throw new Error('invalid entropy') } return bip39.entropyToMnemonic(entropy, word) } /** * generate private key from mnemonic codes * * @param {String} mnemonics * @param {String} hdPaths, format is "m/44'/526'/1'/0/0" * @returns {String} * @public */ function privateKeyFromMnemonicCode (mnemonics, hdPaths) { if (!util._isString(mnemonics)) { throw new Error('The size of mnemonicCodes must be bigger than or equal to 0') } if (!util._isString(hdPaths)) { throw new Error('The size of hdPaths must be bigger than or equal to 0') } const seed = bip39.mnemonicToSeedSync(mnemonics, ''); let derivePrivateKeyRaw = deriveKeyByPath(Slip10Curve.ED25519, hdPaths, seed.toString('hex')); return _createPrivateKey(Array.from(Buffer.from(derivePrivateKeyRaw.key, 'hex'))) } function privKeyFromMCodeAndCrypto (type, mnemonics, hdPaths) { const seed = bip39.mnemonicToSeedSync(mnemonics, ''); let derivePrivateKeyRaw = null; if (!util._isString(mnemonics)) { throw new Error('The size of mnemonicCodes must be bigger than or equal to 0') } if (!util._isString(hdPaths)) { throw new Error('The size of hdPaths must be bigger than or equal to 0') } if (type === CRYPTO_ED25519) { derivePrivateKeyRaw = deriveKeyByPath(Slip10Curve.ED25519, hdPaths, seed.toString('hex')); return _createPrivateKey(Array.from(Buffer.from(derivePrivateKeyRaw.key, 'hex'))) } else if (type === CRYPTO_SM2) { derivePrivateKeyRaw = deriveKeyByPath(Slip10Curve.SM2, hdPaths, seed.toString('hex')); return crypto_sm2._createPrivateKey(Array.from(Buffer.from(derivePrivateKeyRaw.key, 'hex'))) } throw new Error('invalid type') } function sign (message, privateKey) { if (!isPrivateKey(privateKey)) { throw new Error('invalid privateKey') } if (!util._isString(message)) { throw new Error('invalid message') } if (getCryptoTypeFromPrivKey(privateKey) === CRYPTO_SM2) { return crypto_sm2.sign(message, privateKey) } if (getCryptoTypeFromPrivKey(privateKey) === CRYPTO_ED25519) { if (typeof message === 'number') { message = message + '' } const buffer = Buffer.from(message, 'hex') const rawPrivateKey = parsePrivateKey(privateKey) let keyPair = nacl.sign.keyPair.fromSeed(rawPrivateKey) let signBytes = nacl.sign.detached(buffer, keyPair.secretKey) return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(signBytes)) } throw new Error('invalid privateKey') } /** * Verify the signature * * @param {String} message * @param {String} signature * @param {String} publicKey * @returns {Boolean} */ function verify (message, signature, publicKey) { if (!isPublicKey(publicKey)) { throw new Error('invalid publicKey') } if (getCryptoTypeFromPubKey(publicKey) === CRYPTO_SM2) { return crypto_sm2.verify(message, signature, publicKey) } if (typeof message === 'number') { message = message + '' } if (typeof signature !== 'string') { throw new TypeError('signature must be a string') } const rawPublicKey = parsePublicKey(publicKey) const signatureBytes = sjcl.codec.bytes.fromBits( sjcl.codec.hex.toBits(signature) ) const buffer = Buffer.from(message, 'hex') return nacl.sign.detached.verify( buffer, signatureBytes, rawPublicKey ) } function getBidAndKeyPairBySM2 (chainCode) { return crypto_sm2.generate(chainCode) } function getBidAndKeyPair (chainCode) { return this.generate(chainCode) } function privateKeyManager (type, chainCode) { if (type === CRYPTO_ED25519) { return generate(chainCode) } if (type === CRYPTO_SM2) { return crypto_sm2.generate(chainCode) } throw new Error('invalid type') } function privateKeyManagerByKey (privateKey) { if (!isPrivateKey(privateKey)) { throw new Error('invalid privateKey') } if (getCryptoTypeFromPrivKey(privateKey) === CRYPTO_ED25519) { return generateByKey(privateKey) } if (getCryptoTypeFromPrivKey(privateKey) === CRYPTO_SM2) { return crypto_sm2.privateToAccount(privateKey) } throw new Error('invalid privateKey') } function publicKeyManager (publicKey) { if (!isPublicKey(publicKey)) { throw new Error('invalid publicKey') } if (getCryptoTypeFromPubKey(publicKey) === CRYPTO_SM2) { return crypto_sm2.generateByPub(publicKey) } if (getCryptoTypeFromPubKey(publicKey) === CRYPTO_ED25519) { return generateByPub(publicKey) } throw new Error('invalid publicKey') } function generateByPub (encPublicKey) { const encAddress = publicToAddress(encPublicKey) const rawPublicKey = parsePublicKey(encPublicKey) return { encAddress, rawPublicKey } } module.exports = { generate, getBidAndKeyPairBySM2, getBidAndKeyPair, privateKeyManager, privateKeyManagerByKey, publicKeyManager, getEncPublicKey, getEncPublicKeyByRaw, getEncPrivateKeyByRaw, parsePrivateKey, parsePublicKey, isPrivateKey, isPublicKey, isAddress, generateKeyStore, decipherKeyStore, generateChild, privKeyFromMCodeAndCrypto, generateMnemonicCode, privateKeyFromMnemonicCode, sign, verify, getCryptoTypeFromPrivKey, getCryptoTypeFromPubKey } module.exports.CRYPTO_SM2 = CRYPTO_SM2 module.exports.CRYPTO_ED25519 = CRYPTO_ED25519