用原生 BigInt 实现了 modPow 从而重新实现了 decompress_pubkey; 调整 prikey_to_pubkey({compress}) 为 prikey_to_pubkey({uncompress})
This commit is contained in:
parent
40197b2905
commit
acabbfa787
99
ticc.js
99
ticc.js
@ -1,5 +1,5 @@
|
||||
// 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 nacl = require('tweetnacl')
|
||||
const bs58check = require('bs58check')
|
||||
@ -611,19 +611,19 @@ class TicCrypto {
|
||||
* @return {*}
|
||||
* @memberof TicCrypto
|
||||
*/
|
||||
static prikey_to_pubkey ({ prikey, curve, compress } = {}) {
|
||||
static prikey_to_pubkey ({ prikey, curve, uncompress } = {}) {
|
||||
if (this.is_prikey({ prikey }) && prikey.length === 64) {
|
||||
// 只能用于32字节的私钥(BTC, ETH)。也就是不能用于 TIC 的私钥。
|
||||
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。
|
||||
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') // 可用于浏览器
|
||||
// 或者 const ecc = require('eccrypto')
|
||||
// if (compress===false){
|
||||
// return ecc.getPublic(this.hex_to_buf(prikey)).toString('hex')
|
||||
// }else{
|
||||
// if (!uncompress){
|
||||
// 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') 得到的公钥与上面的不同
|
||||
} else if (this.is_prikey({ prikey }) && prikey.length === 128) {
|
||||
@ -648,11 +648,12 @@ class TicCrypto {
|
||||
if (this.is_prikey({ prikey })) {
|
||||
/** @type {*} */
|
||||
let pubkey
|
||||
if (coin === 'ETH') {
|
||||
pubkey = this.prikey_to_pubkey({ prikey, compress: false })
|
||||
if (coin === 'ETH' || coinFamily === 'ETH') {
|
||||
pubkey = this.prikey_to_pubkey({ prikey, uncompress: true })
|
||||
return this.pubkey_to_address({ pubkey, coin, coinFamily, world })
|
||||
// 实际上发现,不论是否 compressed,最后转成的地址都是一样的,因为在 pubkey_to_position 里已经自动处理了。
|
||||
} 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 })
|
||||
}
|
||||
}
|
||||
@ -924,7 +925,7 @@ class TicCrypto {
|
||||
}
|
||||
} else {
|
||||
let prikey = this.randomize_seckey()
|
||||
let pubkey = this.prikey_to_pubkey({ prikey })
|
||||
let pubkey = this.prikey_to_pubkey({ prikey, uncompress: false })
|
||||
return {
|
||||
prikey,
|
||||
pubkey,
|
||||
@ -1413,7 +1414,7 @@ class TicCrypto {
|
||||
* 压缩公钥
|
||||
*
|
||||
* @static
|
||||
* @param {*} uncompressed
|
||||
* @param {*} uncompressed: strings like '0x1234567890abcedf...'
|
||||
* @return {*}
|
||||
* @memberof TicCrypto
|
||||
*/
|
||||
@ -1434,6 +1435,56 @@ class TicCrypto {
|
||||
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 {*}
|
||||
* @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
|
||||
static decompress_pubkey_bigint (compressed) {
|
||||
const bigint = globalThis.bigint || require('big-integer')
|
||||
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);
|
||||
var signY = new Number(compressed[1]) - 2
|
||||
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
|
||||
if (y.mod(2).toJSNumber() !== signY) {
|
||||
// If the parity doesn't match it's the *other* root
|
||||
|
Loading…
Reference in New Issue
Block a user