用eccrypto加解密,用crypto签名。互相转换压缩和非压缩公钥
This commit is contained in:
300
index.js
300
index.js
@@ -1,17 +1,22 @@
|
||||
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
|
||||
const BigInt = require("big-integer") // 处理整数 https://github.com/peterolson/BigInteger.js
|
||||
const crypto=require('crypto')
|
||||
const nacl = require('tweetnacl')
|
||||
const bs58check = require('bs58check')
|
||||
const uuid = require('uuid')
|
||||
const keccak = require('keccak')
|
||||
const eccrypto = require('eccrypto') // 用于加解密。不知道怎么用 crypto 本身的加解密。
|
||||
const keyman = require('js-crypto-key-utils') // 转换原始密钥和 PER/DER 格式。
|
||||
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 里的相同功能
|
||||
// const bitcorelib = require('bitcore-lib')
|
||||
// const secp256k1 = require('secp256k1')
|
||||
|
||||
// 全部以hex为默认输入输出格式,方便人的阅读,以及方便函数之间统一接口
|
||||
|
||||
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 and much more。 可用 Crypto.getHashes/Ciphers/Curves() 查看支持的种类。
|
||||
my.HASHER_LIST=crypto.getHashes()
|
||||
my.CIPHER='aes-256-cfb' // 默认的加解密算法
|
||||
my.CIPHER_LIST=crypto.getCiphers()
|
||||
@@ -61,78 +66,117 @@ module.exports = {
|
||||
return false
|
||||
}
|
||||
,
|
||||
encrypt(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))
|
||||
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
|
||||
}
|
||||
,
|
||||
decrypt(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))
|
||||
let decrypted = decipher.update(data, inputEncoding, outputEncoding)
|
||||
decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
||||
if (option.format==='json') { // 如果用户输入错误密码,deciper也能返回结果。为了判断是否正确结果,对应当是 json 格式的原文做解析来验证。
|
||||
try{
|
||||
JSON.parse(decrypted)
|
||||
}catch(exception){
|
||||
return null
|
||||
}
|
||||
async encrypt(data, {keytype, key, input, output, cipher}={}){
|
||||
if (keytype==='pwd') {
|
||||
if (this.isHashable(data) && typeof(key)==='string') {
|
||||
let inputEncoding=my.INPUT_LIST.indexOf(input)>=0?input:my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
|
||||
let outputEncoding=(output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT) // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
|
||||
let cipher=crypto.createCipher(
|
||||
my.CIPHER_LIST.indexOf(cipher)>=0?cipher:my.CIPHER,
|
||||
this.hash(key))
|
||||
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 decrypted
|
||||
}else if (keytype==='pubkey') { // data 应当是 utf8 的字符串。// 但在浏览器里不能使用 Failed to execute 'encrypt' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
|
||||
let cipherobject = await eccrypto.encrypt(this.hex2buf(key), data)
|
||||
return cipherobject
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
sign(data, seckey, option) { // data can be string or buffer or object, results are the same
|
||||
async decrypt(data, {keytype, key, input, output, cipher, format}={}){ // data 应当是 encrypt 输出的数据类型
|
||||
if (keytype==='pwd') {
|
||||
if (data && (typeof(data)==='string' || data instanceof Buffer) && typeof(key)==='string') {
|
||||
let inputEncoding=my.OUTPUT_LIST.indexOf(input)>=0?input:my.OUTPUT // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
|
||||
let outputEncoding=(output==='buf')?undefined:(my.INPUT_LIST.indexOf(output)>=0?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(cipher)>=0?cipher:my.CIPHER,
|
||||
this.hash(key))
|
||||
let decrypted = decipher.update(data, inputEncoding, outputEncoding)
|
||||
decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
||||
if (format==='json') { // 如果用户输入错误密码,deciper也能返回结果。为了判断是否正确结果,对应当是 json 格式的原文做解析来验证。
|
||||
try{
|
||||
JSON.parse(decrypted)
|
||||
}catch(exception){
|
||||
return null
|
||||
}
|
||||
}
|
||||
return decrypted
|
||||
}else if (keytype==='seckey'){ // cipherobject 需要是 eccrypto 自身encrypt方法返回的对象
|
||||
let plaindata = await eccrypto.decrypt(Buffer.from(key, 'hex'), data) // eccrypto 需要调用 Buffer.compare 方法,不能在这里直接用 hex2buf
|
||||
return plaindata.toString('utf8')
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
/* 以下两个方法基于 eccrypto,注意其在浏览器上会有问题。
|
||||
*/
|
||||
async encryptPubkey(plaindata, pubkey, option){ // plaindata 应当是 utf8 的字符串
|
||||
let cipherobject = await eccrypto.encrypt(this.hex2buf(pubkey), plaindata)
|
||||
return cipherobject
|
||||
}
|
||||
,
|
||||
async decryptSeckey(cipherobject, seckey, option){ // cipherobject 需要是 eccrypto 自身encrypt方法返回的对象
|
||||
let plaindata
|
||||
if (Buffer) { // nodejs
|
||||
plaindata = await eccrypto.decrypt(Buffer.from(seckey, 'hex'), cipherobject) // eccrypto 需要调用 Buffer.compare 方法
|
||||
}else { // browser
|
||||
plaindata = await eccrypto.decrypt(this.hex2buf(seckey), cipherobject)
|
||||
}
|
||||
return plaindata.toString('utf8')
|
||||
}
|
||||
,
|
||||
async sign(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字节
|
||||
// 方案1: 使用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: 纯 crypto
|
||||
let seckeyPEM = await new keyman.Key('oct', this.hex2buf(seckey), {namedCurve:'P-256K'}).export('pem')
|
||||
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)
|
||||
signer.update(data, inputEncoding).end()
|
||||
let signature = signer.sign(seckeyPEM, outputEncoding)
|
||||
return signature // 发现同样的输入,每次调用会生成不同的 signature, 但都可以通过 verify。有一次我竟然徒手修改出一个新签名也通过验证。
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
isSignature(signature){
|
||||
return /^[a-fA-F0-9]{128}$/.test(signature)
|
||||
return /^[a-fA-F0-9]{128,144}$/.test(signature)
|
||||
}
|
||||
,
|
||||
verify (data, signature, pubkey, option) { // data could be anything, but converts to string or remains be Buffer/TypedArray/DataView
|
||||
async verify (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
|
||||
// 方案1: nacl
|
||||
// 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
|
||||
|
||||
// 方案2: 纯 crypto
|
||||
let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(pubkey), {namedCurve:'P-256K'}).export('pem')
|
||||
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 verifier = crypto.createVerify(hasher)
|
||||
verifier.update(data, inputEncoding).end() // end() 在 nodejs 12 里返回verifier自身,但在浏览器里返回 undefined,因此不能串联运行。
|
||||
let verified = verifier.verify(pubkeyPEM, signature, 'hex')
|
||||
return verified
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -230,16 +274,16 @@ module.exports = {
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
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 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'
|
||||
// if (option.compressed==='false'){
|
||||
// return ecc.getPublic(this.hex2arrbuf(seckey)).toString('hex')
|
||||
return new crypto.createECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex', option.compress===false?'uncompressed':'compressed') // ecdh.getPublicKey(不加参数) 默认为 'uncompressed'
|
||||
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。但可惜,浏览器里不存在 crypto.ECDH。
|
||||
// 或者 return this.buf2hex(require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'), option.compress!==false)) // 可用于浏览器。secp256k1缺省或true时输出压缩公钥,false时输出非压缩公钥。
|
||||
// 或者 bitcorelib.PublicKey.fromPrivateKey(new bitcorelib.PrivateKey(seckey)).toString('hex') // 可用于浏览器
|
||||
// 或者 const ecc = require('eccrypto')
|
||||
// if (option.compress===false){
|
||||
// return ecc.getPublic(this.hex2buf(seckey)).toString('hex')
|
||||
// }else{
|
||||
// return ecc.getPublicCompressed(this.hex2arrbuf(seckey)).toString('hex')
|
||||
// return ecc.getPublicCompressed(this.hex2buf(seckey)).toString('hex')
|
||||
// }
|
||||
// 从 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') 得到的公钥与上面的不同
|
||||
}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'))
|
||||
@@ -254,10 +298,10 @@ module.exports = {
|
||||
if (this.isSeckey(seckey)){
|
||||
let pubkey
|
||||
if (option.coin==='ETH'){
|
||||
pubkey = this.seckey2pubkey(seckey, {compress:'uncompressed'})
|
||||
pubkey = this.seckey2pubkey(seckey, {compress:false})
|
||||
return this.pubkey2address(pubkey, option)
|
||||
}else {
|
||||
pubkey = this.seckey2pubkey(seckey, {compress:'compressed'})
|
||||
pubkey = this.seckey2pubkey(seckey, {compress:true})
|
||||
return this.pubkey2address(pubkey, option)
|
||||
}
|
||||
}
|
||||
@@ -285,11 +329,70 @@ module.exports = {
|
||||
return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address) // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) {
|
||||
}
|
||||
,
|
||||
pubkey2address (pubkey, option) { // pubkey 应当是string类型
|
||||
option=option||{}
|
||||
pubkey2position (pubkey, {coin}={}){
|
||||
coin = my.COIN_LIST.indexOf(coin)>=0?coin:my.COIN
|
||||
if(this.isPubkey(pubkey)){
|
||||
if (coin==='ETH'){
|
||||
// 注意,必须要用非压缩的64字节的公钥的buffer,并去掉开头的 04。
|
||||
if (pubkey.length===66) {
|
||||
pubkey = this.decompressPubkey(pubkey)
|
||||
}
|
||||
return keccak('keccak256').update(Buffer.from(pubkey.slice(2),'hex')).digest('hex').slice(-40)
|
||||
}else {
|
||||
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
|
||||
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
|
||||
return h160
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
position2address(position, {coin, netType}={}){
|
||||
if (!position) return null
|
||||
coin = my.COIN_LIST.indexOf(coin)>=0?coin:my.COIN
|
||||
let address
|
||||
if (coin==='ETH'){ // 对以太坊,按照 EIP55,把纯位置转换为大小写敏感能自我验证的hex地址
|
||||
position = position.toLowerCase().replace('0x', '')
|
||||
let hash = keccak('keccak256').update(position).digest('hex')
|
||||
address = '0x'
|
||||
for (var i = 0; i < position.length; i++) {
|
||||
if (parseInt(hash[i], 16) >= 8) {
|
||||
address += position[i].toUpperCase()
|
||||
} else {
|
||||
address += position[i]
|
||||
}
|
||||
}
|
||||
return address
|
||||
}else if (coin === 'BTC'){ // 对比特币,把纯位置转换为大小写敏感能自我验证的bs58check地址
|
||||
let prefix
|
||||
switch (netType) {
|
||||
case 'mainnet': prefix='00'; break; // pubkey hash => 1
|
||||
case 'mainnetSh': prefix='05'; break; // script hash => 3
|
||||
case 'testnet': prefix='6f'; break; // testnet pubkey hash => m or n
|
||||
case 'testnetSh': prefix='c4'; break // testnet script hash => 2
|
||||
case 'namecoin': prefix='34'; break; // Namecoin pubkey hash => M or N
|
||||
case 'compact': prefix='15'; break; // compact pubkey (proposed) => 4
|
||||
default: prefix='00'
|
||||
}
|
||||
address=bs58check.encode(Buffer.from(prefix+position, 'hex')) // wallet import format
|
||||
return address
|
||||
}else {
|
||||
let prefix
|
||||
switch (netType){
|
||||
case 'mainnet': prefix='42'; break; // '42'=>T, '6E'=>m
|
||||
case 'testnet': prefix='7F'; break; // '7F'=>t
|
||||
case 'devnet': prefix='5A'; break; // '5A'=>d
|
||||
default: prefix='42' // 默认暂且为 42,为了兼容已经运行的链。
|
||||
}
|
||||
address=bs58check.encode(Buffer.from(prefix+position, 'hex')) // wallet import format
|
||||
return address
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
pubkey2address (pubkey, option={}) { // pubkey 应当是string类型
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
if (this.isPubkey(pubkey)) {
|
||||
option = option||{}
|
||||
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
|
||||
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
|
||||
let prefix
|
||||
@@ -308,7 +411,7 @@ module.exports = {
|
||||
default: prefix='00'
|
||||
}
|
||||
}else if (option.coin==='ETH'){
|
||||
// 注意,必须要用非压缩的64字节的公钥的buffer。
|
||||
// 注意,必须要用非压缩的64字节的公钥的buffer,并去掉开头的 04。
|
||||
return '0x' + keccak('keccak256').update(Buffer.from(pubkey.slice(2),'hex')).digest('hex').slice(-40)
|
||||
// 或 const { keccak256 } = require('ethereumjs-util'); keccak256(Buffer.from(pubkey.slice(2),'hex)).toString('hex').slice(40)
|
||||
// 或 const { Keccak } = require('sha3'); new Keccak('').update(Bufer.from(pubkey.slice(2),'hex')).digest('hex').slice(-40)
|
||||
@@ -334,13 +437,13 @@ module.exports = {
|
||||
return new Secword(Secword.Words[language]).phrase
|
||||
}
|
||||
,
|
||||
randomSeckey(option){
|
||||
randomSeckey(option){ // todo: 使用 crypto.randomBytes(size)
|
||||
option=option||{}
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
if (option.coin==='TIC'){
|
||||
return Buffer.from(nacl.sign.keyPair().secretKey).toString('hex') // 64字节
|
||||
return crypto.randomBytes(64).toString('hex') // Buffer.from(nacl.sign.keyPair().secretKey).toString('hex') // 64字节
|
||||
}else{
|
||||
return Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
|
||||
return crypto.randomBytes(32).toString('hex') // Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
|
||||
}
|
||||
}
|
||||
,
|
||||
@@ -436,7 +539,7 @@ module.exports = {
|
||||
distanceSig(hash, sig){ // hash为64hex字符,sig为128hex字符。返回用hex表达的距离。
|
||||
if (this.isSignature(sig) && this.isHash(hash)){
|
||||
var hashSig=this.hash(sig) // 把签名也转成32字节的哈希,同样长度方便比较
|
||||
return new BigNumber(hash,16).minus(new BigNumber(hashSig,16)).abs().toString(16)
|
||||
return new BigInt(hash,16).sub(new BigInt(hashSig,16)).abs().toString(16)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -520,14 +623,55 @@ module.exports = {
|
||||
return verifier.update(string2Verify).verify(pubkey, sign, 'base64')
|
||||
}
|
||||
,
|
||||
arrbuf2hex(buffer) { // buffer is an ArrayBuffer
|
||||
buf2hex(buffer) { // buffer is an ArrayBuffer
|
||||
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
|
||||
}
|
||||
,
|
||||
hex2arrbuf(hex){
|
||||
hex2buf(hex){
|
||||
return new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) {
|
||||
return parseInt(h, 16)
|
||||
})) // 注意,arraybuffer没有 toString('hex')功能
|
||||
})) // 注意,arraybuffer没有 toString('hex')功能, Buffer才有。
|
||||
}
|
||||
,
|
||||
hex2base58check(hex){
|
||||
return bs58check.encode(Buffer.from(hex, 'hex'))
|
||||
}
|
||||
,
|
||||
base58check2hex(box){
|
||||
try{
|
||||
return bs58check.decode(box).toString('hex').toUpperCase()
|
||||
}catch(exception){
|
||||
return null
|
||||
}
|
||||
}
|
||||
,
|
||||
hex2eip55(){
|
||||
|
||||
}
|
||||
,
|
||||
// test: https://iancoleman.io/bitcoin-key-compression/
|
||||
// compress: https://hacpai.com/article/1550844562914
|
||||
compressPubkey(uncompressed){
|
||||
let [all, x, y]=uncompressed.toLowerCase().match(/^04(.{64})(.{64})$/)
|
||||
if (/[1,3,5,7,9,b,d,f]$/.test(y)){
|
||||
return '03'+x // y为奇数=>前缀03
|
||||
}else{
|
||||
return '02'+x // y为偶数=>前缀02
|
||||
}
|
||||
}
|
||||
,
|
||||
// uncompress: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/53478265#53478265
|
||||
// https://en.bitcoin.it/wiki/Secp256k1
|
||||
decompressPubkey(compressed){
|
||||
// Consts for secp256k1 curve. Adjust accordingly
|
||||
const prime = new BigInt('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16) // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
|
||||
const pIdent = new BigInt('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
|
||||
var signY = new Number(compressed[1]) - 2
|
||||
var x = new BigInt(compressed.substring(2), 16)
|
||||
var y = x.modPow(3, prime).add(7).mod(prime).modPow( pIdent, prime ) // y mod p = +-(x^3 + 7)^((p+1)/4) mod p
|
||||
if( y.mod(2).toJSNumber() !== signY ) { // If the parity doesn't match it's the *other* root
|
||||
y = prime.subtract( y ) // y = prime - y
|
||||
}
|
||||
return '04' + x.toString(16).padStart(64, '0') + y.toString(16).padStart(64, '0')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user