diff --git a/README.horizontal.drawio b/README.horizontal.drawio
index 9c2cb86..1af10ad 100644
--- a/README.horizontal.drawio
+++ b/README.horizontal.drawio
@@ -213,7 +213,7 @@
-
+
@@ -222,7 +222,7 @@
-
+
diff --git a/ticc.js b/ticc.js
index c54f51c..b070a2a 100644
--- a/ticc.js
+++ b/ticc.js
@@ -31,6 +31,7 @@ my.INPUT = 'utf8' // 默认的加密方法的明文格式。utf8 能够兼容 la
my.INPUT_LIST = ['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView
my.COIN = 'TIC' // 默认的币种
my.COIN_LIST = ['TIC', 'BTC', 'ETH']
+my.WORLD = 'COMET'
my.REGEXP_ALPHABET = {
hex: /^[0-9a-fA-F]+$/,
b32: /^[A-Za-z2-7=]+$/,
@@ -142,7 +143,7 @@ class TicCrypto {
* @return {Boolean}
* @memberof TicCrypto
*/
- static is_seckey ({ prikey } = {}) {
+ static is_prikey ({ prikey } = {}) {
// 比特币、以太坊的私钥:64 hex
// nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(prikey)
@@ -230,8 +231,8 @@ class TicCrypto {
}
} else if (keytype === 'prikey') {
// 尚未走通,不能使用 ticc 生成的 Elliptic curve 椭圆曲线算法公私钥,只能用 crypto.generateKeyPairSync('rsa') 生成的 rsa 公私钥
- let seckeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
- return crypto.privateEncrypt(seckeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
+ let prikeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
+ return crypto.privateEncrypt(prikeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
} else if (keytype === 'pubkey') {
let pubkeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem')
return crypto.publicEncrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
@@ -274,8 +275,8 @@ class TicCrypto {
}
} else if (keytype === 'prikey') {
// 尚未走通,不能使用 ticc 生成的 Elliptic curve 椭圆曲线算法公私钥
- let seckeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
- return crypto.privateDecrypt(seckeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
+ let prikeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
+ return crypto.privateDecrypt(prikeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
} else if (keytype === 'pubkey') {
let pubkeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem')
return crypto.publicDecrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
@@ -295,24 +296,24 @@ class TicCrypto {
*/
static async sign_easy ({ data, prikey, tool = 'crypto', hasher = my.HASHER }) {
// data can be string or buffer or object, results are the same
- if (this.is_hashable({ data }) && this.is_seckey({ prikey })) {
+ if (this.is_hashable({ data }) && this.is_prikey({ prikey })) {
if (tool === 'nacl' && prikey.length === 128) {
- // 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=128字符。
+ // 使用 nacl 的签名算法。注意,nacl.sign 需要的 prikey 是64字节=128字符。
let hashBuf = this.hash_easy(data, { output: 'buf' }) // 哈希必须输出为 buffer
let signature = nacl.sign.detached(hashBuf, Buffer.from(prikey, 'hex'))
return Buffer.from(signature).toString('hex') // 签名是64节,128个hex字符
} else if (tool === 'eccrypto' && prikey.length === 64) {
- // eccrypto 对同一组data,seckey生成的签名是固定的,观察到hex长度为140或142,是der格式。
+ // eccrypto 对同一组data, prikey 生成的签名是固定的,观察到hex长度为140或142,是der格式。
let signature = await eccrypto.sign(Buffer.from(prikey, 'hex'), this.hash_easy(data, { output: 'buf' }))
return signature.toString('hex')
} else if (prikey.length === 64) {
// 纯 crypto
- let seckeyPEM = await new keyman.Key('oct', this.hex_to_buf(prikey), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
+ let prikeyPEM = await new keyman.Key('oct', this.hex_to_buf(prikey), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
let signer = crypto.createSign(hasher) // 注意,不知为何,hasher必须含有'sha'才能完成签名,例如 sha1, sha256, sha512, sha3, RSA-SHA1, id-rsassa-pkcs1-v1_5-with-sha3-224, 其他都会报错。
signer.update(this.hash_easy(data)).end()
- let signature = signer.sign(seckeyPEM, 'hex')
+ let signature = signer.sign(prikeyPEM, 'hex')
// since nodejs 12, 有了 crypto.sign 方法,但在浏览器中无效:
- // let signature = crypto.sign(hasher, Buffer.from(this.hash_easy(data)), seckeyPEM).toString('hex')
+ // let signature = crypto.sign(hasher, Buffer.from(this.hash_easy(data)), prikeyPEM).toString('hex')
return signature // 发现同样的输入,nodejs里每次调用会生成不同的 signature, 且长度不定(140,142,144 hex) 但都可以通过 verify。但在浏览器里调用,signature却是固定的。
}
}
@@ -441,7 +442,7 @@ class TicCrypto {
// 但可以不断延伸下去:/xxx/xxx/xxx/xxx/...
coin = coin?.toUpperCase?.() || my.COIN
- if (!this.is_secword(secword)) {
+ if (!this.is_secword({ secword })) {
// 由于 secword_to_seed 可以对一切字符串都正常返回,为防止secword为空,在这里先做检查。
return null
}
@@ -550,15 +551,20 @@ class TicCrypto {
* @return {Object}
* @memberof TicCrypto
*/
- static secword_to_account ({ secword, coin = my.COIN, world, pass, pathRoot, pathIndex, path, tool, hasher } = {}) {
+ static secword_to_account ({ secword, coin, world, pass, pathRoot, pathIndex, path, tool, hasher } = {}) {
// account 比 keypair 多了 address 字段。
coin = coin?.toUpperCase?.() || my.COIN
let kp = this.secword_to_keypair({ secword, coin, pass, pathRoot, pathIndex, path, tool, hasher })
if (kp) {
if (coin === 'ETH') {
+ world = world || 'mainnet'
let uncompressedPubkey = this.decompress_pubkey(kp.pubkey)
kp.address = this.pubkey_to_address({ pubkey: uncompressedPubkey, coin: 'ETH', world })
+ } else if (coin === 'BTC') {
+ world = world || 'mainnet'
+ kp.address = this.pubkey_to_address({ pubkey: kp.pubkey, coin, world })
} else {
+ world = world || my.WORLD
kp.address = this.pubkey_to_address({ pubkey: kp.pubkey, coin, world })
}
return Object.assign(kp, { coin, world, secword })
@@ -575,19 +581,9 @@ class TicCrypto {
* @return {String} address
* @memberof TicCrypto
*/
- static secword_to_address ({ secword, coin = my.COIN, world, pass, pathRoot, pathIndex, path, tool, hasher } = {}) {
- coin = coin?.toUpperCase?.() || my.COIN
- let kp = this.secword_to_keypair({ secword, coin, pass, pathRoot, pathIndex, path, tool, hasher })
- if (kp) {
- let address
- if (coin === 'ETH') {
- address = this.pubkey_to_address({ pubkey: this.decompress_pubkey(kp.pubkey), coin: 'ETH', world })
- } else {
- address = this.pubkey_to_address({ pubkey: kp.pubkey, coin, world })
- }
- return address
- }
- return null
+ static secword_to_address ({ secword, coin, world, pass, pathRoot, pathIndex, path, tool, hasher } = {}) {
+ const account = this.secword_to_account({ secword, coin, world, pass, pathRoot, pathIndex, path, tool, hasher })
+ return account?.address
}
/**
@@ -599,8 +595,8 @@ class TicCrypto {
* @return {*}
* @memberof TicCrypto
*/
- static seckey_to_pubkey ({ prikey, curve, compress } = {}) {
- if (this.is_seckey({ prikey }) && prikey.length === 64) {
+ static prikey_to_pubkey ({ prikey, curve, compress } = {}) {
+ 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')
@@ -614,7 +610,7 @@ class TicCrypto {
// return ecc.getPublicCompressed(this.hex_to_buf(prikey)).toString('hex')
// }
// 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(prikey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
- } else if (this.is_seckey({ prikey }) && prikey.length === 128) {
+ } else if (this.is_prikey({ prikey }) && prikey.length === 128) {
// 用于64字节=128 hex的 TIC 私钥
let keypair = nacl.sign.keyPair.fromSecretKey(Buffer.from(prikey, 'hex'))
return Buffer.from(keypair.publicKey).toString('hex') // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
@@ -631,16 +627,16 @@ class TicCrypto {
* @return {*}
* @memberof TicCrypto
*/
- static seckey_to_address ({ prikey, coin, world } = {}) {
+ static prikey_to_address ({ prikey, coin, world } = {}) {
coin = coin?.toUpperCase() || my.COIN
- if (this.is_seckey({ prikey })) {
+ if (this.is_prikey({ prikey })) {
/** @type {*} */
let pubkey
if (coin === 'ETH') {
- pubkey = this.seckey_to_pubkey({ prikey, compress: false })
+ pubkey = this.prikey_to_pubkey({ prikey, compress: false })
return this.pubkey_to_address({ pubkey: pubkey, coin, world })
} else {
- pubkey = this.seckey_to_pubkey({ prikey, compress: true })
+ pubkey = this.prikey_to_pubkey({ prikey, compress: true })
return this.pubkey_to_address({ pubkey: pubkey, coin, world })
}
}
@@ -696,7 +692,7 @@ class TicCrypto {
*/
static position_to_address ({ position, coin, world } = {}) {
if (!/^[\da-fA-F]{40}$/.test(position)) return null // 不论 tic, btc, eth,其 position 都是 40字符的。
- coin = coin?.toUpperCase() || my.COIN
+ coin = coin?.toUpperCase?.() || my.COIN
let address
if (coin === 'ETH') {
// 对以太坊,按照 EIP55,把纯位置转换为大小写敏感能自我验证的hex地址。仍然为20节=40符。
@@ -757,7 +753,7 @@ class TicCrypto {
prefix = '74'
break // Base58: 0x90 => d, Base 64: d=0x1d=0b00011101 => 0b 011101xx = 0x74~77
default:
- prefix = '4c'
+ prefix = '74'
}
let checksum = this.hash_easy(this.hash_easy(prefix + position)).slice(0, 6) // 添加 checksum 使得能够检测大小写错误。[todo] 校验码里要不要包含 prefix?
// address = this.hex_to_eip55(prefix + position + checksum) // 前缀1节,位置20节,校验3节,共24节=48字符(能够完全转化为8个色彩),再转eip55。
@@ -837,7 +833,7 @@ class TicCrypto {
*/
static pubkey_to_address ({ pubkey, coin, world } = {}) {
// pubkey 应当是string类型
- coin = coin?.toUpperCase() || my.COIN
+ coin = coin?.toUpperCase?.() || my.COIN
return this.position_to_address({ position: this.pubkey_to_position({ pubkey, coin }), coin, world })
}
@@ -920,7 +916,7 @@ class TicCrypto {
}
} else {
let prikey = this.randomize_seckey()
- let pubkey = this.seckey_to_pubkey({ prikey })
+ let pubkey = this.prikey_to_pubkey({ prikey })
return {
prikey,
pubkey,