用原生 BigInt 实现了 modPow 从而重新实现了 decompress_pubkey; 调整 prikey_to_pubkey({compress}) 为 prikey_to_pubkey({uncompress})

This commit is contained in:
Luk 2024-10-05 18:04:24 +08:00
parent 40197b2905
commit acabbfa787

99
ticc.js
View File

@ -1,5 +1,5 @@
// const bignum=require('bignumber.js') // 处理整数 https://github.com/MikeMcl/bignumber.js // size: 360K // const bignum=require('bignumber.js') // 处理整数 https://github.com/MikeMcl/bignumber.js // size: 360K
const bigint = require('big-integer') // 处理整数 https://github.com/peterolson/BigInteger.js // size: 188K. ethers.js 24M. // 20241005 发现,在 node 控制台里,导入本库命名为 BigInt 后运行 BigInt(xxx) 会导致失败!因为现在已经有 JS 的新 primitive 也叫 BigInt, 不知道作为 server 运行时,怎么没有报错。改名为 bigint // const bigint = require('big-integer') // 处理整数 https://github.com/peterolson/BigInteger.js // size: 188K. ethers.js 24M. // 20241005 发现,在 node 控制台里,导入本库命名为 BigInt 后运行 BigInt(xxx) 会导致失败!因为现在已经有 JS 的新 primitive 也叫 BigInt, 不知道作为 server 运行时,怎么没有报错。改名为 bigint
const crypto = require('crypto') const crypto = require('crypto')
const nacl = require('tweetnacl') const nacl = require('tweetnacl')
const bs58check = require('bs58check') const bs58check = require('bs58check')
@ -611,19 +611,19 @@ class TicCrypto {
* @return {*} * @return {*}
* @memberof TicCrypto * @memberof TicCrypto
*/ */
static prikey_to_pubkey ({ prikey, curve, compress } = {}) { static prikey_to_pubkey ({ prikey, curve, uncompress } = {}) {
if (this.is_prikey({ prikey }) && prikey.length === 64) { if (this.is_prikey({ prikey }) && prikey.length === 64) {
// 只能用于32字节的私钥BTC, ETH)。也就是不能用于 TIC 的私钥。 // 只能用于32字节的私钥BTC, ETH)。也就是不能用于 TIC 的私钥。
curve = my.CURVE_LIST.includes(curve) ? curve : my.CURVE // 默认为 secp256k1 curve = my.CURVE_LIST.includes(curve) ? curve : my.CURVE // 默认为 secp256k1
// return new crypto.createECDH(curve).setPrivateKey(prikey,'hex').getPublicKey('hex', compress===false?'uncompressed':'compressed') // ecdh.getPublicKey(不加参数) 默认为 'compressed'。用 HBuilderX 2.6.4 打包成ios或安卓 app 后 setPrivateKey() 报错TypeError: null is not an object (evaluating 'this.rand.getBytes') // return new crypto.createECDH(curve).setPrivateKey(prikey,'hex').getPublicKey('hex', uncompress?'uncompressed':'compressed') // ecdh.getPublicKey(不加参数) 默认为 'compressed'。用 HBuilderX 2.6.4 打包成ios或安卓 app 后 setPrivateKey() 报错TypeError: null is not an object (evaluating 'this.rand.getBytes')
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。但可惜,浏览器里不存在 crypto.ECDH。 // 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。但可惜,浏览器里不存在 crypto.ECDH。
return this.buf_to_hex(secp256k1.publicKeyCreate(Buffer.from(prikey, 'hex'), compress !== false)) // 可用于浏览器。缺省输出压缩公钥,compress=false时输出非压缩公钥 return this.buf_to_hex(secp256k1.publicKeyCreate(Buffer.from(prikey, 'hex'), !uncompress)) // 可用于浏览器。缺省输出压缩公钥,第二个参数必须正好为false时输出非压缩公钥为undefined或true时输出压缩公钥其他时报错
// 或者 bitcorelib.PublicKey.fromPrivateKey(new bitcorelib.PrivateKey(prikey)).toString('hex') // 可用于浏览器 // 或者 bitcorelib.PublicKey.fromPrivateKey(new bitcorelib.PrivateKey(prikey)).toString('hex') // 可用于浏览器
// 或者 const ecc = require('eccrypto') // 或者 const ecc = require('eccrypto')
// if (compress===false){ // if (!uncompress){
// return ecc.getPublic(this.hex_to_buf(prikey)).toString('hex')
// }else{
// return ecc.getPublicCompressed(this.hex_to_buf(prikey)).toString('hex') // return ecc.getPublicCompressed(this.hex_to_buf(prikey)).toString('hex')
// } else{
// return ecc.getPublic(this.hex_to_buf(prikey)).toString('hex')
// } // }
// 注意Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(prikey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同 // 注意Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(prikey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
} else if (this.is_prikey({ prikey }) && prikey.length === 128) { } else if (this.is_prikey({ prikey }) && prikey.length === 128) {
@ -648,11 +648,12 @@ class TicCrypto {
if (this.is_prikey({ prikey })) { if (this.is_prikey({ prikey })) {
/** @type {*} */ /** @type {*} */
let pubkey let pubkey
if (coin === 'ETH') { if (coin === 'ETH' || coinFamily === 'ETH') {
pubkey = this.prikey_to_pubkey({ prikey, compress: false }) pubkey = this.prikey_to_pubkey({ prikey, uncompress: true })
return this.pubkey_to_address({ pubkey, coin, coinFamily, world }) return this.pubkey_to_address({ pubkey, coin, coinFamily, world })
// 实际上发现,不论是否 compressed最后转成的地址都是一样的因为在 pubkey_to_position 里已经自动处理了。
} else { } else {
pubkey = this.prikey_to_pubkey({ prikey, compress: true }) pubkey = this.prikey_to_pubkey({ prikey, uncompress: false })
return this.pubkey_to_address({ pubkey, coin, coinFamily, world }) return this.pubkey_to_address({ pubkey, coin, coinFamily, world })
} }
} }
@ -924,7 +925,7 @@ class TicCrypto {
} }
} else { } else {
let prikey = this.randomize_seckey() let prikey = this.randomize_seckey()
let pubkey = this.prikey_to_pubkey({ prikey }) let pubkey = this.prikey_to_pubkey({ prikey, uncompress: false })
return { return {
prikey, prikey,
pubkey, pubkey,
@ -1413,7 +1414,7 @@ class TicCrypto {
* 压缩公钥 * 压缩公钥
* *
* @static * @static
* @param {*} uncompressed * @param {*} uncompressed: strings like '0x1234567890abcedf...'
* @return {*} * @return {*}
* @memberof TicCrypto * @memberof TicCrypto
*/ */
@ -1434,6 +1435,56 @@ class TicCrypto {
return null // 非压缩公钥有错误。 return null // 非压缩公钥有错误。
} }
/**
*
* decompress_pubkey 需要用到 big-integer modPow 方法如果想用原生的 BigInt就需要自己实现 modPow
* @param {*} base
* @param {*} exponent
* @param {*} modulus
* @returns
*/
static modPow (base, exponent, modulus) {
let result = BigInt(1)
base = BigInt(base)
exponent = BigInt(exponent)
modulus = BigInt(modulus)
while (exponent > 0n) {
if (exponent & BigInt(1)) {
result = (result * base) % modulus
}
base = (base * base) % modulus
exponent = exponent >> BigInt(1)
}
return result
}
/**
* 解压缩公钥
*
* @static
* @param {*} compressed: strings like '020123456789abcdef...'
* @return {*}
* @memberof TicCrypto
*/
static decompress_pubkey (compressed = '') {
// uncompress: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/53478265#53478265
// https://en.bitcoin.it/wiki/Secp256k1
// 把 02x 或 03x 的压缩公钥 转成 04xy 的非压缩公钥
// Consts for secp256k1 curve. Adjust accordingly
const prime = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16) // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
const pIdent = BigInt('0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
var signY = BigInt(Number(compressed[1]) - 2)
var x = BigInt('0x' + compressed.substr(2))
var y = this.modPow((this.modPow(x, 3, prime) + BigInt(7)) % prime, pIdent, prime) // y mod p = +-(x^3 + 7)^((p+1)/4) mod p
if (y % BigInt(2) !== signY) {
// If the parity doesn't match it's the *other* root
y = prime - y
}
return '04' + this.padStart(x.toString(16), 64, '0') + this.padStart(y.toString(16), 64, '0')
}
/** /**
* 解压缩公钥 * 解压缩公钥
* *
@ -1442,32 +1493,12 @@ class TicCrypto {
* @return {*} * @return {*}
* @memberof TicCrypto * @memberof TicCrypto
*/ */
static decompress_pubkey (compressed) { static decompress_pubkey_bigint (compressed) {
// uncompress: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/53478265#53478265 const bigint = globalThis.bigint || require('big-integer')
// https://en.bitcoin.it/wiki/Secp256k1
// 把 02x 或 03x 的压缩公钥 转成 04xy 的非压缩公钥
// Consts for secp256k1 curve. Adjust accordingly
const prime = bigint('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16) // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 const prime = bigint('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16) // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
const pIdent = bigint('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4); const pIdent = bigint('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
var signY = new Number(compressed[1]) - 2 var signY = new Number(compressed[1]) - 2
var x = bigint(compressed.substr(2), 16) var x = bigint(compressed.substr(2), 16)
// 需要用到 big-integer 的 modPow 方法。如果想用原生的 BigInt可以自己实现 modPow:
/*
function modPow(base, exponent, modulus) {
let result = BigInt(1);
base = BigInt(base);
while (exponent > 0n) {
if (exponent & BigInt(1)) {
result = (result * base) % modulus;
}
base = (base * base) % modulus;
exponent = exponent >> BigInt(1);
}
return result;
}
*/
var y = x.modPow(3, prime).add(7).mod(prime).modPow(pIdent, prime) // y mod p = +-(x^3 + 7)^((p+1)/4) mod p 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 (y.mod(2).toJSNumber() !== signY) {
// If the parity doesn't match it's the *other* root // If the parity doesn't match it's the *other* root