重命名旧的默克根计算函数

This commit is contained in:
徐稼生 2018-12-28 15:35:35 +00:00
parent e968ea686f
commit 15c35c803f

964
index.js
View File

@ -1,483 +1,483 @@
const BigNumber=require('bignumber.js') // https://github.com/MikeMcl/bignumber.js 几个库的比较: node-bignum: 使用到openssl在windows上需要下载二进制包有时下载失败。bigi: 不错。 bignumber.js不错。 const BigNumber=require('bignumber.js') // https://github.com/MikeMcl/bignumber.js 几个库的比较: node-bignum: 使用到openssl在windows上需要下载二进制包有时下载失败。bigi: 不错。 bignumber.js不错。
const crypto=require('crypto') const crypto=require('crypto')
const nacl = require('tweetnacl') const nacl = require('tweetnacl')
const bs58check = require('bs58check') const bs58check = require('bs58check')
const uuid = require('uuid') const uuid = require('uuid')
const Secword = require('bitcore-mnemonic') // https://bitcore.io/api/mnemonic/ https://github.com/bitpay/bitcore-mnemonic const Secword = require('bitcore-mnemonic') // https://bitcore.io/api/mnemonic/ https://github.com/bitpay/bitcore-mnemonic
// const bip39 = require('bip39') // https://github.com/bitcoinjs/bip39 // 有更多语言,但不方便选择语言,也不能使用 pass // const bip39 = require('bip39') // https://github.com/bitcoinjs/bip39 // 有更多语言,但不方便选择语言,也不能使用 pass
// const HDKey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能 // const HDKey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
// 全部以hex为默认输入输出格式方便人的阅读以及方便函数之间统一接口 // 全部以hex为默认输入输出格式方便人的阅读以及方便函数之间统一接口
const my={} const my={}
my.HASHER='sha256' // 默认的哈希算法。could be md5, sha1, sha256, sha512, ripemd160。 可用 Crypto.getHashes/Ciphers/Curves() 查看支持的种类。 my.HASHER='sha256' // 默认的哈希算法。could be md5, sha1, sha256, sha512, ripemd160。 可用 Crypto.getHashes/Ciphers/Curves() 查看支持的种类。
my.HASHER_LIST=crypto.getHashes() my.HASHER_LIST=crypto.getHashes()
my.CIPHER='aes-256-cfb' // 默认的加解密算法 my.CIPHER='aes-256-cfb' // 默认的加解密算法
my.CIPHER_LIST=crypto.getCiphers() my.CIPHER_LIST=crypto.getCiphers()
my.CURVE='secp256k1' // 默认的ECDH曲线用于把私钥转成公钥。 my.CURVE='secp256k1' // 默认的ECDH曲线用于把私钥转成公钥。
my.CURVE_LIST=['secp256k1'] // crypto.getCurves() 引入到浏览器里后出错,不支持 getCurves. my.CURVE_LIST=['secp256k1'] // crypto.getCurves() 引入到浏览器里后出错,不支持 getCurves.
my.OUTPUT='hex' // 默认的哈希或加密的输入格式 my.OUTPUT='hex' // 默认的哈希或加密的输入格式
my.OUTPUT_LIST=['hex','latin1','base64'] // or 'buf' to Buffer explicitly my.OUTPUT_LIST=['hex','latin1','base64'] // or 'buf' to Buffer explicitly
my.INPUT='utf8' // 默认的加密方法的明文格式。utf8 能够兼容 latin1, ascii 的情形 my.INPUT='utf8' // 默认的加密方法的明文格式。utf8 能够兼容 latin1, ascii 的情形
my.INPUT_LIST=['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView my.INPUT_LIST=['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView
my.COIN='TIC' // 默认的币种 my.COIN='TIC' // 默认的币种
my.COIN_LIST=['TIC','BTC','ETH'] my.COIN_LIST=['TIC','BTC','ETH']
module.exports = { module.exports = {
hash:function(data, option){ // data can be anything, but converts to string or remains be Buffer/TypedArray/DataView hash:function(data, option){ // data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
if (this.isHashable(data)) { if (this.isHashable(data)) {
option=option||{} option=option||{}
if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView)) if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView))
data=JSON.stringify(data) data=JSON.stringify(data)
if (option.salt && typeof(option.salt)==='string') if (option.salt && typeof(option.salt)==='string')
data=data+this.hash(option.salt) data=data+this.hash(option.salt)
let hasher= my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER // 默认为 sha256. let hasher= my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER // 默认为 sha256.
let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView. let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // option.output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64' let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // option.output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64'
return crypto.createHash(hasher).update(data, inputEncoding).digest(outputEncoding) return crypto.createHash(hasher).update(data, inputEncoding).digest(outputEncoding)
} }
return null return null
} }
, ,
isHashable:function(data, option){ isHashable:function(data, option){
option=option||{} option=option||{}
if (option.strict) { if (option.strict) {
return data && typeof(data)!=='boolean' && data!==Infinity // 允许大多数数据,除了空值、布尔值、无限数 return data && typeof(data)!=='boolean' && data!==Infinity // 允许大多数数据,除了空值、布尔值、无限数
} }
return typeof(data)!=='undefined' // 允许一切数据,除非 undefined return typeof(data)!=='undefined' // 允许一切数据,除非 undefined
} }
, ,
isHash:function(hash, option){ isHash:function(hash, option){
option=option||{} option=option||{}
option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
switch(option.hasher){ switch(option.hasher){
case 'sha256': return /^[a-fA-F0-9]{64}$/.test(hash) case 'sha256': return /^[a-fA-F0-9]{64}$/.test(hash)
case 'md5': return /^[a-fA-F0-9]{32}$/.test(hash) case 'md5': return /^[a-fA-F0-9]{32}$/.test(hash)
case 'ripemd160': case 'sha1': return /^[a-fA-F0-9]{40}$/.test(hash) case 'ripemd160': case 'sha1': return /^[a-fA-F0-9]{40}$/.test(hash)
case 'sha512': return /^[a-fA-F0-9]{128}$/.test(hash) case 'sha512': return /^[a-fA-F0-9]{128}$/.test(hash)
} }
return false return false
} }
, ,
encrypt: function(data, pwd, option){ encrypt: function(data, pwd, option){
if (this.isHashable(data) && typeof(pwd)==='string') { if (this.isHashable(data) && typeof(pwd)==='string') {
option=option||{} option=option||{}
let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
let cipher=crypto.createCipher( let cipher=crypto.createCipher(
my.CIPHER_LIST.indexOf(option.cipher)>=0?option.cipher:my.CIPHER, my.CIPHER_LIST.indexOf(option.cipher)>=0?option.cipher:my.CIPHER,
this.hash(pwd)) this.hash(pwd))
if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView)) if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView))
data=JSON.stringify(data) data=JSON.stringify(data)
let encrypted = cipher.update(data, inputEncoding, outputEncoding) let encrypted = cipher.update(data, inputEncoding, outputEncoding)
encrypted += cipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string encrypted += cipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
return encrypted return encrypted
} }
return null return null
} }
, ,
decrypt: function(data, pwd, option){ // data 应当是 encrypt 输出的数据类型 decrypt: function(data, pwd, option){ // data 应当是 encrypt 输出的数据类型
if (data && (typeof(data)==='string' || data instanceof Buffer) && typeof(pwd)==='string') { if (data && (typeof(data)==='string' || data instanceof Buffer) && typeof(pwd)==='string') {
option=option||{} option=option||{}
let inputEncoding=my.OUTPUT_LIST.indexOf(option.input)>=0?option.input:my.OUTPUT // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer let inputEncoding=my.OUTPUT_LIST.indexOf(option.input)>=0?option.input:my.OUTPUT // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
let outputEncoding=(option.output==='buf')?undefined:(my.INPUT_LIST.indexOf(option.output)>=0?option.output:my.INPUT) // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly let outputEncoding=(option.output==='buf')?undefined:(my.INPUT_LIST.indexOf(option.output)>=0?option.output:my.INPUT) // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly
let decipher=crypto.createDecipher( let decipher=crypto.createDecipher(
my.CIPHER_LIST.indexOf(option.cipher)>=0?option.cipher:my.CIPHER, my.CIPHER_LIST.indexOf(option.cipher)>=0?option.cipher:my.CIPHER,
this.hash(pwd)) this.hash(pwd))
let decrypted = decipher.update(data, inputEncoding, outputEncoding) let decrypted = decipher.update(data, inputEncoding, outputEncoding)
decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
if (option.format==='json') { // 如果用户输入错误密码deciper也能返回结果。为了判断是否正确结果对应当是 json 格式的原文做解析来验证。 if (option.format==='json') { // 如果用户输入错误密码deciper也能返回结果。为了判断是否正确结果对应当是 json 格式的原文做解析来验证。
try{ try{
JSON.parse(decrypted) JSON.parse(decrypted)
}catch(exception){ }catch(exception){
return null return null
} }
} }
return decrypted return decrypted
} }
return null return null
} }
, ,
sign: function(data, seckey, option) { // data can be string or buffer or object, results are the same sign: function(data, seckey, option) { // data can be string or buffer or object, results are the same
if (this.isHashable(data) && this.isSeckey(seckey)) { if (this.isHashable(data) && this.isSeckey(seckey)) {
option=option||{} option=option||{}
// 使用nacl的签名算法。注意nacl.sign需要的seckey是64字节=512位而比特币/以太坊的seckey是32字节。因此本方法只能用于 TIC 币的 keypair。 // 使用nacl的签名算法。注意nacl.sign需要的seckey是64字节=512位而比特币/以太坊的seckey是32字节。因此本方法只能用于 TIC 币的 keypair。
option.output='buf' // 哈希必须输出为 buffer option.output='buf' // 哈希必须输出为 buffer
var hashBuf = this.hash(data, option) var hashBuf = this.hash(data, option)
var signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex')) var signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'))
return Buffer.from(signature).toString('hex') // 返回128个hex字符64字节 return Buffer.from(signature).toString('hex') // 返回128个hex字符64字节
// 方案2尚未彻底实现。 // 方案2尚未彻底实现。
// let hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER // let hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
// let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView. // let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
// let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT)
// let signer=crypto.createSign(hasher) // let signer=crypto.createSign(hasher)
// return signer.update(data, inputEncoding).sign(seckey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式因此这样写是不能用的。 // return signer.update(data, inputEncoding).sign(seckey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式因此这样写是不能用的。
} }
return null return null
} }
, ,
isSignature:function(signature){ isSignature:function(signature){
return /^[a-fA-F0-9]{128}$/.test(signature) return /^[a-fA-F0-9]{128}$/.test(signature)
} }
, ,
verify: function (data, signature, pubkey, option) { // data could be anything, but converts to string or remains be Buffer/TypedArray/DataView verify: function (data, signature, pubkey, option) { // data could be anything, but converts to string or remains be Buffer/TypedArray/DataView
if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey)){ if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey)){
option=option||{} option=option||{}
option.output='buf' // 哈希必须输出为 buffer option.output='buf' // 哈希必须输出为 buffer
var bufHash=this.hash(data, option) var bufHash=this.hash(data, option)
var bufSignature = Buffer.from(signature, 'hex') var bufSignature = Buffer.from(signature, 'hex')
var bufPubkey = Buffer.from(pubkey, 'hex') var bufPubkey = Buffer.from(pubkey, 'hex')
var res = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey) var res = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
return res return res
} }
return null return null
} }
, ,
pass2keypair:function(pass, option){ // 如果使用其他机制例如密码、随机数不使用secword也可生成keypair pass2keypair:function(pass, option){ // 如果使用其他机制例如密码、随机数不使用secword也可生成keypair
if (this.isHashable(pass)){ if (this.isHashable(pass)){
option=option||{} option=option||{}
option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
var hashBuf = crypto.createHash(option.hasher).update(pass).digest() var hashBuf = crypto.createHash(option.hasher).update(pass).digest()
var keypair = nacl.sign.keyPair.fromSeed(hashBuf) var keypair = nacl.sign.keyPair.fromSeed(hashBuf)
return { return {
hash: hashBuf.toString('hex'), hash: hashBuf.toString('hex'),
pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型 pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型
seckey: Buffer.from(keypair.secretKey).toString('hex') seckey: Buffer.from(keypair.secretKey).toString('hex')
} }
} }
return null return null
} }
, ,
secword2keypair: function(secword, option){ // option.coin 币种option.passphase 密码默认为空option.path==='master' 生成 HD master key不定义则默认为相应币种的第一对公私钥。 secword2keypair: function(secword, option){ // option.coin 币种option.passphase 密码默认为空option.path==='master' 生成 HD master key不定义则默认为相应币种的第一对公私钥。
if (Secword.isValid(secword)){ if (Secword.isValid(secword)){
option=option||{} option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
if(option.coin==='TIC') { if(option.coin==='TIC') {
// 采用自己的算法bip39算法从secword到种子hash后用 nacl.sign.keyPair.fromSeed()方法。 // 采用自己的算法bip39算法从secword到种子hash后用 nacl.sign.keyPair.fromSeed()方法。
option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
let hashBuf=crypto.createHash(option.hasher).update(this.secword2seed(secword, option.pass)).digest() let hashBuf=crypto.createHash(option.hasher).update(this.secword2seed(secword, option.pass)).digest()
let keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl.sign.keyPair.fromSeed 要求32字节的种子而 this.secword2seed生成的是64字节种子所以要先做一次sha256 let keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl.sign.keyPair.fromSeed 要求32字节的种子而 this.secword2seed生成的是64字节种子所以要先做一次sha256
return { return {
coin: option.coin, coin: option.coin,
pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型 pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型
seckey: Buffer.from(keypair.secretKey).toString('hex') // nacl.sign.keyPair.fromSeed 得到的 seckey 是64字节的不同于比特币/以太坊的32字节密钥。 seckey: Buffer.from(keypair.secretKey).toString('hex') // nacl.sign.keyPair.fromSeed 得到的 seckey 是64字节的不同于比特币/以太坊的32字节密钥。
} }
}else { }else {
// 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。 // 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。
// let hdmaster=HDKey.fromMasterSeed(new Buffer(this.secword2seed(secword, option.pass), 'hex')) // 和 new Secword(secword).toHDPrivateKey 求出的公私钥一样! // let hdmaster=HDKey.fromMasterSeed(new Buffer(this.secword2seed(secword, option.pass), 'hex')) // 和 new Secword(secword).toHDPrivateKey 求出的公私钥一样!
let hdmaster=new Secword(secword).toHDPrivateKey(option.pass) // 和 ethers.HDNode.fromMnemonic(secword)的公私钥一样。而 ethers.HDNode.fromMnemonic(secword).derivePath("m/44'/60'/0'/0/0")的公私钥===ethers.Wallet.fromMnemonic(secword [,"m/44'/60'/0'/0/0"]) let hdmaster=new Secword(secword).toHDPrivateKey(option.pass) // 和 ethers.HDNode.fromMnemonic(secword)的公私钥一样。而 ethers.HDNode.fromMnemonic(secword).derivePath("m/44'/60'/0'/0/0")的公私钥===ethers.Wallet.fromMnemonic(secword [,"m/44'/60'/0'/0/0"])
let key=hdmaster let key=hdmaster
if (option.path==='master'){ if (option.path==='master'){
key=hdmaster key=hdmaster
}else if (!option.path) { }else if (!option.path) {
switch(option.coin){ switch(option.coin){
case 'BTC': key=hdmaster.derive("m/44'/0'/0'/0/0"); break case 'BTC': key=hdmaster.derive("m/44'/0'/0'/0/0"); break
case 'ETH': key=hdmaster.derive("m/44'/60'/0'/0/0"); break case 'ETH': key=hdmaster.derive("m/44'/60'/0'/0/0"); break
default: key=hdmaster.derive("m/44'/99'/0'/0/0"); break default: key=hdmaster.derive("m/44'/99'/0'/0/0"); break
} }
}else { // 指定了路径 option.path例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1" }else { // 指定了路径 option.path例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
key=hdmaster.derive(option.path) key=hdmaster.derive(option.path)
} }
return { return {
coin: option.coin, coin: option.coin,
seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果 seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果
pubkey: key.publicKey.toString('hex') pubkey: key.publicKey.toString('hex')
} }
} }
} }
return null return null
} }
, ,
seckey2pubkey:function(seckey, option){ seckey2pubkey:function(seckey, option){
option=option||{} option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
if (this.isSeckey(seckey) && seckey.length===64){ // 只能用于32字节的私钥BTC, ETH)。也就是不能用于 TIC 的私钥。 if (this.isSeckey(seckey) && seckey.length===64){ // 只能用于32字节的私钥BTC, ETH)。也就是不能用于 TIC 的私钥。
let curve = my.CURVE_LIST.indexOf(option.curve)>=0?option.curve:my.CURVE // 默认为 secp256k1 let curve = my.CURVE_LIST.indexOf(option.curve)>=0?option.curve:my.CURVE // 默认为 secp256k1
let compress = ['compressed', 'uncompressed'].indexOf(option.compress)>=0?option.compress:'compressed' // 默认为压缩格式的公钥 let compress = ['compressed', 'uncompressed'].indexOf(option.compress)>=0?option.compress:'compressed' // 默认为压缩格式的公钥
return new crypto.ECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex',compress).toString('hex') // ecdh.getPublicKey(不加参数) 默认为 'uncompressed' return new crypto.ECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex',compress).toString('hex') // ecdh.getPublicKey(不加参数) 默认为 'uncompressed'
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。 // 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。
// 或者 require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'),compress).toString('hex') // 或者 require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'),compress).toString('hex')
// 或者 require('bitcore-lib').PublicKey.fromPrivateKey(new Btc.PrivateKey(seckey)).toString('hex') // 或者 require('bitcore-lib').PublicKey.fromPrivateKey(new Btc.PrivateKey(seckey)).toString('hex')
// 注意Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同 // 注意Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
}else if (this.isSeckey(seckey) && seckey.length===128 && option.coin==='TIC'){ // 用于64字节=128 hex的 TIC 私钥 }else if (this.isSeckey(seckey) && seckey.length===128 && option.coin==='TIC'){ // 用于64字节=128 hex的 TIC 私钥
let keypair=nacl.sign.keyPair.fromSecretKey(Buffer.from(seckey,'hex')) let keypair=nacl.sign.keyPair.fromSecretKey(Buffer.from(seckey,'hex'))
return Buffer.from(keypair.publicKey).toString('hex') // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型 return Buffer.from(keypair.publicKey).toString('hex') // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型
} }
return null return null
} }
, ,
secword2account:function(secword, option){ // account 比 keypair 多了 address 字段。 secword2account:function(secword, option){ // account 比 keypair 多了 address 字段。
option=option||{} option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
let kp=this.secword2keypair(secword, option) let kp=this.secword2keypair(secword, option)
if (kp) { if (kp) {
kp.address=this.pubkey2address(kp.pubkey, option) kp.address=this.pubkey2address(kp.pubkey, option)
return kp return kp
} }
return null return null
} }
, ,
secword2address:function(secword, option){ secword2address:function(secword, option){
option=option||{} option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
let address let address
let kp=this.secword2keypair(secword, option) let kp=this.secword2keypair(secword, option)
if (kp) { if (kp) {
return this.pubkey2address(kp.pubkey,option) return this.pubkey2address(kp.pubkey,option)
} }
return null return null
} }
, ,
isSecword:function(secword){ isSecword:function(secword){
return Secword.isValid(secword) return Secword.isValid(secword)
} }
, ,
isSeckey:function(seckey){ isSeckey:function(seckey){
// 比特币、以太坊的私钥64 hex // 比特币、以太坊的私钥64 hex
// nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex // nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey) return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey)
} }
, ,
isPubkey:function(pubkey){ isPubkey:function(pubkey){
// 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex // 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
// 以太坊的公钥:'02|03' + 64 hex // 以太坊的公钥:'02|03' + 64 hex
// nacl.sign 的公钥64 hex // nacl.sign 的公钥64 hex
return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0" return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
} }
, ,
isAddress: function (address) { isAddress: function (address) {
return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address) // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) { return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address) // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) {
} }
, ,
pubkey2address:function (pubkey, option) { // pubkey 应当是string类型 pubkey2address:function (pubkey, option) { // pubkey 应当是string类型
option=option||{} option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
if (this.isPubkey(pubkey)) { if (this.isPubkey(pubkey)) {
option = option||{} option = option||{}
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest() let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex') let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
let prefix let prefix
if (option.coin==='TIC'){ if (option.coin==='TIC'){
switch(option.netType){ switch(option.netType){
case 'mainnet': prefix='42'; break; // '42'=>T, '6E'=>m case 'mainnet': prefix='42'; break; // '42'=>T, '6E'=>m
case 'testnet': prefix='7F'; break; // '7F'=>t case 'testnet': prefix='7F'; break; // '7F'=>t
case 'devnet': prefix='5A'; break; // '5A'=>d case 'devnet': prefix='5A'; break; // '5A'=>d
default: prefix='42' // 默认暂且为 42为了兼容已经运行的链。 default: prefix='42' // 默认暂且为 42为了兼容已经运行的链。
} }
}else if(option.coin==='BTC'){ }else if(option.coin==='BTC'){
switch (option.netType) { switch (option.netType) {
case 'mainnet': prefix='00'; break; // 1 case 'mainnet': prefix='00'; break; // 1
case 'testnet': prefix='6f'; break; // m or n case 'testnet': prefix='6f'; break; // m or n
case 'p2sh': prefix='05'; break; // 3 case 'p2sh': prefix='05'; break; // 3
default: prefix='6f' default: prefix='6f'
} }
}else { // 目前不支持 ETH或其他币种 地址转换因为这会大量增加前端打包的js。 }else { // 目前不支持 ETH或其他币种 地址转换因为这会大量增加前端打包的js。
return null return null
} }
var wifAddress=bs58check.encode(Buffer.from(prefix+h160,'hex')) // wallet import format var wifAddress=bs58check.encode(Buffer.from(prefix+h160,'hex')) // wallet import format
return wifAddress return wifAddress
} }
return null return null
} }
, ,
secword2seed:function(secword, pass) { // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样是64字节的种子。 secword2seed:function(secword, pass) { // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样是64字节的种子。
if (Secword.isValid(secword)) { // bip39.validateMnemonic(secword)) { if (Secword.isValid(secword)) { // bip39.validateMnemonic(secword)) {
return new Secword(secword).toSeed(pass).toString('hex') // 结果一致于 bip39.mnemonicToSeedHex(secword) 或 ethers.HDNode.mnemonic2Seed(secword) return new Secword(secword).toSeed(pass).toString('hex') // 结果一致于 bip39.mnemonicToSeedHex(secword) 或 ethers.HDNode.mnemonic2Seed(secword)
} }
return null return null
} }
, ,
randomSecword:function(lang){ // Object.keys(Secword.Words) => [ 'CHINESE', 'ENGLISH', 'FRENCH', 'ITALIAN', 'JAPANESE', 'SPANISH' ] randomSecword:function(lang){ // Object.keys(Secword.Words) => [ 'CHINESE', 'ENGLISH', 'FRENCH', 'ITALIAN', 'JAPANESE', 'SPANISH' ]
lang = (lang && Secword.Words.hasOwnProperty(lang.toUpperCase())) ? lang.toUpperCase() : 'ENGLISH' lang = (lang && Secword.Words.hasOwnProperty(lang.toUpperCase())) ? lang.toUpperCase() : 'ENGLISH'
return new Secword(Secword.Words[lang]).phrase return new Secword(Secword.Words[lang]).phrase
} }
, ,
randomSeckey:function(option){ randomSeckey:function(option){
option=option||{} option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
if (option.coin==='TIC'){ if (option.coin==='TIC'){
return Buffer.from(nacl.sign.keyPair().secretKey).toString('hex') // 64字节 return Buffer.from(nacl.sign.keyPair().secretKey).toString('hex') // 64字节
}else{ }else{
return Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节 return Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
} }
} }
, ,
randomKeypair:function(option){ randomKeypair:function(option){
option=option||{} option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
let kp let kp
if (option.coin==='TIC'){ if (option.coin==='TIC'){
kp=nacl.sign.keyPair() kp=nacl.sign.keyPair()
}else{ }else{
kp=nacl.box.keyPair() kp=nacl.box.keyPair()
} }
return { return {
seckey:Buffer.from(kp.secretKey).toString('hex'), seckey:Buffer.from(kp.secretKey).toString('hex'),
pubkey:Buffer.from(kp.publicKey).toString('hex') pubkey:Buffer.from(kp.publicKey).toString('hex')
} }
} }
, ,
randomString:function (length=6, alphabet) { // 长度为 length字母表为 alphabet 的随机字符串 randomString:function (length=6, alphabet) { // 长度为 length字母表为 alphabet 的随机字符串
alphabet = alphabet||"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@" alphabet = alphabet||"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@"
var text = '' var text = ''
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
text += alphabet.charAt(Math.floor(Math.random() * alphabet.length)) text += alphabet.charAt(Math.floor(Math.random() * alphabet.length))
} }
return text return text
} }
, ,
randomNumber:function(option){ // 长度为 option.length 的随机数字,或者 (option.min||0) <= num < option.max randomNumber:function(option){ // 长度为 option.length 的随机数字,或者 (option.min||0) <= num < option.max
option=option||{} option=option||{}
var num=0 var num=0
if (option.length>0){ if (option.length>0){
num=parseInt(Math.random()*Math.pow(10,option.length)) num=parseInt(Math.random()*Math.pow(10,option.length))
let l = new String(num).length let l = new String(num).length
while(l < option.length) { while(l < option.length) {
num = '0' + num // 注意,这时返回的是字符串! num = '0' + num // 注意,这时返回的是字符串!
l++ l++
} }
}else if (option.max>0){ }else if (option.max>0){
option.min = (option.min>=0)?option.min:0 option.min = (option.min>=0)?option.min:0
num=parseInt(Math.random()*(option.max-option.min))+option.min num=parseInt(Math.random()*(option.max-option.min))+option.min
}else{ // 如果 option 为空 }else{ // 如果 option 为空
num=Math.random() num=Math.random()
} }
return num return num
} }
, ,
randomUuid:uuid.v4 randomUuid:uuid.v4
, ,
getMerkleRoot:function(hashList, option){ getMerkleHash:function(hashList, option){
// merkle算法略有难度暂时用最简单的hash代替 // merkle算法略有难度暂时用最简单的hash代替
if(Array.isArray(hashList)){ if(Array.isArray(hashList)){
option=option||{} option=option||{}
let output=(option.output==='buf')?undefined:(option.output||my.OUTPUT) let output=(option.output==='buf')?undefined:(option.output||my.OUTPUT)
let hasher=crypto.createHash(my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER) let hasher=crypto.createHash(my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER)
for (var hash of hashList){ for (var hash of hashList){
hasher.update(hash) hasher.update(hash)
} }
return hasher.digest(output) return hasher.digest(output)
} }
return null return null
} }
, ,
getMerkleRoot:function(todoHashList, option){ getMerkleRoot:function(todoHashList, option){
//深拷贝传入数组,防止引用对象被改变 //深拷贝传入数组,防止引用对象被改变
let hashList = [...todoHashList] let hashList = [...todoHashList]
if(!Array.isArray(hashList)) if(!Array.isArray(hashList))
return null return null
var border = hashList.length; var border = hashList.length;
if(border == 0) if(border == 0)
return this.hash('') return this.hash('')
if(border == 1) if(border == 1)
return this.hash(hashList[0]); return this.hash(hashList[0]);
while(1){ while(1){
let i = 1,j = 0; let i = 1,j = 0;
for(; i < border; i = i + 2){ for(; i < border; i = i + 2){
hashList[j] = this.hash(hashList[i - 1] + hashList[i]); hashList[j] = this.hash(hashList[i - 1] + hashList[i]);
if(border == 2){ if(border == 2){
return hashList[0]; return hashList[0];
} }
if(i + 1 == border) break; if(i + 1 == border) break;
j = j + 1; j = j + 1;
if(i + 2 == border){ if(i + 2 == border){
i = i + 1; i = i + 1;
hashList[j] = this.hash(hashList[i]); hashList[j] = this.hash(hashList[i]);
break; break;
} }
} }
border = j + 1; border = j + 1;
} }
return hashList return hashList
} }
, ,
distanceSig:function(hash, sig){ // hash为64hex字符sig为128hex字符。返回用hex表达的距离。 distanceSig:function(hash, sig){ // hash为64hex字符sig为128hex字符。返回用hex表达的距离。
if (this.isSignature(sig) && this.isHash(hash)){ if (this.isSignature(sig) && this.isHash(hash)){
var hashSig=this.hash(sig) // 把签名也转成32字节的哈希同样长度方便比较 var hashSig=this.hash(sig) // 把签名也转成32字节的哈希同样长度方便比较
return new BigNumber(hash,16).minus(new BigNumber(hashSig,16)).abs().toString(16) return new BigNumber(hash,16).minus(new BigNumber(hashSig,16)).abs().toString(16)
} }
return null return null
} }
, ,
compareSig:function(hash, sig1, sig2){ // 返回距离hash更近的sig compareSig:function(hash, sig1, sig2){ // 返回距离hash更近的sig
if (this.isHash(hash)) { if (this.isHash(hash)) {
if (this.isSignature(sig2) && this.isSignature(sig1)) { if (this.isSignature(sig2) && this.isSignature(sig1)) {
var dis1=this.distanceSig(hash,sig1) var dis1=this.distanceSig(hash,sig1)
var dis2=this.distanceSig(hash,sig2) var dis2=this.distanceSig(hash,sig2)
if (dis1<dis2) { if (dis1<dis2) {
return sig1 return sig1
}else if (dis1>dis2) { }else if (dis1>dis2) {
return sig2 return sig2
}else if (dis1===dis2) { // 如果极其巧合的距离相等,也可能是一个在左、一个在右,那就按 signature 本身的字符串排序来比较。 }else if (dis1===dis2) { // 如果极其巧合的距离相等,也可能是一个在左、一个在右,那就按 signature 本身的字符串排序来比较。
return sig1<sig2 ? sig1 : sig1===sig2 ? sig1 : sig2 return sig1<sig2 ? sig1 : sig1===sig2 ? sig1 : sig2
} }
}else if (this.isSignature(sig2)){ // 允许其中一个signature是非法的例如undefined }else if (this.isSignature(sig2)){ // 允许其中一个signature是非法的例如undefined
return sig2 return sig2
}else if (this.isSignature(sig1)){ }else if (this.isSignature(sig1)){
return sig1 return sig1
} }
} }
return null return null
} }
, ,
sortSigList:function(hash, sigList) { sortSigList:function(hash, sigList) {
if (Array.isArray(sigList) && this.isHash(hash)){ if (Array.isArray(sigList) && this.isHash(hash)){
sigList.sort(function(sig1, sig2){ sigList.sort(function(sig1, sig2){
if (this.isSignature(sig1) && this.isSignature(sig2)) { if (this.isSignature(sig1) && this.isSignature(sig2)) {
var winner=this.compareSig(hash, sig1, sig2) var winner=this.compareSig(hash, sig1, sig2)
if (sig1===sig2) return 0 if (sig1===sig2) return 0
else if (winner===sig1) return -1 else if (winner===sig1) return -1
else if (winner===sig2) return 1 else if (winner===sig2) return 1
}else { // 如果 sig1 或 sig2 不是 signature 格式 }else { // 如果 sig1 或 sig2 不是 signature 格式
throw 'Not a signature!' throw 'Not a signature!'
} }
}) })
return sigList return sigList
} }
return null return null
} }
, ,
/** /**
* 把数组所有元素按照参数=参数值的模式用&字符拼接成字符串 * 把数组所有元素按照参数=参数值的模式用&字符拼接成字符串
* @param $para 需要拼接的数组 * @param $para 需要拼接的数组
* return 拼接完成以后的字符串 * return 拼接完成以后的字符串
*/ */
getString2Sign: function (paramSet, converter, delimiter) { getString2Sign: function (paramSet, converter, delimiter) {
if (paramSet && typeof paramSet==='object') { if (paramSet && typeof paramSet==='object') {
var string2Sign = '' var string2Sign = ''
var converter = converter || '' var converter = converter || ''
var delimiter = delimiter || '' var delimiter = delimiter || ''
for (var key of Object.keys(paramSet).sort()) { for (var key of Object.keys(paramSet).sort()) {
var value=paramSet[key] var value=paramSet[key]
if (value && typeof value==='object'){ // 万一 bis_content 等对象直接送了进来。 if (value && typeof value==='object'){ // 万一 bis_content 等对象直接送了进来。
value=JSON.stringify(value) value=JSON.stringify(value)
} }
if ((typeof value==='string' && value!=='') || typeof value==='number') { if ((typeof value==='string' && value!=='') || typeof value==='number') {
if (converter==='urlencode') value=encodeURIComponent(value) if (converter==='urlencode') value=encodeURIComponent(value)
string2Sign += (key + '=' + delimiter + value + delimiter + '&') // 根据产品、版本、请求或响应的不同有的需要key="vlaue"有的只要key=value。 string2Sign += (key + '=' + delimiter + value + delimiter + '&') // 根据产品、版本、请求或响应的不同有的需要key="vlaue"有的只要key=value。
} }
} }
string2Sign=string2Sign.replace(/&$/, '') // 删除末尾的 & string2Sign=string2Sign.replace(/&$/, '') // 删除末尾的 &
// if (get_magic_quotes_gpc()) { $string2Sign = stripslashes($string2Sign); } // if (get_magic_quotes_gpc()) { $string2Sign = stripslashes($string2Sign); }
// string2Sign=string2Sign.replace(/\\/g, ''); // 去除转义符 \ (似乎其实不去除,也完全不会影响,因为编程语言内部就会处理掉\) // string2Sign=string2Sign.replace(/\\/g, ''); // 去除转义符 \ (似乎其实不去除,也完全不会影响,因为编程语言内部就会处理掉\)
// string2Sign=string2Sign.replace(/\//g, '\\/'); // 为了verify把正斜杠进行转义 / 参见 https://openclub.alipay.com/read.php?tid=559&fid=2 // string2Sign=string2Sign.replace(/\//g, '\\/'); // 为了verify把正斜杠进行转义 / 参见 https://openclub.alipay.com/read.php?tid=559&fid=2
return string2Sign return string2Sign
} }
return '' return ''
} }
, ,
rsaSign: function(string2Sign, prikey, signType){ rsaSign: function(string2Sign, prikey, signType){
signType=signType||'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more signType=signType||'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
let signer=crypto.createSign(signType) let signer=crypto.createSign(signType)
return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64')) return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'))
} }
, ,
rsaVerify: function(string2Verify, sign, pubkey, signType){ rsaVerify: function(string2Verify, sign, pubkey, signType){
signType=signType||'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more signType=signType||'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
let verifier=crypto.createVerify(signType) let verifier=crypto.createVerify(signType)
return verifier.update(string2Verify).verify(pubkey, sign, 'base64') return verifier.update(string2Verify).verify(pubkey, sign, 'base64')
} }
} }