为避免 nodejs 的 crypto 的 sign 产生的签名不固定,换用 eccrypto 的 sign 做为默认。
This commit is contained in:
parent
354a90a8ad
commit
cbd46d62a7
67
index.js
67
index.js
@ -66,14 +66,14 @@ module.exports = {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
isSecword(secword){
|
isSecword(secword){ // 注意 not all 12 words combinations are valid for both bitcore and bip39. Must be generated automatically. 另外,实际上bitcore和bip39对12, 15, 18, ... 长度的合法助记词都返回 true。
|
||||||
//// for bitcore-mnemonic. 注意,bitcore-mnemonic 对少于12词的会抛出异常,很蠢。
|
//// for bitcore-mnemonic. 注意,bitcore-mnemonic 对少于12词的会抛出异常,很蠢。
|
||||||
// if (typeof secword==='string' && 12===secword.split(/ +/).length)
|
// if (typeof secword==='string' && 12===secword.split(/ +/).length)
|
||||||
// return BitcoreMnemonic.isValid(secword)
|
// return BitcoreMnemonic.isValid(secword)
|
||||||
// else
|
// else
|
||||||
// return false
|
// return false
|
||||||
|
|
||||||
//// for bip39. 注意,bip39对当前defaultWordlist之外其他语言的合法 mnemonic 也返回 false。所以不能直接 bip39.validateMnemonic(secword)
|
//// for bip39. 注意,bip39对当前defaultWordlist之外其他语言的合法 mnemonic 也返回 false,这一点不如 bitcore-mnemonic. 所以不能直接 bip39.validateMnemonic(secword)
|
||||||
if (typeof secword==='string' && 12===secword.split(/ +/).length)
|
if (typeof secword==='string' && 12===secword.split(/ +/).length)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
@ -94,7 +94,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
,
|
,
|
||||||
isSignature(signature){
|
isSignature(signature){
|
||||||
return /^[a-fA-F0-9]{128,144}$/.test(signature)
|
return /^[a-fA-F0-9]{128,144}$/.test(signature) && (signature.length % 2 === 0) // 128 for nacl, 140/142/144 for crypto and eccrypto in der format.
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
async encrypt(data, {keytype, key, input, output, cipher}={}){
|
async encrypt(data, {keytype, key, input, output, cipher}={}){
|
||||||
@ -145,14 +145,22 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
,
|
,
|
||||||
async sign(data, seckey, option={}) { // data can be string or buffer or object, results are the same
|
async sign(data, seckey, option={}) { // data can be string or buffer or object, results are the same
|
||||||
if (this.isHashable(data) && this.isSeckey(seckey) && seckey.length===64) { // 纯 crypto
|
if (this.isHashable(data) && this.isSeckey(seckey) && seckey.length===64) {
|
||||||
let seckeyPEM = await new keyman.Key('oct', this.hex2buf(seckey), {namedCurve:'P-256K'}).export('pem')
|
if (option.tool==='crypto') { // 纯 crypto
|
||||||
|
let seckeyPEM = await new keyman.Key('oct', this.hex2buf(seckey), {namedCurve:'P-256K'}).export('pem') // 私钥导出的der格式为144字节。
|
||||||
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 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)
|
||||||
signer.update(this.hash(data, option)).end()
|
signer.update(this.hash(data, option)).end()
|
||||||
let signature = signer.sign(seckeyPEM, 'hex')
|
let signature = signer.sign(seckeyPEM, 'hex')
|
||||||
return signature // 发现同样的输入,每次调用会生成不同的 signature, 且长度不定(140~144 hex) 但都可以通过 verify。有一次我竟然徒手修改出一个新签名也通过验证。
|
return signature // 发现同样的输入,每次调用会生成不同的 signature, 且长度不定(140,142,144 hex) 但都可以通过 verify。
|
||||||
|
}else if (option.tool==='nacl') {
|
||||||
|
// 这样不行,无法和verify共享一套公私钥。
|
||||||
|
// let naclSeckey = this.buf2hex(nacl.sign.keyPair.fromSeed(seckey).seckey)
|
||||||
|
// return await this.sign(data, naclSeckey, option)
|
||||||
|
}else { // default to eccrypto,因为它对同一组data,seckey生成的签名是固定的,观察到hex长度为140或142,是der格式。
|
||||||
|
let signature = await eccrypto.sign(Buffer.from(seckey,'hex'), crypto.createHash('sha256').update(data).digest())
|
||||||
|
return signature.toString('hex')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.isHashable(data) && this.isSeckey(seckey) && seckey.length===128) { // 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=128字符。
|
if (this.isHashable(data) && this.isSeckey(seckey) && seckey.length===128) { // 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=128字符。
|
||||||
option.output='buf' // 哈希必须输出为 buffer
|
option.output='buf' // 哈希必须输出为 buffer
|
||||||
@ -164,15 +172,29 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
,
|
,
|
||||||
async 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) && signature.length>=140){ // 纯 crypto
|
if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey) && signature.length>=140){
|
||||||
let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(pubkey), {namedCurve:'P-256K'}).export('pem')
|
if (option.tool==='crypto') { // 纯 crypto
|
||||||
|
let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(pubkey), {namedCurve:'P-256K'}).export('pem') // 公钥导出的der格式为88字节。经测试,同一对压缩和非压缩公钥得出的结果一模一样。
|
||||||
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 verifier = crypto.createVerify(hasher)
|
let verifier = crypto.createVerify(hasher)
|
||||||
verifier.update(this.hash(data, option)).end() // end() 在 nodejs 12 里返回verifier自身,但在浏览器里返回 undefined,因此不能串联运行。
|
verifier.update(this.hash(data, option)).end() // end() 在 nodejs 12 里返回verifier自身,但在浏览器里返回 undefined,因此不能串联运行。
|
||||||
let verified = verifier.verify(pubkeyPEM, signature, 'hex')
|
let verified = verifier.verify(pubkeyPEM, signature, 'hex') // 如果给signature添加1位hex,crypto 的 verify结果也是true! 估计因为一位hex不被转成字节。
|
||||||
return verified
|
return verified
|
||||||
|
}else if ('nacl'===option.tool) {
|
||||||
|
// 这样不行,无法和sign共享一套公私钥
|
||||||
|
// let naclPubkey = nacl.sign.keyPair.fromSeed()
|
||||||
|
}else { // 默认使用 eccrypto
|
||||||
|
try {
|
||||||
|
await eccrypto.verify(Buffer.from(pubkey, 'hex'),
|
||||||
|
crypto.createHash('sha256').update(data).digest(),
|
||||||
|
Buffer.from(signature, 'hex')) // 如果给signature添加1位hex,eccrypto 的 verify结果也是true! 估计因为一位hex不被转成字节。
|
||||||
|
return true
|
||||||
|
}catch(exception){
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
if (signature.length===128){ // nacl
|
}
|
||||||
|
}
|
||||||
|
if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey) && signature.length===128){ // nacl
|
||||||
option.output='buf' // 哈希必须输出为 buffer
|
option.output='buf' // 哈希必须输出为 buffer
|
||||||
let bufHash=this.hash(data, option)
|
let bufHash=this.hash(data, option)
|
||||||
let bufSignature = Buffer.from(signature, 'hex')
|
let bufSignature = Buffer.from(signature, 'hex')
|
||||||
@ -180,7 +202,7 @@ module.exports = {
|
|||||||
let verified = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
|
let verified = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
|
||||||
return verified
|
return verified
|
||||||
}
|
}
|
||||||
return null
|
return false
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
pass2keypair(pass, option){ // 如果使用其他机制,例如密码、随机数,不使用secword,也可生成keypair
|
pass2keypair(pass, option){ // 如果使用其他机制,例如密码、随机数,不使用secword,也可生成keypair
|
||||||
@ -188,7 +210,7 @@ module.exports = {
|
|||||||
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) // nacl的seed要求是32字节
|
||||||
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类型
|
||||||
@ -198,6 +220,14 @@ module.exports = {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
|
entropy2secword(entropy){ // entropy could be hex string or buffer. Byte length could be of 16, 20, 24, 28, ... which outputs mnemonic of length 12, 15, 18, 21, ...
|
||||||
|
return bip39.entropyToMnemonic(entropy) // results are the same for the same entropy.
|
||||||
|
}
|
||||||
|
,
|
||||||
|
secword2entropy(secword){ // secword could be of length 12, 15, 18, ... which outputs hex of length 32, 40, ...
|
||||||
|
return bip39.mnemonicToEntropy(secword) // results are the same for the same secword.
|
||||||
|
}
|
||||||
|
,
|
||||||
secword2keypair(secword, option){
|
secword2keypair(secword, option){
|
||||||
// option.coin 币种;
|
// option.coin 币种;
|
||||||
// option.passphase 密码,默认为空;
|
// option.passphase 密码,默认为空;
|
||||||
@ -256,6 +286,7 @@ module.exports = {
|
|||||||
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.secword=secword
|
||||||
kp.address=this.seckey2address(kp.seckey, option)
|
kp.address=this.seckey2address(kp.seckey, option)
|
||||||
return kp
|
return kp
|
||||||
}
|
}
|
||||||
@ -448,7 +479,7 @@ module.exports = {
|
|||||||
return bip39.generateMnemonic()
|
return bip39.generateMnemonic()
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
randomSeckey(option){ // todo: 使用 crypto.randomBytes(size)
|
randomSeckey(option){ // 跳过 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.tool==='nacl'){
|
if (option.tool==='nacl'){
|
||||||
@ -458,8 +489,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
randomKeypair(option){
|
randomKeypair(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.tool==='nacl'){
|
if (option.tool==='nacl'){
|
||||||
@ -482,6 +512,11 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
,
|
,
|
||||||
|
randomAccount(option={}){
|
||||||
|
let secword=this.randomSecword(option.lang)
|
||||||
|
return this.secword2account(secword, option)
|
||||||
|
}
|
||||||
|
,
|
||||||
randomString (length=6, alphabet) { // 长度为 length,字母表为 alphabet 的随机字符串
|
randomString (length=6, alphabet) { // 长度为 length,字母表为 alphabet 的随机字符串
|
||||||
alphabet = alphabet||"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@"
|
alphabet = alphabet||"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@"
|
||||||
var text = ''
|
var text = ''
|
||||||
|
Loading…
Reference in New Issue
Block a user