tic 默认使用和 btc, eth 一样的公私钥算法
This commit is contained in:
parent
09d831af00
commit
72b89fe997
189
index.js
189
index.js
@ -66,6 +66,31 @@ module.exports = {
|
||||
return false
|
||||
}
|
||||
,
|
||||
isSecword(secword){
|
||||
return Secword.isValid(secword)
|
||||
}
|
||||
,
|
||||
isSeckey(seckey){
|
||||
// 比特币、以太坊的私钥:64 hex
|
||||
// nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
|
||||
return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey)
|
||||
}
|
||||
,
|
||||
isPubkey(pubkey){
|
||||
// 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
|
||||
// 以太坊的公钥:'02|03' + 64 hex
|
||||
// nacl.sign 的公钥:64 hex
|
||||
return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
|
||||
}
|
||||
,
|
||||
isAddress (address) {
|
||||
return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address) // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) {
|
||||
}
|
||||
,
|
||||
isSignature(signature){
|
||||
return /^[a-fA-F0-9]{128,144}$/.test(signature)
|
||||
}
|
||||
,
|
||||
async encrypt(data, {keytype, key, input, output, cipher}={}){
|
||||
if (keytype==='pwd') {
|
||||
if (this.isHashable(data) && typeof(key)==='string') {
|
||||
@ -113,34 +138,8 @@ module.exports = {
|
||||
return null
|
||||
}
|
||||
,
|
||||
/* 以下两个方法基于 eccrypto,注意其在浏览器上会有问题。
|
||||
*/
|
||||
async encryptPubkey(plaindata, pubkey, option){ // plaindata 应当是 utf8 的字符串
|
||||
let cipherobject = await eccrypto.encrypt(this.hex2buf(pubkey), plaindata)
|
||||
return cipherobject
|
||||
}
|
||||
,
|
||||
async decryptSeckey(cipherobject, seckey, option){ // cipherobject 需要是 eccrypto 自身encrypt方法返回的对象
|
||||
let plaindata
|
||||
if (Buffer) { // nodejs
|
||||
plaindata = await eccrypto.decrypt(Buffer.from(seckey, 'hex'), cipherobject) // eccrypto 需要调用 Buffer.compare 方法
|
||||
}else { // browser
|
||||
plaindata = await eccrypto.decrypt(this.hex2buf(seckey), cipherobject)
|
||||
}
|
||||
return plaindata.toString('utf8')
|
||||
}
|
||||
,
|
||||
async sign(data, seckey, option) { // data can be string or buffer or object, results are the same
|
||||
if (this.isHashable(data) && this.isSeckey(seckey)) {
|
||||
option=option||{}
|
||||
|
||||
// 方案1: 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=512位,而比特币/以太坊的seckey是32字节。因此本方法只能用于 TIC 币的 keypair。
|
||||
// option.output='buf' // 哈希必须输出为 buffer
|
||||
// var hashBuf = this.hash(data, option)
|
||||
// var signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'))
|
||||
// return Buffer.from(signature).toString('hex') // 返回128个hex字符,64字节
|
||||
|
||||
// 方案2: 纯 crypto
|
||||
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
|
||||
let seckeyPEM = await new keyman.Key('oct', this.hex2buf(seckey), {namedCurve:'P-256K'}).export('pem')
|
||||
let hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
|
||||
let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
|
||||
@ -148,27 +147,19 @@ module.exports = {
|
||||
let signer=crypto.createSign(hasher)
|
||||
signer.update(data, inputEncoding).end()
|
||||
let signature = signer.sign(seckeyPEM, outputEncoding)
|
||||
return signature // 发现同样的输入,每次调用会生成不同的 signature, 但都可以通过 verify。有一次我竟然徒手修改出一个新签名也通过验证。
|
||||
return signature // 发现同样的输入,每次调用会生成不同的 signature, 且长度不定(140~144 hex) 但都可以通过 verify。有一次我竟然徒手修改出一个新签名也通过验证。
|
||||
}
|
||||
if (this.isHashable(data) && this.isSeckey(seckey) && seckey.length===128) { // 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=128字符。
|
||||
option.output='buf' // 哈希必须输出为 buffer
|
||||
let hashBuf = this.hash(data, option)
|
||||
let signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex'))
|
||||
return Buffer.from(signature).toString('hex') // 签名是64节,128个hex字符
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
isSignature(signature){
|
||||
return /^[a-fA-F0-9]{128,144}$/.test(signature)
|
||||
}
|
||||
,
|
||||
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)){
|
||||
// 方案1: nacl
|
||||
// option=option||{}
|
||||
// option.output='buf' // 哈希必须输出为 buffer
|
||||
// var bufHash=this.hash(data, option)
|
||||
// var bufSignature = Buffer.from(signature, 'hex')
|
||||
// var bufPubkey = Buffer.from(pubkey, 'hex')
|
||||
// var res = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
|
||||
// return res
|
||||
|
||||
// 方案2: 纯 crypto
|
||||
if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey) && signature.length>=140){ // 纯 crypto
|
||||
let pubkeyPEM = await new keyman.Key('oct', this.hex2buf(pubkey), {namedCurve:'P-256K'}).export('pem')
|
||||
let hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
|
||||
let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
|
||||
@ -178,6 +169,14 @@ module.exports = {
|
||||
let verified = verifier.verify(pubkeyPEM, signature, 'hex')
|
||||
return verified
|
||||
}
|
||||
if (signature.length===128){ // nacl
|
||||
option.output='buf' // 哈希必须输出为 buffer
|
||||
let bufHash=this.hash(data, option)
|
||||
let bufSignature = Buffer.from(signature, 'hex')
|
||||
let bufPubkey = Buffer.from(pubkey, 'hex')
|
||||
let verified = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
|
||||
return verified
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
@ -212,7 +211,7 @@ module.exports = {
|
||||
option=option||{}
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
|
||||
if(option.coin==='TIC') {
|
||||
if(option.tool==='nacl') {
|
||||
// 采用自己的算法:bip39算法从secword到种子,hash后用 nacl.sign.keyPair.fromSeed()方法。
|
||||
option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
|
||||
let hashBuf=crypto.createHash(option.hasher).update(this.secword2seed(secword, option.pass)).digest()
|
||||
@ -233,6 +232,7 @@ module.exports = {
|
||||
switch(option.coin){
|
||||
case 'BTC': key=hdmaster.derive("m/44'/0'/0'/0/0"); break
|
||||
case 'ETH': key=hdmaster.derive("m/44'/60'/0'/0/0"); break
|
||||
case 'TIC': key=hdmaster.derive("m/44'/66'/0'/0/0"); break
|
||||
default: key=hdmaster.derive("m/44'/99'/0'/0/0"); break
|
||||
}
|
||||
}else { // 指定了路径 option.path,例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
|
||||
@ -269,8 +269,7 @@ module.exports = {
|
||||
return null
|
||||
}
|
||||
,
|
||||
seckey2pubkey(seckey, option){
|
||||
option=option||{}
|
||||
seckey2pubkey(seckey, option={}){
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
if (this.isSeckey(seckey) && seckey.length===64){ // 只能用于32字节的私钥(BTC, ETH)。也就是不能用于 TIC 的私钥。
|
||||
let curve = my.CURVE_LIST.indexOf(option.curve)>=0?option.curve:my.CURVE // 默认为 secp256k1
|
||||
@ -285,7 +284,7 @@ module.exports = {
|
||||
// return ecc.getPublicCompressed(this.hex2buf(seckey)).toString('hex')
|
||||
// }
|
||||
// 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同
|
||||
}else if (this.isSeckey(seckey) && seckey.length===128 && option.coin==='TIC'){ // 用于64字节=128 hex的 TIC 私钥
|
||||
}else if (this.isSeckey(seckey) && seckey.length===128){ // 用于64字节=128 hex的 TIC 私钥
|
||||
let keypair=nacl.sign.keyPair.fromSecretKey(Buffer.from(seckey,'hex'))
|
||||
return Buffer.from(keypair.publicKey).toString('hex') // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型
|
||||
}
|
||||
@ -308,27 +307,6 @@ module.exports = {
|
||||
return null
|
||||
}
|
||||
,
|
||||
isSecword(secword){
|
||||
return Secword.isValid(secword)
|
||||
}
|
||||
,
|
||||
isSeckey(seckey){
|
||||
// 比特币、以太坊的私钥:64 hex
|
||||
// nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex
|
||||
return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey)
|
||||
}
|
||||
,
|
||||
isPubkey(pubkey){
|
||||
// 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
|
||||
// 以太坊的公钥:'02|03' + 64 hex
|
||||
// nacl.sign 的公钥:64 hex
|
||||
return /^((02|03)?[a-fA-F0-9]{64}|04[a-fA-F0-9]{128})$/.test(pubkey) // "d2f186a630f5558ba3ede10a4dd0549da5854eab3ed28ee8534350c2535d38b0"
|
||||
}
|
||||
,
|
||||
isAddress (address) {
|
||||
return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address) // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) {
|
||||
}
|
||||
,
|
||||
pubkey2position (pubkey, {coin}={}){
|
||||
coin = my.COIN_LIST.indexOf(coin)>=0?coin:my.COIN
|
||||
if(this.isPubkey(pubkey)){
|
||||
@ -440,7 +418,7 @@ module.exports = {
|
||||
randomSeckey(option){ // todo: 使用 crypto.randomBytes(size)
|
||||
option=option||{}
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
if (option.coin==='TIC'){
|
||||
if (option.tool==='nacl'){
|
||||
return crypto.randomBytes(64).toString('hex') // Buffer.from(nacl.sign.keyPair().secretKey).toString('hex') // 64字节
|
||||
}else{
|
||||
return crypto.randomBytes(32).toString('hex') // Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
|
||||
@ -451,14 +429,23 @@ module.exports = {
|
||||
option=option||{}
|
||||
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
|
||||
let kp
|
||||
if (option.coin==='TIC'){
|
||||
kp=nacl.sign.keyPair()
|
||||
}else{
|
||||
kp=nacl.box.keyPair()
|
||||
}
|
||||
return {
|
||||
seckey:Buffer.from(kp.secretKey).toString('hex'),
|
||||
pubkey:Buffer.from(kp.publicKey).toString('hex')
|
||||
if (option.tool==='nacl'){
|
||||
if (option.purpose==='encrypt'){
|
||||
kp=nacl.box.keyPair()
|
||||
}else{
|
||||
kp=nacl.sign.keyPair()
|
||||
}
|
||||
return {
|
||||
seckey:Buffer.from(kp.secretKey).toString('hex'),
|
||||
pubkey:Buffer.from(kp.publicKey).toString('hex')
|
||||
}
|
||||
}else {
|
||||
let seckey=this.randomSeckey()
|
||||
let pubkey=this.seckey2pubkey(seckey)
|
||||
return {
|
||||
seckey,
|
||||
pubkey
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
@ -471,19 +458,14 @@ module.exports = {
|
||||
return text
|
||||
}
|
||||
,
|
||||
randomNumber(option){ // 长度为 option.length 的随机数字,或者 (option.min||0) <= num < option.max
|
||||
option=option||{}
|
||||
randomNumber({length, min, max}={}){ // 长度为 length 的随机数字,或者 (min||0) <= num < max
|
||||
var num=0
|
||||
if (option.length>0){
|
||||
num=parseInt(Math.random()*Math.pow(10,option.length))
|
||||
let l = new String(num).length
|
||||
while(l < option.length) {
|
||||
num = '0' + num // 注意,这时返回的是字符串!
|
||||
l++
|
||||
}
|
||||
}else if (option.max>0){
|
||||
option.min = (option.min>=0)?option.min:0
|
||||
num=parseInt(Math.random()*(option.max-option.min))+option.min
|
||||
if (typeof length === 'number' && length>0){
|
||||
num=parseInt(Math.random()*Math.pow(10,length))
|
||||
num.toString().padStart(length, '0')
|
||||
}else if (typeof max === 'number' && max>0){
|
||||
min = (typeof min === 'number' && min>=0) ? min : 0
|
||||
num=parseInt(Math.random()*(max-min))+min
|
||||
}else{ // 如果 option 为空
|
||||
num=Math.random()
|
||||
}
|
||||
@ -617,10 +599,10 @@ module.exports = {
|
||||
return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'))
|
||||
}
|
||||
,
|
||||
rsaVerify(string2Verify, sign, pubkey, signType){
|
||||
rsaVerify(string2Verify, signature, pubkey, signType){
|
||||
signType=signType||'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
|
||||
let verifier=crypto.createVerify(signType)
|
||||
return verifier.update(string2Verify).verify(pubkey, sign, 'base64')
|
||||
return verifier.update(string2Verify).verify(pubkey, signature, 'base64')
|
||||
}
|
||||
,
|
||||
buf2hex(buffer) { // buffer is an ArrayBuffer
|
||||
@ -633,20 +615,33 @@ module.exports = {
|
||||
})) // 注意,arraybuffer没有 toString('hex')功能, Buffer才有。
|
||||
}
|
||||
,
|
||||
hex2base58check(hex){
|
||||
hex2b58c(hex){
|
||||
return bs58check.encode(Buffer.from(hex, 'hex'))
|
||||
}
|
||||
,
|
||||
base58check2hex(box){
|
||||
b58c2hex(box){
|
||||
try{
|
||||
return bs58check.decode(box).toString('hex').toUpperCase()
|
||||
return bs58check.decode(box).toString('hex')
|
||||
}catch(exception){
|
||||
return null
|
||||
}
|
||||
}
|
||||
,
|
||||
hex2eip55(){
|
||||
|
||||
hex2eip55(hex){
|
||||
if (typeof(hex)==='string' && hex.length===64) {
|
||||
hex = hex.toLowerCase().replace('0x', '')
|
||||
let hash = keccak('keccak256').update(hex).digest('hex')
|
||||
result = '0x'
|
||||
for (var i = 0; i < hex.length; i++) {
|
||||
if (parseInt(hash[i], 16) >= 8) {
|
||||
result += hex[i].toUpperCase()
|
||||
} else {
|
||||
result += hex[i]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
,
|
||||
// test: https://iancoleman.io/bitcoin-key-compression/
|
||||
|
Loading…
Reference in New Issue
Block a user