add: 通过 ethereum-rsa 实现了椭圆曲线公私钥加解密!fix: is_secword
This commit is contained in:
parent
c9b147aeaf
commit
ec359d0d98
@ -10,6 +10,7 @@
|
|||||||
"bip39": "^3.0.4",
|
"bip39": "^3.0.4",
|
||||||
"bs58check": "^2.1.2",
|
"bs58check": "^2.1.2",
|
||||||
"eccrypto-js": "^5.4.0",
|
"eccrypto-js": "^5.4.0",
|
||||||
|
"ethereum-rsa": "^1.0.5",
|
||||||
"hdkey": "^2.0.1",
|
"hdkey": "^2.0.1",
|
||||||
"js-crypto-key-utils": "^1.0.4",
|
"js-crypto-key-utils": "^1.0.4",
|
||||||
"keccak": "^3.0.2",
|
"keccak": "^3.0.2",
|
||||||
|
160
ticc.js
160
ticc.js
@ -6,8 +6,9 @@ const bs58check = require('bs58check')
|
|||||||
const bs58 = require('bs58') // bs58check depends on bs58
|
const bs58 = require('bs58') // bs58check depends on bs58
|
||||||
const uuid = require('uuid')
|
const uuid = require('uuid')
|
||||||
const keccak = require('keccak')
|
const keccak = require('keccak')
|
||||||
const eccrypto = require('eccrypto-js') // 用于加解密。eccrypto 在 windows 上和 openssl 的版本兼容性有点麻烦,所以换用 eccrypto-js
|
const ecc = require('eccrypto-js') // 用于加解密。eccrypto 在 windows 上和 openssl 的版本兼容性有点麻烦,所以换用 eccrypto-js
|
||||||
const keyman = require('js-crypto-key-utils') // 转换原始密钥和 PER/DER 格式。
|
const keyman = require('js-crypto-key-utils') // 转换原始密钥和 PER/DER 格式。
|
||||||
|
const ethrsa = require('ethereum-rsa')
|
||||||
// const BitcoreMnemonic = require('bitcore-mnemonic') // https://bitcore.io/api/mnemonic/ https://github.com/bitpay/bitcore-mnemonic // 打包成 app 里常有问题,试图访问 window 变量,无法生成 secword
|
// const BitcoreMnemonic = require('bitcore-mnemonic') // https://bitcore.io/api/mnemonic/ https://github.com/bitpay/bitcore-mnemonic // 打包成 app 里常有问题,试图访问 window 变量,无法生成 secword
|
||||||
const bip39 = require('bip39') // https://github.com/bitcoinjs/bip39 // 有更多语言,但不方便选择语言,也不能使用 pass
|
const bip39 = require('bip39') // https://github.com/bitcoinjs/bip39 // 有更多语言,但不方便选择语言,也不能使用 pass
|
||||||
const hdkey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
|
const hdkey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
|
||||||
@ -123,7 +124,10 @@ class TicCrypto {
|
|||||||
// return false
|
// return false
|
||||||
|
|
||||||
//// for bip39. 注意,bip39对当前defaultWordlist之外其他语言的合法 mnemonic 也返回 false,这一点不如 bitcore-mnemonic. 所以不能直接 bip39.validateMnemonic(secword)
|
//// for bip39. 注意,bip39对当前defaultWordlist之外其他语言的合法 mnemonic 也返回 false,这一点不如 bitcore-mnemonic. 所以不能直接 bip39.validateMnemonic(secword)
|
||||||
if (typeof secword === 'string' && !/(^\s)|\s\s|(\s$)/.test(secword) && [12, 15, 18, 21, 24].includes(secword.split(/\s+/).length)) {
|
secword = secword
|
||||||
|
.replace(/^\s+/, '') // 删除开头的空格
|
||||||
|
.replace(/\s+$/, '') // 删除末尾的空格
|
||||||
|
if (typeof secword === 'string' && [12, 15, 18, 21, 24].includes(secword.split(/\s+/).length)) {
|
||||||
if (mode === 'easy') return true // easy模式不检查校验等等严格的合法性了,反正 secword_to_seed 是接受一切字符串的
|
if (mode === 'easy') return true // easy模式不检查校验等等严格的合法性了,反正 secword_to_seed 是接受一切字符串的
|
||||||
if (my.langMap[lang?.toLowerCase?.()]) {
|
if (my.langMap[lang?.toLowerCase?.()]) {
|
||||||
// 指定了语言则针对该语言词库检查
|
// 指定了语言则针对该语言词库检查
|
||||||
@ -131,7 +135,9 @@ class TicCrypto {
|
|||||||
} else {
|
} else {
|
||||||
// 未指定语言则检查所有可能语言词库
|
// 未指定语言则检查所有可能语言词库
|
||||||
for (let lang of my.langList) {
|
for (let lang of my.langList) {
|
||||||
return bip39.validateMnemonic(secword, bip39.wordlists[lang])
|
if (bip39.validateMnemonic(secword, bip39.wordlists[lang])) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,35 +214,40 @@ class TicCrypto {
|
|||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @param {*} data
|
* @param {*} data
|
||||||
* @param {*} option [{ tool, keytype, key, input, output, cipher }={}]
|
* @param {*} option [{ mode, key, input, output, cipher }={}]
|
||||||
* @return {String}
|
* @return {String}
|
||||||
* @memberof TicCrypto
|
* @memberof TicCrypto
|
||||||
*/
|
*/
|
||||||
static async encrypt_easy ({ data, tool = 'crypto', keytype = 'pwd', key, input = my.INPUT, output = my.OUTPUT, cipher = my.CIPHER } = {}) {
|
static async encrypt_easy ({ data, mode = 'semkey', key, input = my.INPUT, output = my.OUTPUT, cipher = my.CIPHER } = {}) {
|
||||||
if (tool === 'eccrypto') {
|
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
|
||||||
|
if (mode === 'ethrsa') {
|
||||||
|
if (key?.prikey && key?.pubkey) {
|
||||||
|
return ethrsa.encryptMessage(data, key?.prikey, key?.pubkey)
|
||||||
|
} else {
|
||||||
|
return ethrsa.encryptMessage(data, key?.senderPrikey, key?.receiverPubkey)
|
||||||
|
}
|
||||||
|
} else if (mode === 'ecc') {
|
||||||
// data 应当是 utf8 的字符串。key 必须是 pubkey
|
// data 应当是 utf8 的字符串。key 必须是 pubkey
|
||||||
// eccrypto 能用 Uint8Array 和 Buffer
|
// eccrypto 能用 Uint8Array 和 Buffer
|
||||||
// eccrypto-js 只能用 Buffer
|
// eccrypto-js 只能用 Buffer
|
||||||
// 在浏览器里 https://github.com/bitchan/eccrypto 库报错,即使用了 Uint8Array: Failed to execute 'encrypt' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
|
// 在浏览器里 https://github.com/bitchan/eccrypto 库报错,即使用了 Uint8Array: Failed to execute 'encrypt' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'
|
||||||
let cipherObject = await eccrypto.encrypt(Buffer.from(this.hex_to_buf(key)), data)
|
let cipherObject = await ecc.encrypt(Buffer.from(this.hex_to_buf(key)), data)
|
||||||
return cipherObject // 返回一个复杂的结构 {iv:Buffer, ciphertext:Buffer, ...}。对同样的key和data,每次返回的结果不一样
|
return cipherObject // 返回一个复杂的结构 {iv:Buffer, ciphertext:Buffer, ...}。对同样的key和data,每次返回的结果不一样
|
||||||
} else if (keytype === 'pwd') {
|
} else if (mode === 'semkey') {
|
||||||
// 对称加密
|
// 对称加密
|
||||||
if (typeof key === 'string') {
|
let inputEncoding = input // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
|
||||||
let inputEncoding = input // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
|
let outputEncoding = output === 'buf' ? undefined : output // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
|
||||||
let outputEncoding = output === 'buf' ? undefined : output // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
|
const iv = crypto.randomBytes(16)
|
||||||
const iv = crypto.randomBytes(16)
|
let encryptor = crypto.createCipheriv(cipher, this.hex_to_buf(this.hash_easy(key)), iv) // cipher 和 key 的长度必须相同,例如 cipher 是 ***-192,那么 key 就必须是 192/8=24 字节 = 48 hex 的。
|
||||||
let encryptor = crypto.createCipheriv(cipher, this.hex_to_buf(this.hash_easy(key)), iv) // cipher 和 key 的长度必须相同,例如 cipher 是 ***-192,那么 key 就必须是 192/8=24 字节 = 48 hex 的。
|
let ciphertext = encryptor.update(data, inputEncoding, outputEncoding)
|
||||||
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
|
ciphertext += encryptor.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
||||||
let ciphertext = encryptor.update(data, inputEncoding, outputEncoding)
|
return { iv: iv.toString('hex'), ciphertext } // 有 iv,显然每次结果不一样
|
||||||
ciphertext += encryptor.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
} else if (mode === 'prikey') {
|
||||||
return { iv: iv.toString('hex'), ciphertext } // 有 iv,显然每次结果不一样
|
// 只能用于 crypto.generateKeyPairSync('rsa') 生成的 rsa 公私钥,不能用于 Elliptic Curve 的公私钥
|
||||||
}
|
|
||||||
} else if (keytype === 'prikey') {
|
|
||||||
// 尚未走通,不能使用 ticc 生成的 Elliptic curve 椭圆曲线算法公私钥,只能用 crypto.generateKeyPairSync('rsa') 生成的 rsa 公私钥
|
|
||||||
let prikeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
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。每次结果都一样。
|
return crypto.privateEncrypt(prikeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
|
||||||
} else if (keytype === 'pubkey') {
|
} else if (mode === 'pubkey') {
|
||||||
|
// 只能用于 crypto.generateKeyPairSync('rsa') 生成的 rsa 公私钥,不能用于 Elliptic Curve 的公私钥
|
||||||
let pubkeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem')
|
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。每次结果不一样。
|
return crypto.publicEncrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
|
||||||
}
|
}
|
||||||
@ -248,39 +259,46 @@ class TicCrypto {
|
|||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @param {*} data
|
* @param {*} data
|
||||||
* @param {Object} option [{ keytype, key, input, output, cipher, format }={}]
|
* @param {Object} option [{ mode, key, input, output, cipher, format }={}]
|
||||||
* @return {String}
|
* @return {String}
|
||||||
* @memberof TicCrypto
|
* @memberof TicCrypto
|
||||||
*/
|
*/
|
||||||
static async decrypt_easy ({ data = {}, tool = 'crypto', keytype = 'pwd', key, input = my.OUTPUT, output = my.OUTPUT, cipher = my.CIPHER } = {}) {
|
static async decrypt_easy ({ data = {}, mode = 'semkey', key, input = my.OUTPUT, output = 'utf8', cipher = my.CIPHER } = {}) {
|
||||||
// data 应当是 encrypt 输出的数据类型
|
// data 应当是 encrypt 输出的数据类型
|
||||||
if (tool === 'eccrypto') {
|
if (mode === 'ethrsa') {
|
||||||
|
if (key?.prikey && key?.pubkey) {
|
||||||
|
return ethrsa.decryptMessage(data, key?.prikey, key?.pubkey)
|
||||||
|
} else {
|
||||||
|
return ethrsa.decryptMessage(data, key?.receiverPrikey, key?.senderPubkey)
|
||||||
|
}
|
||||||
|
} else if (mode === 'ecc') {
|
||||||
try {
|
try {
|
||||||
// eccrypto 只能接受 Buffer, 不接受 Uint8Array, 因为 eccrypto 需要调用 Buffer.compare 方法,不能在这里直接用 hex_to_buf
|
// eccrypto 只能接受 Buffer, 不接受 Uint8Array, 因为 eccrypto 需要调用 Buffer.compare 方法,不能在这里直接用 hex_to_buf
|
||||||
// eccrypto 也只能接受 Buffer, 不接受 Uint8Array
|
// eccrypto 也只能接受 Buffer, 不接受 Uint8Array
|
||||||
// data 需要是 eccrypto 自身encrypt方法返回的 cipherObject. key 是 private key。
|
// data 需要是 eccrypto 自身encrypt方法返回的 cipherObject. key 是 private key。
|
||||||
let plainbuffer = await eccrypto.decrypt(Buffer.from(key, 'hex'), data) // 返回的是 Buffer
|
let plainbuffer = await ecc.decrypt(Buffer.from(key, 'hex'), data) // 返回的是 Buffer
|
||||||
return plainbuffer.toString('utf8')
|
return plainbuffer.toString('utf8')
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
// eccrypto 对无法解密的,会抛出异常
|
// eccrypto 对无法解密的,会抛出异常
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
} else if (keytype === 'pwd') {
|
} else if (mode === 'semkey') {
|
||||||
// 对称解密
|
// 对称解密
|
||||||
if ((typeof data.ciphertext === 'string' || data.ciphertext instanceof Buffer) && typeof key === 'string') {
|
if (typeof data.ciphertext === 'string' || data.ciphertext instanceof Buffer) {
|
||||||
let inputEncoding = input // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
|
let inputEncoding = input // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
|
||||||
let outputEncoding = output === 'buf' ? undefined : output // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly
|
let outputEncoding = output === 'buf' ? undefined : output // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly
|
||||||
let decryptor = crypto.createDecipheriv(cipher, this.hex_to_buf(this.hash_easy(key)), Buffer.from(data.iv, 'hex'))
|
let decryptor = crypto.createDecipheriv(cipher, this.hex_to_buf(this.hash_easy(key)), Buffer.from(data.iv, 'hex'))
|
||||||
let decrypted = decryptor.update(data.ciphertext, inputEncoding, outputEncoding)
|
let decrypted = decryptor.update(data.ciphertext, inputEncoding, outputEncoding)
|
||||||
decrypted += decryptor.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
decrypted += decryptor.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
|
||||||
// 如果用户输入错误密码,deciper也能解密,无法自动判断是否正确结果。可在返回后人工判断。
|
// 如果用户输入错误密钥,deciper也能解密,无法自动判断是否正确结果。可在返回后人工判断。
|
||||||
return decrypted
|
return decrypted
|
||||||
}
|
}
|
||||||
} else if (keytype === 'prikey') {
|
} else if (mode === 'prikey') {
|
||||||
// 尚未走通,不能使用 ticc 生成的 Elliptic curve 椭圆曲线算法公私钥
|
// 只能用于 crypto.generateKeyPairSync('rsa') 生成的 rsa 公私钥,不能用于 Elliptic Curve 的公私钥
|
||||||
let prikeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem') // 私钥导出的der格式为144字节。
|
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。每次结果都一样。
|
return crypto.privateDecrypt(prikeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果都一样。
|
||||||
} else if (keytype === 'pubkey') {
|
} else if (mode === 'pubkey') {
|
||||||
|
// 只能用于 crypto.generateKeyPairSync('rsa') 生成的 rsa 公私钥,不能用于 Elliptic Curve 的公私钥
|
||||||
let pubkeyPEM = await new keyman.Key('oct', this.hex_to_buf(key), { namedCurve: 'P-256K' }).export('pem')
|
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。每次结果不一样。
|
return crypto.publicDecrypt(pubkeyPEM, Buffer.from(data)) // 返回 Buffer。每次结果不一样。
|
||||||
}
|
}
|
||||||
@ -305,9 +323,9 @@ class TicCrypto {
|
|||||||
let hashBuf = this.hash_easy(data, { output: 'buf' }) // 哈希必须输出为 buffer
|
let hashBuf = this.hash_easy(data, { output: 'buf' }) // 哈希必须输出为 buffer
|
||||||
let signature = nacl.sign.detached(hashBuf, Buffer.from(prikey, 'hex'))
|
let signature = nacl.sign.detached(hashBuf, Buffer.from(prikey, 'hex'))
|
||||||
return Buffer.from(signature).toString('hex') // 签名是64节,128个hex字符
|
return Buffer.from(signature).toString('hex') // 签名是64节,128个hex字符
|
||||||
} else if (tool === 'eccrypto' && prikey.length === 64) {
|
} else if (tool === 'ecc' && prikey.length === 64) {
|
||||||
// eccrypto 对同一组data, prikey 生成的签名是固定的,观察到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' }))
|
let signature = await ecc.sign(Buffer.from(prikey, 'hex'), this.hash_easy(data, { output: 'buf' }))
|
||||||
return signature.toString('hex')
|
return signature.toString('hex')
|
||||||
} else if (prikey.length === 64) {
|
} else if (prikey.length === 64) {
|
||||||
// 纯 crypto
|
// 纯 crypto
|
||||||
@ -343,10 +361,10 @@ class TicCrypto {
|
|||||||
let bufPubkey = Buffer.from(pubkey, 'hex')
|
let bufPubkey = Buffer.from(pubkey, 'hex')
|
||||||
let verified = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
|
let verified = nacl.sign.detached.verify(bufHash, bufSignature, bufPubkey)
|
||||||
return verified
|
return verified
|
||||||
} else if ('eccrypto' === tool && signature.length >= 140) {
|
} else if ('ecc' === tool && signature.length >= 140) {
|
||||||
// 默认使用 eccrypto // 发现大小写不影响 eccrypto 验签!都能通过
|
// 默认使用 eccrypto // 发现大小写不影响 eccrypto 验签!都能通过
|
||||||
try {
|
try {
|
||||||
let result = await eccrypto.verify(Buffer.from(pubkey, 'hex'), this.hash_easy(data, { output: 'buf' }), Buffer.from(signature, 'hex')) // 如果给signature添加1位hex,eccrypto 的 verify结果也是true! 估计因为一位hex不被转成字节。
|
let result = await ecc.verify(Buffer.from(pubkey, 'hex'), this.hash_easy(data, { output: 'buf' }), Buffer.from(signature, 'hex')) // 如果给signature添加1位hex,eccrypto 的 verify结果也是true! 估计因为一位hex不被转成字节。
|
||||||
return true
|
return true
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
// 对能够验证的,eccrypto返回 null;对无法验证的,抛出异常
|
// 对能够验证的,eccrypto返回 null;对无法验证的,抛出异常
|
||||||
@ -558,19 +576,20 @@ class TicCrypto {
|
|||||||
static secword_to_account ({ secword, coin, coinFamily, world, pass, pathSeed, pathIndex, path, tool, hasher } = {}) {
|
static secword_to_account ({ secword, coin, coinFamily, world, pass, pathSeed, pathIndex, path, tool, hasher } = {}) {
|
||||||
// account 比 keypair 多了 address 字段。
|
// account 比 keypair 多了 address 字段。
|
||||||
coin = coin?.toUpperCase?.() || my.COIN
|
coin = coin?.toUpperCase?.() || my.COIN
|
||||||
|
coinFamily = coinFamily?.toUpperCase?.() || my.COIN_FAMILY
|
||||||
let kp = this.secword_to_keypair({ secword, coin, pass, pathSeed, pathIndex, path, tool, hasher })
|
let kp = this.secword_to_keypair({ secword, coin, pass, pathSeed, pathIndex, path, tool, hasher })
|
||||||
if (kp) {
|
if (kp) {
|
||||||
if (coin === 'ETH') {
|
if (coin === 'ETH' || coinFamily === 'ETH') {
|
||||||
world = world || 'mainnet'
|
world = world || 'mainnet'
|
||||||
kp.address = this.pubkey_to_address({ pubkey: this.decompress_pubkey(kp.pubkey), coin, coinFamily, world })
|
kp.address = this.pubkey_to_address({ pubkey: this.decompress_pubkey(kp.pubkey), coin, coinFamily, world })
|
||||||
} else if (coin === 'BTC') {
|
} else if (coin === 'BTC' || coinFamily === 'BTC') {
|
||||||
world = world || 'mainnet'
|
world = world || 'mainnet'
|
||||||
kp.address = this.pubkey_to_address({ pubkey: kp.pubkey, coin, coinFamily, world })
|
kp.address = this.pubkey_to_address({ pubkey: kp.pubkey, coin, coinFamily, world })
|
||||||
} else {
|
} else {
|
||||||
world = world || my.WORLD
|
world = world || my.WORLD
|
||||||
kp.address = this.pubkey_to_address({ pubkey: kp.pubkey, coin, coinFamily, world })
|
kp.address = this.pubkey_to_address({ pubkey: kp.pubkey, coin, coinFamily, world })
|
||||||
}
|
}
|
||||||
return { ...kp, coin, world, secword }
|
return { ...kp, coin, coinFamily, world, secword }
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -1344,27 +1363,27 @@ class TicCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Base32
|
// https://en.wikipedia.org/wiki/Base32
|
||||||
static hex_to_b32 (hex, { encoding = 'RFC4648' } = {}) {
|
static hex_to_b32 (hex) {
|
||||||
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
||||||
return base32encode(Buffer.from(hex, 'hex'), encoding)
|
return base32encode(Buffer.from(hex, 'hex'), 'RFC4648')
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
static b32_to_hex (b32, { encoding = 'RFC4648' } = {}) {
|
static b32_to_hex (b32) {
|
||||||
if (/^[A-Za-z2-7=]+$/.test(b32)) {
|
if (/^[A-Za-z2-7=]+$/.test(b32)) {
|
||||||
return Buffer.from(base32decode(b32.toUpperCase(), encoding)).toString('hex')
|
return Buffer.from(base32decode(b32.toUpperCase(), 'RFC4648')).toString('hex')
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
static hex_to_b32h (hex, { encoding = 'RFC4648-HEX' } = {}) {
|
static hex_to_b32h (hex) {
|
||||||
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
||||||
return base32encode(Buffer.from(hex, 'hex'), encoding)
|
return base32encode(Buffer.from(hex, 'hex'), 'RFC4648-HEX')
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
static b32h_to_hex (b32, { encoding = 'RFC4648-HEX' } = {}) {
|
static b32h_to_hex (b32h) {
|
||||||
if (/^[0-9A-Va-v=]+$/.test(b32)) {
|
if (/^[0-9A-Va-v=]+$/.test(b32h)) {
|
||||||
return Buffer.from(base32decode(b32.toUpperCase(), encoding)).toString('hex')
|
return Buffer.from(base32decode(b32.toUpperCase(), 'RFC4648-HEX')).toString('hex')
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -1453,14 +1472,17 @@ class TicCrypto {
|
|||||||
static cid_to_cosh ({ cid }) {
|
static cid_to_cosh ({ cid }) {
|
||||||
if (/^[Q|1]/.test(cid)) {
|
if (/^[Q|1]/.test(cid)) {
|
||||||
return this.b58_to_hex(cid).slice(4)
|
return this.b58_to_hex(cid).slice(4)
|
||||||
} else if (/^b/.test(cid)) {
|
} else if (/^[b|B]/.test(cid)) {
|
||||||
return this.b32_to_hex(cid.substr(1)).slice(8)
|
return this.b32_to_hex(cid.substr(1)).slice(8)
|
||||||
} else if (/^z/.test(cid)) {
|
} else if (/^z/.test(cid)) {
|
||||||
return this.b58_to_hex(cid.substr(1)).slice(8)
|
return this.b58_to_hex(cid.substr(1)).slice(8)
|
||||||
|
} else if (/^[m|M|u|U]/.test(cid)) {
|
||||||
|
return Buffer.from(cid.substr(1), 'base64').toString('hex')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static cosh_to_cid ({ cosh, cidBase = 'b32', cidVersion = 1, cidCodec = 'raw', cidAlgo = 'sha256' }) {
|
static cosh_to_cid ({ cosh, cidBase = 'b32', cidVersion = 1, cidCodec = 'raw', cidAlgo = 'sha256' }) {
|
||||||
|
// https://github.com/multiformats/multibase
|
||||||
const multibase = {
|
const multibase = {
|
||||||
identity: 0x00,
|
identity: 0x00,
|
||||||
b2: '0',
|
b2: '0',
|
||||||
@ -1472,38 +1494,56 @@ class TicCrypto {
|
|||||||
B32: 'B',
|
B32: 'B',
|
||||||
b32h: 'v',
|
b32h: 'v',
|
||||||
B32h: 'V',
|
B32h: 'V',
|
||||||
|
b32hp: 't',
|
||||||
|
B32hp: 'T',
|
||||||
|
b32p: 'c',
|
||||||
|
B32p: 'C',
|
||||||
|
b32z: 'h', // base32z, z-base-32
|
||||||
b36: 'k',
|
b36: 'k',
|
||||||
|
B36: 'K',
|
||||||
b64: 'm',
|
b64: 'm',
|
||||||
b64p: 'M',
|
b64p: 'M',
|
||||||
b64u: 'u',
|
b64u: 'u',
|
||||||
b64up: 'U',
|
b64up: 'U',
|
||||||
b58: 'z',
|
b58: 'z',
|
||||||
}
|
}
|
||||||
|
// https://github.com/multiformats/multicodec
|
||||||
const multicodec = {
|
const multicodec = {
|
||||||
|
raw: '55',
|
||||||
dagpb: '70',
|
dagpb: '70',
|
||||||
p2pkey: '72',
|
p2pkey: '72',
|
||||||
raw: '55',
|
|
||||||
}
|
}
|
||||||
const multialgo = {
|
const multialgo = {
|
||||||
identify: '00',
|
identify: '00',
|
||||||
sha256: '12',
|
sha256: '12',
|
||||||
|
sha512: '13',
|
||||||
|
keccak256: '1b',
|
||||||
|
ripemd160: '1053',
|
||||||
|
md5: 'd5',
|
||||||
}
|
}
|
||||||
if (cidVersion === 0) {
|
if (cidVersion === 0) {
|
||||||
return this.hex_to_b58(`${multialgo[cidAlgo]}${Number(cosh.length / 2).toString(16)}${cosh}`)
|
return this.hex_to_b58(`${multialgo[cidAlgo]}${Number(cosh.length / 2).toString(16)}${cosh}`)
|
||||||
}
|
} else if (cidVersion === 1) {
|
||||||
if (cidVersion === 1) {
|
const fullHex = `01${multicodec[cidCodec]}${multialgo[cidAlgo]}${Number(cosh.length / 2).toString(16)}${cosh}`
|
||||||
let fullHex = `01${multicodec[cidCodec]}${multialgo[cidAlgo]}${Number(cosh.length / 2).toString(16)}${cosh}`
|
let converted = ''
|
||||||
console.log(fullHex)
|
|
||||||
if (cidBase === 'b32') {
|
if (cidBase === 'b32') {
|
||||||
return (
|
converted = this.hex_to_b32(fullHex)
|
||||||
multibase[cidBase] +
|
.toLowerCase()
|
||||||
this.hex_to_b32(fullHex)
|
.replace(/=/g, '')
|
||||||
.toLowerCase()
|
} else if (cidBase === 'B32') {
|
||||||
.replace(/=/g, '')
|
converted = this.hex_to_b32(fullHex)
|
||||||
)
|
.toUpperCase()
|
||||||
|
.replace(/=/g, '')
|
||||||
} else if (cidBase === 'b58') {
|
} else if (cidBase === 'b58') {
|
||||||
return multibase[cidBase] + this.hex_to_b58(fullHex)
|
converted = this.hex_to_b58(fullHex)
|
||||||
|
} else if (cidBase === 'b64p') {
|
||||||
|
converted = Buffer.from(fullHex, 'hex').toString('base64')
|
||||||
|
} else if (cidBase === 'b64') {
|
||||||
|
converted = Buffer.from(fullHex, 'hex')
|
||||||
|
.toString('base64')
|
||||||
|
.replace(/=/g, '')
|
||||||
}
|
}
|
||||||
|
return multibase[cidBase] + converted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user