rename pass2keypair to pass_to_keypair
This commit is contained in:
@@ -1,321 +1,401 @@
|
||||
const crypto = require('crypto');
|
||||
const nacl = require('tweetnacl');
|
||||
const bs58check = require('bs58check');
|
||||
const { keccak256 } = require('js-sha3');
|
||||
const Secword = require('bitcore-mnemonic'); // https://bitcore.io/api/mnemonic/ https://github.com/bitpay/bitcore-mnemonic
|
||||
const crypto = require('crypto')
|
||||
const nacl = require('tweetnacl')
|
||||
const bs58check = require('bs58check')
|
||||
const { keccak256 } = require('js-sha3')
|
||||
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 HDKey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
|
||||
|
||||
// 全部以hex为默认输入输出格式,方便人的阅读,以及方便函数之间统一接口
|
||||
|
||||
const my = {};
|
||||
my.HASHER = 'sha256'; // 默认的哈希算法。could be md5, sha1, sha256, sha512, ripemd160。 可用 Crypto.getHashes/Ciphers/Curves() 查看支持的种类。
|
||||
my.HASHER_LIST = crypto.getHashes();
|
||||
my.CIPHER = 'aes-256-cfb'; // 默认的加解密算法
|
||||
my.CIPHER_LIST = crypto.getCiphers();
|
||||
my.CURVE = 'secp256k1'; // 默认的ECDH曲线,用于把私钥转成公钥。
|
||||
my.CURVE_LIST = ['secp256k1']; // crypto.getCurves() 引入到浏览器里后出错,不支持 getCurves.
|
||||
my.OUTPUT = 'hex'; // 默认的哈希或加密的输入格式
|
||||
my.OUTPUT_LIST = ['hex', 'latin1', 'base64']; // or 'buf' to Buffer explicitly
|
||||
my.INPUT = 'utf8'; // 默认的加密方法的明文格式。utf8 能够兼容 latin1, ascii 的情形
|
||||
my.INPUT_LIST = ['utf8', 'ascii', 'latin1']; // ignored for Buffer/TypedArray/DataView
|
||||
my.COIN = 'BTC'; // 默认的币种
|
||||
my.COIN_LIST = ['TIC', 'BTC', 'ETH'];
|
||||
my.CHAINNET = 'mainnet'; // 默认的链网
|
||||
const my = {}
|
||||
my.HASHER = 'sha256' // 默认的哈希算法。could be md5, sha1, sha256, sha512, ripemd160。 可用 Crypto.getHashes/Ciphers/Curves() 查看支持的种类。
|
||||
my.HASHER_LIST = crypto.getHashes()
|
||||
my.CIPHER = 'aes-256-cfb' // 默认的加解密算法
|
||||
my.CIPHER_LIST = crypto.getCiphers()
|
||||
my.CURVE = 'secp256k1' // 默认的ECDH曲线,用于把私钥转成公钥。
|
||||
my.CURVE_LIST = ['secp256k1'] // crypto.getCurves() 引入到浏览器里后出错,不支持 getCurves.
|
||||
my.OUTPUT = 'hex' // 默认的哈希或加密的输入格式
|
||||
my.OUTPUT_LIST = ['hex', 'latin1', 'base64'] // or 'buf' to Buffer explicitly
|
||||
my.INPUT = 'utf8' // 默认的加密方法的明文格式。utf8 能够兼容 latin1, ascii 的情形
|
||||
my.INPUT_LIST = ['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView
|
||||
my.COIN = 'BTC' // 默认的币种
|
||||
my.COIN_LIST = ['TIC', 'BTC', 'ETH']
|
||||
my.CHAINNET = 'mainnet' // 默认的链网
|
||||
|
||||
module.exports = {
|
||||
hash: function (data, option) { // data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
|
||||
if (typeof(data) !== 'boolean' && data !== Infinity) {
|
||||
option = option || {};
|
||||
if (typeof (data) !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView))
|
||||
data = JSON.stringify(data);
|
||||
if (option.salt && typeof (option.salt) === 'string')
|
||||
data = data + this.hash(option.salt);
|
||||
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 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 null;
|
||||
hash: function (data, option) {
|
||||
// data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
|
||||
if (typeof data !== 'boolean' && data !== Infinity) {
|
||||
option = option || {}
|
||||
if (
|
||||
typeof data !== 'string' &&
|
||||
!(data instanceof Buffer) &&
|
||||
!(data instanceof DataView)
|
||||
)
|
||||
data = JSON.stringify(data)
|
||||
if (option.salt && typeof option.salt === 'string')
|
||||
data = data + this.hash(option.salt)
|
||||
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 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)
|
||||
}
|
||||
,
|
||||
isHashable: function (data, option) {
|
||||
option = option || {};
|
||||
if (option.strict) {
|
||||
return data && typeof (data) !== 'boolean' && data !== Infinity; // 允许大多数数据,除了空值、布尔值、无限数
|
||||
}
|
||||
return typeof (data) !== 'undefined'; // 允许一切数据,除非 undefined
|
||||
return null
|
||||
},
|
||||
isHashable: function (data, option) {
|
||||
option = option || {}
|
||||
if (option.strict) {
|
||||
return data && typeof data !== 'boolean' && data !== Infinity // 允许大多数数据,除了空值、布尔值、无限数
|
||||
}
|
||||
,
|
||||
isHash: function (hash, option) {
|
||||
option = option || {};
|
||||
option.hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER;
|
||||
switch (option.hasher) {
|
||||
case 'sha256': return /^[a-fA-F0-9]{64}$/.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 'sha512': return /^[a-fA-F0-9]{128}$/.test(hash);
|
||||
}
|
||||
return false;
|
||||
return typeof data !== 'undefined' // 允许一切数据,除非 undefined
|
||||
},
|
||||
isHash: function (hash, option) {
|
||||
option = option || {}
|
||||
option.hasher =
|
||||
my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
|
||||
switch (option.hasher) {
|
||||
case 'sha256':
|
||||
return /^[a-fA-F0-9]{64}$/.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 'sha512':
|
||||
return /^[a-fA-F0-9]{128}$/.test(hash)
|
||||
}
|
||||
,
|
||||
encrypt: function (data, pwd, option) {
|
||||
if (this.isHashable(data) && typeof (pwd) === 'string') {
|
||||
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 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(
|
||||
my.CIPHER_LIST.indexOf(option.cipher) >= 0 ? option.cipher : my.CIPHER,
|
||||
this.hash(pwd, option)); //哈希加盐
|
||||
if (typeof (data) !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView))
|
||||
data = JSON.stringify(data);
|
||||
let encrypted = cipher.update(data, inputEncoding, outputEncoding);
|
||||
encrypted += cipher.final(outputEncoding); // 但是 Buffer + Buffer 还是会变成string
|
||||
return encrypted;
|
||||
}
|
||||
return null;
|
||||
return false
|
||||
},
|
||||
encrypt: function (data, pwd, option) {
|
||||
if (this.isHashable(data) && typeof pwd === 'string') {
|
||||
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 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(
|
||||
my.CIPHER_LIST.indexOf(option.cipher) >= 0 ? option.cipher : my.CIPHER,
|
||||
this.hash(pwd, option)
|
||||
) //哈希加盐
|
||||
if (
|
||||
typeof data !== 'string' &&
|
||||
!(data instanceof Buffer) &&
|
||||
!(data instanceof DataView)
|
||||
)
|
||||
data = JSON.stringify(data)
|
||||
let encrypted = cipher.update(data, inputEncoding, outputEncoding)
|
||||
encrypted += cipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
||||
return encrypted
|
||||
}
|
||||
,
|
||||
decrypt: function (data, pwd, option) { // data 应当是 encrypt 输出的数据类型
|
||||
if (data && (typeof (data) === 'string' || data instanceof Buffer) && typeof (pwd) === 'string') {
|
||||
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 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(
|
||||
my.CIPHER_LIST.indexOf(option.cipher) >= 0 ? option.cipher : my.CIPHER,
|
||||
this.hash(pwd, option)); // 哈希加盐
|
||||
let decrypted = decipher.update(data, inputEncoding, outputEncoding);
|
||||
decrypted += decipher.final(outputEncoding); // 但是 Buffer + Buffer 还是会变成string
|
||||
try {
|
||||
JSON.parse(decrypted);
|
||||
} catch (exception) {
|
||||
return null;
|
||||
}
|
||||
return decrypted;
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
},
|
||||
decrypt: function (data, pwd, option) {
|
||||
// data 应当是 encrypt 输出的数据类型
|
||||
if (
|
||||
data &&
|
||||
(typeof data === 'string' || data instanceof Buffer) &&
|
||||
typeof pwd === 'string'
|
||||
) {
|
||||
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 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(
|
||||
my.CIPHER_LIST.indexOf(option.cipher) >= 0 ? option.cipher : my.CIPHER,
|
||||
this.hash(pwd, option)
|
||||
) // 哈希加盐
|
||||
let decrypted = decipher.update(data, inputEncoding, outputEncoding)
|
||||
decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
||||
try {
|
||||
JSON.parse(decrypted)
|
||||
} catch (exception) {
|
||||
return null
|
||||
}
|
||||
return decrypted
|
||||
}
|
||||
,
|
||||
sign: function (data, seckey, option) { // data can be string or buffer or object, results are the same
|
||||
if (this.isHashable(data) && this.isSeckey(seckey)) {
|
||||
option = option || {};
|
||||
return null
|
||||
},
|
||||
sign: function (data, seckey, option) {
|
||||
// data can be string or buffer or object, results are the same
|
||||
if (this.isHashable(data) && this.isSeckey(seckey)) {
|
||||
option = option || {}
|
||||
|
||||
// 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=512位,而比特币/以太坊的seckey是32字节。因此本方法只能用于 TIC 币的 keypair。
|
||||
option.output = 'buf'; // 哈希必须输出为 buffer
|
||||
var hashBuf = this.hash(data, option);
|
||||
var signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'));
|
||||
return Buffer.from(signature).toString('hex'); // 返回128个hex字符,64字节
|
||||
// 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=512位,而比特币/以太坊的seckey是32字节。因此本方法只能用于 TIC 币的 keypair。
|
||||
option.output = 'buf' // 哈希必须输出为 buffer
|
||||
var hashBuf = this.hash(data, option)
|
||||
var signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'))
|
||||
return Buffer.from(signature).toString('hex') // 返回128个hex字符,64字节
|
||||
|
||||
// 方案2:尚未彻底实现。
|
||||
// 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 outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT)
|
||||
// let signer=crypto.createSign(hasher)
|
||||
// return signer.update(data, inputEncoding).sign(seckey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式,因此这样写是不能用的。
|
||||
// 方案2:尚未彻底实现。
|
||||
// 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 outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT)
|
||||
// let signer=crypto.createSign(hasher)
|
||||
// return signer.update(data, inputEncoding).sign(seckey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式,因此这样写是不能用的。
|
||||
}
|
||||
return null
|
||||
},
|
||||
isSignature: function (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
|
||||
if (
|
||||
this.isHashable(data) &&
|
||||
this.isSignature(signature) &&
|
||||
this.isPubkey(pubkey)
|
||||
) {
|
||||
option = option || {}
|
||||
option.output = 'buf' // 哈希必须输出为 buffer
|
||||
var bufHash = this.hash(data, option)
|
||||
var bufSignature = Buffer.from(signature, 'hex')
|
||||
var bufPubkey = Buffer.from(pubkey, 'hex')
|
||||
var res = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
|
||||
return res
|
||||
}
|
||||
return null
|
||||
},
|
||||
pass_to_keypair: function (pass, option) {
|
||||
// 如果使用其他机制,例如密码、随机数,不使用secword,也可生成keypair
|
||||
if (this.isHashable(pass)) {
|
||||
option = option || {}
|
||||
option.hasher =
|
||||
my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER
|
||||
var hashBuf = crypto
|
||||
.createHash(option.hasher)
|
||||
.update(pass)
|
||||
.digest()
|
||||
var keypair = nacl.sign.keyPair.fromSeed(hashBuf)
|
||||
return {
|
||||
hash: hashBuf.toString('hex'),
|
||||
pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
|
||||
seckey: Buffer.from(keypair.secretKey).toString('hex')
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
secword2keypair: function (
|
||||
secword,
|
||||
option = { coin: my.COIN, path: 'master' }
|
||||
) {
|
||||
// option.coin 币种;option.passphase 密码,默认为空;option.path==='master' 生成 HD master key,不定义则默认为相应币种的第一对公私钥。
|
||||
if (Secword.isValid(secword)) {
|
||||
const { coin = my.COIN, path = 'master', pass = '' } = option
|
||||
// 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。
|
||||
// let hdmaster=HDKey.fromMasterSeed(new Buffer(this.secword2seed(secword, option.pass), 'hex')) // 和 new Secword(secword).toHDPrivateKey 求出的公私钥一样!
|
||||
let hdmaster = new Secword(secword).toHDPrivateKey(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
|
||||
if (path === 'master') {
|
||||
key = hdmaster
|
||||
} else if (!path) {
|
||||
switch (coin) {
|
||||
case 'BTC':
|
||||
key = hdmaster.derive("m/44'/0'/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
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
// 指定了路径 option.path,例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
|
||||
key = hdmaster.derive(path)
|
||||
}
|
||||
return {
|
||||
coin,
|
||||
path,
|
||||
seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果
|
||||
pubkey: key.publicKey.toString('hex')
|
||||
}
|
||||
}
|
||||
,
|
||||
isSignature: function (signature) {
|
||||
return /^[a-fA-F0-9]{128}$/.test(signature);
|
||||
return null
|
||||
},
|
||||
seckey2pubkey: function (seckey, option = {}) {
|
||||
if (this.isSeckey(seckey) && seckey.length === 64) {
|
||||
// 只能用于32字节的私钥(BTC, ETH)。也就是不能用于 TIC 的私钥。
|
||||
const { curve = my.CURVE, compress = 'compressed' } = option
|
||||
return new crypto.ECDH(curve)
|
||||
.setPrivateKey(seckey, 'hex')
|
||||
.getPublicKey('hex', compress)
|
||||
.toString('hex') // ecdh.getPublicKey(不加参数) 默认为 'uncompressed'
|
||||
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。
|
||||
// 或者 require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'),compress).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') 得到的公钥与上面的不同
|
||||
}
|
||||
,
|
||||
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)) {
|
||||
option = option || {};
|
||||
option.output = 'buf'; // 哈希必须输出为 buffer
|
||||
var bufHash = this.hash(data, option);
|
||||
var bufSignature = Buffer.from(signature, 'hex');
|
||||
var bufPubkey = Buffer.from(pubkey, 'hex');
|
||||
var res = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey);
|
||||
return res;
|
||||
return null
|
||||
},
|
||||
secword2account: function (secword, option) {
|
||||
// account 比 keypair 多了 address 字段。
|
||||
option = option || {}
|
||||
option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
|
||||
let kp = this.secword2keypair(secword, option)
|
||||
if (kp) {
|
||||
kp.address = this.pubkey2address(kp.pubkey, option)
|
||||
return kp
|
||||
}
|
||||
return null
|
||||
},
|
||||
secword2address: function (secword, option) {
|
||||
option = option || {}
|
||||
option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN
|
||||
let kp = this.secword2keypair(secword, option)
|
||||
if (kp) {
|
||||
return this.pubkey2address(kp.pubkey, option)
|
||||
}
|
||||
return null
|
||||
},
|
||||
isSecword: function (secword) {
|
||||
return Secword.isValid(secword)
|
||||
},
|
||||
isSeckey: function (seckey) {
|
||||
// 比特币、以太坊的私钥:64 hex
|
||||
// nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
|
||||
return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey)
|
||||
},
|
||||
isPubkey: function (pubkey) {
|
||||
// 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
|
||||
// 以太坊的公钥:'02|03' + 64 hex
|
||||
// nacl.sign 的公钥:64 hex
|
||||
return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
|
||||
},
|
||||
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)) {
|
||||
},
|
||||
pubkey2address: function (pubkey, option = {}) {
|
||||
// pubkey 应当是string类型
|
||||
if (this.isPubkey(pubkey)) {
|
||||
const { coin = 'BTC', netType = 'mainnet', curve = my.CURVE } = option
|
||||
let h256 = crypto
|
||||
.createHash('sha256')
|
||||
.update(Buffer.from(pubkey, 'hex'))
|
||||
.digest()
|
||||
let h160 = crypto
|
||||
.createHash('ripemd160')
|
||||
.update(h256)
|
||||
.digest('hex')
|
||||
let prefix
|
||||
if (coin === 'BTC') {
|
||||
switch (netType) {
|
||||
case 'mainnet':
|
||||
prefix = '00'
|
||||
break // 1
|
||||
case 'testnet':
|
||||
prefix = '6f'
|
||||
break // m or n
|
||||
case 'p2sh':
|
||||
prefix = '05'
|
||||
break // 3
|
||||
default:
|
||||
prefix = '00'
|
||||
}
|
||||
return null;
|
||||
return bs58check.encode(Buffer.from(prefix + h160, 'hex')) // wallet import format
|
||||
} else if (coin === 'ETH') {
|
||||
// 目前不支持 ETH或其他币种 地址转换,因为这会大量增加前端打包的js。
|
||||
const uncompressedPubkey = crypto.ECDH.convertKey(
|
||||
pubkey,
|
||||
curve,
|
||||
'hex',
|
||||
'buffer',
|
||||
'uncompressed'
|
||||
)
|
||||
return '0x' + keccak256(uncompressedPubkey.slice(1)).slice(24)
|
||||
}
|
||||
}
|
||||
,
|
||||
pass2keypair: function (pass, option) { // 如果使用其他机制,例如密码、随机数,不使用secword,也可生成keypair
|
||||
if (this.isHashable(pass)) {
|
||||
option = option || {};
|
||||
option.hasher = my.HASHER_LIST.indexOf(option.hasher) >= 0 ? option.hasher : my.HASHER;
|
||||
var hashBuf = crypto.createHash(option.hasher).update(pass).digest();
|
||||
var keypair = nacl.sign.keyPair.fromSeed(hashBuf);
|
||||
return {
|
||||
hash: hashBuf.toString('hex'),
|
||||
pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
|
||||
seckey: Buffer.from(keypair.secretKey).toString('hex')
|
||||
};
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
},
|
||||
secword2seed: function (secword, pass) {
|
||||
// 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样,是64字节的种子。
|
||||
if (Secword.isValid(secword)) {
|
||||
// bip39.validateMnemonic(secword)) {
|
||||
return new Secword(secword).toSeed(pass).toString('hex') // 结果一致于 bip39.mnemonicToSeedHex(secword) 或 ethers.HDNode.mnemonic2Seed(secword)
|
||||
}
|
||||
,
|
||||
secword2keypair: function (secword, option = {coin: my.COIN, path: 'master'}) { // option.coin 币种;option.passphase 密码,默认为空;option.path==='master' 生成 HD master key,不定义则默认为相应币种的第一对公私钥。
|
||||
if (Secword.isValid(secword)) {
|
||||
const { coin = my.COIN, path = 'master', pass = '' } = option;
|
||||
// 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。
|
||||
// let hdmaster=HDKey.fromMasterSeed(new Buffer(this.secword2seed(secword, option.pass), 'hex')) // 和 new Secword(secword).toHDPrivateKey 求出的公私钥一样!
|
||||
let hdmaster = new Secword(secword).toHDPrivateKey(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;
|
||||
if (path === 'master') {
|
||||
key = hdmaster;
|
||||
} else if (!path) {
|
||||
switch (coin) {
|
||||
case 'BTC': key = hdmaster.derive("m/44'/0'/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;
|
||||
}
|
||||
} else { // 指定了路径 option.path,例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
|
||||
key = hdmaster.derive(path);
|
||||
}
|
||||
return {
|
||||
coin,
|
||||
path,
|
||||
seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果
|
||||
pubkey: key.publicKey.toString('hex')
|
||||
};
|
||||
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
},
|
||||
randomSecword: function (lang) {
|
||||
// Object.keys(Secword.Words) => [ 'CHINESE', 'ENGLISH', 'FRENCH', 'ITALIAN', 'JAPANESE', 'SPANISH' ]
|
||||
lang =
|
||||
lang && Secword.Words.hasOwnProperty(lang.toUpperCase())
|
||||
? lang.toUpperCase()
|
||||
: 'ENGLISH'
|
||||
return new Secword(Secword.Words[lang]).phrase
|
||||
},
|
||||
randomSeckey: function () {
|
||||
return Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
|
||||
},
|
||||
randomKeypair: function () {
|
||||
// 此函数有错!!
|
||||
let kp = nacl.box.keyPair()
|
||||
const seckey = Buffer.from(kp.secretKey).toString('hex')
|
||||
const pubkey = this.seckey2pubkey(seckey)
|
||||
return {
|
||||
seckey,
|
||||
pubkey
|
||||
}
|
||||
,
|
||||
seckey2pubkey: function (seckey, option = {}) {
|
||||
if (this.isSeckey(seckey) && seckey.length === 64) { // 只能用于32字节的私钥(BTC, ETH)。也就是不能用于 TIC 的私钥。
|
||||
const {curve = my.CURVE, compress = 'compressed'} = option;
|
||||
return new crypto.ECDH(curve).setPrivateKey(seckey, 'hex').getPublicKey('hex', compress).toString('hex'); // ecdh.getPublicKey(不加参数) 默认为 'uncompressed'
|
||||
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。
|
||||
// 或者 require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'),compress).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') 得到的公钥与上面的不同
|
||||
}
|
||||
return null;
|
||||
},
|
||||
randomString: function (length = 6, alphabet) {
|
||||
// 长度为 length,字母表为 alphabet 的随机字符串
|
||||
alphabet =
|
||||
alphabet ||
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@'
|
||||
var text = ''
|
||||
for (var i = 0; i < length; i++) {
|
||||
text += alphabet.charAt(Math.floor(Math.random() * alphabet.length))
|
||||
}
|
||||
,
|
||||
secword2account: function (secword, option) { // account 比 keypair 多了 address 字段。
|
||||
option = option || {};
|
||||
option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN;
|
||||
let kp = this.secword2keypair(secword, option);
|
||||
if (kp) {
|
||||
kp.address = this.pubkey2address(kp.pubkey, option);
|
||||
return kp;
|
||||
}
|
||||
return null;
|
||||
return text
|
||||
},
|
||||
randomNumber: function (option) {
|
||||
// 长度为 option.length 的随机数字,或者 (option.min||0) <= num < option.max
|
||||
option = option || {}
|
||||
let num = 0
|
||||
if (option.length > 0) {
|
||||
num = parseInt(Math.random() * Math.pow(10, option.length))
|
||||
let l = new String(num).length
|
||||
while (l < option.length) {
|
||||
num = '0' + num // 注意,这时返回的是字符串!
|
||||
l++
|
||||
}
|
||||
} else if (option.max > 0) {
|
||||
option.min = option.min >= 0 ? option.min : 0
|
||||
num = parseInt(Math.random() * (option.max - option.min)) + option.min
|
||||
} else {
|
||||
// 如果 option 为空
|
||||
num = Math.random()
|
||||
}
|
||||
,
|
||||
secword2address: function (secword, option) {
|
||||
option = option || {};
|
||||
option.coin = my.COIN_LIST.indexOf(option.coin) >= 0 ? option.coin : my.COIN;
|
||||
let kp = this.secword2keypair(secword, option);
|
||||
if (kp) {
|
||||
return this.pubkey2address(kp.pubkey, option);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
,
|
||||
isSecword: function (secword) {
|
||||
return Secword.isValid(secword);
|
||||
}
|
||||
,
|
||||
isSeckey: function (seckey) {
|
||||
// 比特币、以太坊的私钥:64 hex
|
||||
// nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
|
||||
return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey);
|
||||
}
|
||||
,
|
||||
isPubkey: function (pubkey) {
|
||||
// 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
|
||||
// 以太坊的公钥:'02|03' + 64 hex
|
||||
// nacl.sign 的公钥:64 hex
|
||||
return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey); // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
|
||||
}
|
||||
,
|
||||
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)) {
|
||||
}
|
||||
,
|
||||
pubkey2address: function (pubkey, option = {}) { // pubkey 应当是string类型
|
||||
if (this.isPubkey(pubkey)) {
|
||||
const { coin = 'BTC', netType = 'mainnet', curve = my.CURVE } = option;
|
||||
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest();
|
||||
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex');
|
||||
let prefix;
|
||||
if (coin === 'BTC') {
|
||||
switch (netType) {
|
||||
case 'mainnet': prefix = '00'; break; // 1
|
||||
case 'testnet': prefix = '6f'; break; // m or n
|
||||
case 'p2sh': prefix = '05'; break; // 3
|
||||
default: prefix = '00';
|
||||
}
|
||||
return bs58check.encode(Buffer.from(prefix + h160, 'hex')); // wallet import format
|
||||
} else if (coin === 'ETH') { // 目前不支持 ETH或其他币种 地址转换,因为这会大量增加前端打包的js。
|
||||
const uncompressedPubkey = crypto.ECDH.convertKey(pubkey, curve, 'hex', 'buffer', 'uncompressed');
|
||||
return '0x' + keccak256(uncompressedPubkey.slice(1)).slice(24);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
,
|
||||
secword2seed: function (secword, pass) { // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样,是64字节的种子。
|
||||
if (Secword.isValid(secword)) { // bip39.validateMnemonic(secword)) {
|
||||
return new Secword(secword).toSeed(pass).toString('hex'); // 结果一致于 bip39.mnemonicToSeedHex(secword) 或 ethers.HDNode.mnemonic2Seed(secword)
|
||||
}
|
||||
return null;
|
||||
}
|
||||
,
|
||||
randomSecword: function (lang) { // Object.keys(Secword.Words) => [ 'CHINESE', 'ENGLISH', 'FRENCH', 'ITALIAN', 'JAPANESE', 'SPANISH' ]
|
||||
lang = (lang && Secword.Words.hasOwnProperty(lang.toUpperCase())) ? lang.toUpperCase() : 'ENGLISH';
|
||||
return new Secword(Secword.Words[lang]).phrase;
|
||||
}
|
||||
,
|
||||
randomSeckey: function () {
|
||||
return Buffer.from(nacl.box.keyPair().secretKey).toString('hex'); // 32字节
|
||||
}
|
||||
,
|
||||
randomKeypair: function () {
|
||||
// 此函数有错!!
|
||||
let kp = nacl.box.keyPair();
|
||||
const seckey = Buffer.from(kp.secretKey).toString('hex');
|
||||
const pubkey = this.seckey2pubkey(seckey);
|
||||
return {
|
||||
seckey, pubkey
|
||||
};
|
||||
}
|
||||
,
|
||||
randomString: function (length = 6, alphabet) { // 长度为 length,字母表为 alphabet 的随机字符串
|
||||
alphabet = alphabet || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@";
|
||||
var text = '';
|
||||
for (var i = 0; i < length; i++) {
|
||||
text += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
,
|
||||
randomNumber: function (option) { // 长度为 option.length 的随机数字,或者 (option.min||0) <= num < option.max
|
||||
option = option || {};
|
||||
let num = 0;
|
||||
if (option.length > 0) {
|
||||
num = parseInt(Math.random() * Math.pow(10, option.length));
|
||||
let l = new String(num).length;
|
||||
while (l < option.length) {
|
||||
num = '0' + num; // 注意,这时返回的是字符串!
|
||||
l++;
|
||||
}
|
||||
} else if (option.max > 0) {
|
||||
option.min = (option.min >= 0) ? option.min : 0;
|
||||
num = parseInt(Math.random() * (option.max - option.min)) + option.min;
|
||||
} else { // 如果 option 为空
|
||||
num = Math.random();
|
||||
}
|
||||
return num;
|
||||
}
|
||||
,
|
||||
rsaSign: function (string2Sign, prikey, signType) {
|
||||
signType = signType || 'RSA-SHA1'; // could be RSA-SHA256, RSA-SHA1 or more
|
||||
let signer = crypto.createSign(signType);
|
||||
return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'));
|
||||
}
|
||||
,
|
||||
rsaVerify: function (string2Verify, sign, pubkey, signType) {
|
||||
signType = signType || 'RSA-SHA1'; // could be RSA-SHA256, RSA-SHA1 or more
|
||||
let verifier = crypto.createVerify(signType);
|
||||
return verifier.update(string2Verify).verify(pubkey, sign, 'base64');
|
||||
}
|
||||
};
|
||||
return num
|
||||
},
|
||||
rsaSign: function (string2Sign, prikey, signType) {
|
||||
signType = signType || 'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
|
||||
let signer = crypto.createSign(signType)
|
||||
return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'))
|
||||
},
|
||||
rsaVerify: function (string2Verify, sign, pubkey, signType) {
|
||||
signType = signType || 'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
|
||||
let verifier = crypto.createVerify(signType)
|
||||
return verifier.update(string2Verify).verify(pubkey, sign, 'base64')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user