用原生 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 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
|
||||||
|
Loading…
Reference in New Issue
Block a user