添加 cid 转换函数

This commit is contained in:
陆柯 2022-05-28 09:08:55 +08:00
parent 447a3eb442
commit 74694866bc

243
index.js
View File

@ -31,6 +31,17 @@ my.INPUT = 'utf8' // 默认的加密方法的明文格式。utf8 能够兼容 la
my.INPUT_LIST = ['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView
my.COIN = 'TIC' // 默认的币种
my.COIN_LIST = ['TIC', 'EXT', 'BTC', 'ETH']
my.REGEXP_ALPHABET = {
hex: /^[0-9a-fA-F]+$/,
b32: /^[A-Za-z2-7=]+$/,
b32h: /^[0-9A-Va-v=]+$/,
b36: /^[0-9A-Z-a-z]+$/,
b58: /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/,
b62: /^[A-Za-z0-9]+$/,
b64: /^[A-Za-z0-9\+\/=]+$/,
b64u: /^[A-Za-z0-9\-_]+$/,
b64t: /^[A-Za-z0-9\._]+$/,
}
/**
*
@ -46,7 +57,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static isHashable(data, { strict = false } = {}) {
static isHashable (data, { strict = false } = {}) {
if (strict) {
return typeof data !== 'boolean' && data !== Infinity && data ? true : false // 允许大多数数据除了null、''、0、布尔值、无限数。注意 data 要放在最后,否则会被 return 直接返回,而不是返回 Boolean
}
@ -62,7 +73,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static isHash(hash, { hasher = my.HASHER } = {}) {
static isHash (hash, { hasher = my.HASHER } = {}) {
if (my.HASHER_LIST.indexOf(hasher) >= 0) {
switch (hasher) {
case 'sha256':
@ -88,7 +99,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static isSecword(secword, { mode = 'strict' } = {}) {
static isSecword (secword, { mode = 'strict' } = {}) {
// 注意 not all 12 words combinations are valid for both bitcore and bip39, because there are checksum in mnemonic. 另外实际上bitcore和bip39对12, 15, 18, ... 长度的合法助记词都返回 true。
//// for bitcore-mnemonic. 注意bitcore-mnemonic 对少于12词的会抛出异常很蠢。
// if (typeof secword==='string' && 12===secword.split(/ +/).length)
@ -115,7 +126,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static isSeckey(seckey) {
static 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)
@ -129,7 +140,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static isPubkey(pubkey) {
static isPubkey (pubkey) {
// 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex
// 以太坊的公钥:'02|03' + 64 hex
// nacl.sign 的公钥64 hex
@ -144,7 +155,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static isSignature(signature) {
static isSignature (signature) {
return /^[a-fA-F0-9]{128,144}$/.test(signature) && signature.length % 2 === 0 // 128 for nacl, 140/142/144 for crypto and eccrypto in der format.
}
@ -157,14 +168,17 @@ class TICrypto {
* @return {String}
* @memberof TICrypto
*/
static hash(data, { hasher = my.HASHER, salt, input = my.INPUT, output = my.OUTPUT } = {}) {
static hash (data, { hasher = my.HASHER, salt, input = my.INPUT, output = my.OUTPUT } = {}) {
// data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
if (this.isHashable(data)) {
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
if (salt && typeof salt === 'string') data = data + this.hash(salt)
let inputEncoding = input // my.INPUT_LIST.indexOf(input)>=0?input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
let outputEncoding = output === 'buf' ? undefined : output // (my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT) // output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64'
return crypto.createHash(hasher).update(data, inputEncoding).digest(outputEncoding)
return crypto
.createHash(hasher)
.update(data, inputEncoding)
.digest(outputEncoding)
}
return null
}
@ -178,7 +192,7 @@ class TICrypto {
* @return {String}
* @memberof TICrypto
*/
static async encrypt({ data, tool = 'crypto', keytype = 'pwd', key, input, output, cipher } = {}) {
static async encrypt ({ data, tool = 'crypto', keytype = 'pwd', key, input, output, cipher } = {}) {
if (tool === 'eccrypto') {
// data 应当是 utf8 的字符串。key 必须是 pubkey
// eccrypto 能用 Uint8Array 和 Buffer
@ -218,7 +232,7 @@ class TICrypto {
* @return {String}
* @memberof TICrypto
*/
static async decrypt({ data = {}, tool = 'crypto', keytype = 'pwd', key, input, output, cipher } = {}) {
static async decrypt ({ data = {}, tool = 'crypto', keytype = 'pwd', key, input, output, cipher } = {}) {
// data 应当是 encrypt 输出的数据类型
if (tool === 'eccrypto') {
try {
@ -267,7 +281,7 @@ class TICrypto {
* @return {String}
* @memberof TICrypto
*/
static async sign({ data, seckey, tool = 'crypto', hasher }) {
static async sign ({ data, seckey, tool = 'crypto', hasher }) {
// data can be string or buffer or object, results are the same
if (this.isHashable(data) && this.isSeckey(seckey)) {
if (tool === 'nacl' && seckey.length === 128) {
@ -304,7 +318,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static async verify({ data, signature, pubkey, tool = 'crypto', hasher }) {
static async verify ({ data, signature, pubkey, tool = 'crypto', hasher }) {
// data could be anything, but converts to string or remains be Buffer/TypedArray/DataView
if (this.isHashable(data) && this.isSignature(signature) && this.isPubkey(pubkey)) {
if ('nacl' === tool && signature.length === 128) {
@ -345,11 +359,14 @@ class TICrypto {
* @return {Object} {pubkey, seckey, address,}
* @memberof TICrypto
*/
static pass2keypair(pass, { hasher } = {}) {
static pass2keypair (pass, { hasher } = {}) {
// 如果使用其他机制例如密码、随机数不使用secword也可生成keypair
if (this.isHashable(pass)) {
hasher = my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER
var hashBuf = crypto.createHash(hasher).update(pass).digest()
var hashBuf = crypto
.createHash(hasher)
.update(pass)
.digest()
var keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl的seed要求是32字节
return {
hash: hashBuf.toString('hex'),
@ -368,7 +385,7 @@ class TICrypto {
* @return {String}
* @memberof TICrypto
*/
static entropy2secword(entropy) {
static entropy2secword (entropy) {
// entropy could be hex string or buffer. Byte length could be of 16, 20, 24, 28, ... which outputs mnemonic of length 12, 15, 18, 21, ...
return bip39.entropyToMnemonic(entropy) // results are the same for the same entropy.
}
@ -381,7 +398,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static secword2entropy(secword) {
static secword2entropy (secword) {
// secword could be of length 12, 15, 18, ... which outputs hex of length 32, 40, ...
return bip39.mnemonicToEntropy(secword) // results are the same for the same secword.
}
@ -395,7 +412,7 @@ class TICrypto {
* @return {Object} {pubkey, seckey,}
* @memberof TICrypto
*/
static secword2keypair(secword, { coin, pass, path, tool, hasher } = {}) {
static secword2keypair (secword, { coin, pass, path, tool, hasher } = {}) {
// coin 币种;
// passphase 密码,默认为空;
// path==='master' 生成 HD master key不定义则默认为相应币种的第一对公私钥。
@ -412,7 +429,10 @@ class TICrypto {
if (tool === 'nacl') {
// 采用自己的算法bip39算法从secword到种子hash后用 nacl.sign.keyPair.fromSeed()方法。
hasher = my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER
let hashBuf = crypto.createHash(hasher).update(this.secword2seed(secword, pass)).digest()
let hashBuf = crypto
.createHash(hasher)
.update(this.secword2seed(secword, pass))
.digest()
let keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl.sign.keyPair.fromSeed 要求32字节的种子而 this.secword2seed生成的是64字节种子所以要先做一次sha256
return {
coin: coin,
@ -466,7 +486,7 @@ class TICrypto {
* @return {String} path
* @memberof TICrypto
*/
static seed2path(seed, { coin = 'TIC' } = { coin: 'TIC' }) {
static seed2path (seed, { coin = 'TIC' } = { coin: 'TIC' }) {
// 路径规范 BIP44: m/Purpose'/Coin'/Account'/Change/Index,
// 但实际上 Purpose, Coin 都可任意定;' 可有可无;
// Account/Change/Index 最大到 parseInt(0x7FFFFFFF, 16)
@ -501,7 +521,7 @@ class TICrypto {
* @return {Object}
* @memberof TICrypto
*/
static secword2account(secword, { coin, pass, path, tool, hasher } = {}) {
static secword2account (secword, { coin, pass, path, tool, hasher } = {}) {
// account 比 keypair 多了 address 字段。
coin = my.COIN_LIST.indexOf(coin?.toUpperCase()) >= 0 ? coin.toUpperCase() : my.COIN
let kp = this.secword2keypair(secword, { coin, pass, path, tool, hasher })
@ -526,7 +546,7 @@ class TICrypto {
* @return {String} address
* @memberof TICrypto
*/
static secword2address(secword, { coin, world, pass, path, tool, hasher } = {}) {
static secword2address (secword, { coin, world, pass, path, tool, hasher } = {}) {
coin = my.COIN_LIST.indexOf(coin?.toUpperCase()) >= 0 ? coin.toUpperCase() : my.COIN
let kp = this.secword2keypair(secword, { coin, pass, path, tool, hasher })
if (kp) {
@ -550,7 +570,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static seckey2pubkey(seckey, { curve, compress } = {}) {
static seckey2pubkey (seckey, { curve, compress } = {}) {
if (this.isSeckey(seckey) && seckey.length === 64) {
// 只能用于32字节的私钥BTC, ETH)。也就是不能用于 TIC 的私钥。
curve = my.CURVE_LIST.indexOf(curve) >= 0 ? curve : my.CURVE // 默认为 secp256k1
@ -582,7 +602,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static seckey2address(seckey, { coin, world } = {}) {
static seckey2address (seckey, { coin, world } = {}) {
coin = my.COIN_LIST.indexOf(coin?.toUpperCase()) >= 0 ? coin.toUpperCase() : my.COIN
if (this.isSeckey(seckey)) {
/** @type {*} */
@ -608,7 +628,7 @@ class TICrypto {
* @memberof TICrypto
* position 就是通常所说的 PubKeyHash出现在比特币交易的锁定脚本里
*/
static pubkey2position(pubkey, { coin } = {}) {
static pubkey2position (pubkey, { coin } = {}) {
// tic, btc, eth 的 position 都是 20节=40字符的。
coin = my.COIN_LIST.indexOf(coin?.toUpperCase()) >= 0 ? coin.toUpperCase() : my.COIN
if (this.isPubkey(pubkey)) {
@ -622,8 +642,14 @@ class TICrypto {
.digest('hex')
.slice(-40)
} else {
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
let h256 = crypto
.createHash('sha256')
.update(Buffer.from(pubkey, 'hex'))
.digest()
let h160 = crypto
.createHash('ripemd160')
.update(h256)
.digest('hex')
return h160
}
}
@ -639,7 +665,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static position2address(position, { coin, world } = {}) {
static position2address (position, { coin, world } = {}) {
if (!/^[\da-fA-F]{40}$/.test(position)) return null // 不论 tic, btc, eth其 position 都是 40字符的。
if (coin) coin = coin.toUpperCase()
coin = my.COIN_LIST.indexOf(coin) >= 0 ? coin : my.COIN
@ -647,7 +673,9 @@ class TICrypto {
if (coin === 'ETH') {
// 对以太坊,按照 EIP55把纯位置转换为大小写敏感能自我验证的hex地址。仍然为20节=40符。
position = position.toLowerCase().replace('0x', '')
let hash = keccak('keccak256').update(position).digest('hex')
let hash = keccak('keccak256')
.update(position)
.digest('hex')
address = '0x'
for (var i = 0; i < position.length; i++) {
if (parseInt(hash[i], 16) >= 8) {
@ -719,16 +747,19 @@ class TICrypto {
* @memberof TICrypto
* 地址和PubKeyHash(即position)之间能互相转化
*/
static address2position() {
if (/^0x[\da-fA-F]{40}$/.test(address)) { // ETH
static address2position () {
if (/^0x[\da-fA-F]{40}$/.test(address)) {
// ETH
// todo: 如果是大小写敏感的,进行有效性验证
return address.toLowerCase()
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address)) { // BTC
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address)) {
// BTC
let hex = this.b58c_to_hex(address)
if (hex) {
return hex.slice(2) // 去除网络前缀
}
} else if (/^[Tt][0-9a-zA-Z\._]{31}$/.test(address)) { // TIC
} else if (/^[Tt][0-9a-zA-Z\._]{31}$/.test(address)) {
// TIC
// 格式合法
let hex = this.b64t_to_hex(address)
let [all, prefix, position, checksum] = hex.match(/^([\da-fA-F]{2})([\da-fA-F]{40})([\da-fA-F]{6})$/)
@ -747,7 +778,7 @@ class TICrypto {
* @return {Boolean}
* @memberof TICrypto
*/
static isAddress(address) {
static isAddress (address) {
if (/^(0x)?[\da-fA-F]{40}$/.test(address)) {
return 'ETH'
} else if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{26,34}$/.test(address) && address.length !== 32) {
@ -776,7 +807,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static pubkey2address(pubkey, { coin, world } = {}) {
static pubkey2address (pubkey, { coin, world } = {}) {
// pubkey 应当是string类型
coin = my.COIN_LIST.indexOf(coin?.toUpperCase()) >= 0 ? coin.toUpperCase() : my.COIN
return this.position2address(this.pubkey2position(pubkey, { coin }), { coin, world })
@ -791,7 +822,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static secword2seed(secword, pass) {
static secword2seed (secword, pass) {
// 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样是64字节的种子。
// 注意bip39.mnemonicToSeedSync 也接受不合法的 secword只要是个string或者是 undefined/null/0/''/false这几个的结果都一样
return bip39.mnemonicToSeedSync(secword, pass).toString('hex') // 结果一致与 new BitcoreMnemonic(secword).toSeed(pass).toString('hex') 或 ethers.HDNode.mnemonic2Seed(secword)。
@ -805,7 +836,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static randomSecword(lang = 'english') {
static randomSecword (lang = 'english') {
// accepts case-insensitive lang, such as 'chinese, cn, tw, en'
//// for BitcoreMnemonic
// lang=lang.toUpperCase()
@ -849,7 +880,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static randomSeckey({ coin, tool } = {}) {
static randomSeckey ({ coin, tool } = {}) {
// 跳过 secword 直接产生随机密钥
coin = my.COIN_LIST.indexOf(coin?.toUpperCase()) >= 0 ? coin : my.COIN
if (tool === 'nacl') {
@ -867,7 +898,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static randomKeypair({ tool, purpose } = {}) {
static randomKeypair ({ tool, purpose } = {}) {
let kp
if (tool === 'nacl') {
if (purpose === 'encrypt') {
@ -897,7 +928,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static randomAccount({ lang, coin, pass, path, tool, hasher } = {}) {
static randomAccount ({ lang, coin, pass, path, tool, hasher } = {}) {
let secword = this.randomSecword(lang)
return this.secword2account(secword, { coin, pass, path, tool, hasher })
}
@ -911,7 +942,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static randomString(length = 6, alphabet) {
static randomString (length = 6, alphabet) {
// 长度为 length字母表为 alphabet 的随机字符串
alphabet = alphabet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@'
var text = ''
@ -929,7 +960,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static randomNumber({ length, min, max } = {}) {
static randomNumber ({ length, min, max } = {}) {
// 长度为 length 的随机数字,或者 (min||0) <= num < max
var num = 0
if (typeof length === 'number' && length > 0) {
@ -955,7 +986,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static padStart(string, targetLength, symbol) {
static padStart (string, targetLength, symbol) {
// 2020-03: 发现在浏览器里,还不支持 string.padStart(),只好自己写个暂代。
let padLength = targetLength - string.length
for (let index = 1; index <= padLength; index++) {
@ -970,7 +1001,7 @@ class TICrypto {
* @static
* @memberof TICrypto
*/
static randomUuid() {
static randomUuid () {
return uuid.v4()
}
@ -983,7 +1014,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static getMerkleHash(hashList, { output, hasher } = {}) {
static getMerkleHash (hashList, { output, hasher } = {}) {
// merkle算法略有难度暂时用最简单的hash代替
if (Array.isArray(hashList)) {
myhasher = crypto.createHash(my.HASHER_LIST.indexOf(hasher) >= 0 ? hasher : my.HASHER)
@ -1004,7 +1035,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static getMerkleRoot(todoHashList) {
static getMerkleRoot (todoHashList) {
//深拷贝传入数组,防止引用对象被改变
let hashList = [...todoHashList]
if (!Array.isArray(hashList)) return null
@ -1041,11 +1072,14 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static distanceSig(hash, sig) {
static distanceSig (hash, sig) {
// hash为64hex字符sig为128hex字符。返回用hex表达的距离。
if (this.isSignature(sig) && this.isHash(hash)) {
var hashSig = this.hash(sig) // 把签名也转成32字节的哈希同样长度方便比较
return new BigInt(hash, 16).subtract(new BigInt(hashSig, 16)).abs().toString(16)
return new BigInt(hash, 16)
.subtract(new BigInt(hashSig, 16))
.abs()
.toString(16)
}
return null
}
@ -1060,7 +1094,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static compareSig(hash, sig1, sig2) {
static compareSig (hash, sig1, sig2) {
// 返回距离hash更近的sig
if (this.isHash(hash)) {
if (this.isSignature(sig2) && this.isSignature(sig1)) {
@ -1093,7 +1127,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static sortSigList(hash, sigList) {
static sortSigList (hash, sigList) {
if (Array.isArray(sigList) && this.isHash(hash)) {
sigList.sort(function (sig1, sig2) {
if (this.isSignature(sig1) && this.isSignature(sig2)) {
@ -1117,7 +1151,7 @@ class TICrypto {
* @param $para 需要拼接的数组把数组所有元素按照参数=参数值的模式用&字符拼接成字符串
* @return 拼接完成以后的字符串
*/
static getString2Sign(paramSet, converter, delimiter) {
static getString2Sign (paramSet, converter, delimiter) {
if (paramSet && typeof paramSet === 'object') {
var string2Sign = ''
var converter = converter || ''
@ -1152,7 +1186,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static rsaSign(string2Sign, prikey, signType) {
static rsaSign (string2Sign, prikey, signType) {
signType = signType || 'RSA-SHA1' // could be RSA-SHA256, RSA-SHA1 or more
let signer = crypto.createSign(signType)
return encodeURIComponent(signer.update(string2Sign).sign(prikey, 'base64'))
@ -1169,7 +1203,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static rsaVerify(string2Verify, signature, pubkey, signType) {
static 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, signature, 'base64')
@ -1183,7 +1217,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static buf_to_hex(buffer) {
static buf_to_hex (buffer) {
// buffer is an ArrayBuffer
return Array.prototype.map.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)).join('')
}
@ -1196,7 +1230,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static hex_to_buf(hex) {
static hex_to_buf (hex) {
return new Uint8Array(
hex.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
@ -1215,7 +1249,7 @@ class TICrypto {
* bs58check bs58 可接受string, Buffer, ArrayBuffer, Array 包括空字符串'', 各种内容的数组例如包含 undefined{...}等等;
* 不可接受 undefined, null, {...}, 等等会返回 exception
*/
static hex_to_b58c(hex) {
static hex_to_b58c (hex) {
try {
return bs58check.encode(Buffer.from(hex, 'hex'))
} catch (exception) {
@ -1223,7 +1257,7 @@ class TICrypto {
}
}
static hex_to_b58(hex) {
static hex_to_b58 (hex) {
try {
return bs58.encode(Buffer.from(hex, 'hex'))
} catch (exception) {
@ -1239,7 +1273,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static b58c_to_hex(box) {
static b58c_to_hex (box) {
try {
return bs58check.decode(box).toString('hex')
} catch (exception) {
@ -1247,7 +1281,7 @@ class TICrypto {
}
}
static b58_to_hex(box) {
static b58_to_hex (box) {
try {
return bs58.decode(box).toString('hex')
} catch (exception) {
@ -1263,11 +1297,14 @@ class TICrypto {
* @param {*} b64
* @returns
*/
static b64_to_b64t(b64='') {
return b64.replace(/\+/g, '.').replace(/\//g, '_').replace(/=/g, '')
static b64_to_b64t (b64 = '') {
return b64
.replace(/\+/g, '.')
.replace(/\//g, '_')
.replace(/=/g, '')
}
static b64t_to_b64(b64t='') {
static b64t_to_b64 (b64t = '') {
return b64t.replace(/\./g, '+').replace(/_/g, '/')
}
@ -1279,7 +1316,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static hex_to_b64t(hex) {
static hex_to_b64t (hex) {
if (/^[0-9a-fA-F]+$/.test(hex)) {
return this.b64_to_b64t(Buffer.from(hex, 'hex').toString('base64'))
}
@ -1294,7 +1331,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static b64t_to_hex(b64t) {
static b64t_to_hex (b64t) {
if (/^[0-9a-zA-Z\._]+$/.test(b64t)) {
return Buffer.from(this.b64t_to_b64(b64t), 'base64').toString('hex')
}
@ -1302,25 +1339,25 @@ class TICrypto {
}
// https://en.wikipedia.org/wiki/Base32
static hex_to_b32(hex, {encoding='RFC4648'}={}) {
static hex_to_b32 (hex, { encoding = 'RFC4648' } = {}) {
if (/^[0-9a-fA-F]+$/.test(hex)) {
return base32encode(Buffer.from(hex, 'hex'), encoding)
}
return null
}
static b32_to_hex(b32, {encoding='RFC4648'}={}) {
static b32_to_hex (b32, { encoding = 'RFC4648' } = {}) {
if (/^[A-Za-z2-7=]+$/.test(b32)) {
return Buffer.from(base32decode(b32.toUpperCase(), encoding)).toString('hex')
}
return null
}
static hex_to_b32h(hex, {encoding='RFC4648-HEX'}={}) {
static hex_to_b32h (hex, { encoding = 'RFC4648-HEX' } = {}) {
if (/^[0-9a-fA-F]+$/.test(hex)) {
return base32encode(Buffer.from(hex, 'hex'), encoding)
}
return null
}
static b32h_to_hex(b32, {encoding='RFC4648-HEX'}={}) {
static b32h_to_hex (b32, { encoding = 'RFC4648-HEX' } = {}) {
if (/^[0-9A-Va-v=]+$/.test(b32)) {
return Buffer.from(base32decode(b32.toUpperCase(), encoding)).toString('hex')
}
@ -1335,10 +1372,12 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static hex_to_eip55(hex) {
static hex_to_eip55 (hex) {
if (/^(0x)?[\da-fA-F]+$/.test(hex)) {
hex = hex.toLowerCase().replace('0x', '')
let hash = keccak('keccak256').update(hex).digest('hex')
let hash = keccak('keccak256')
.update(hex)
.digest('hex')
let result = ''
for (var i = 0; i < hex.length; i++) {
if (parseInt(hash[i], 16) >= 8) {
@ -1360,7 +1399,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static compressPubkey(uncompressed) {
static compressPubkey (uncompressed) {
// test: https://iancoleman.io/bitcoin-key-compression/
// compress: https://hacpai.com/article/1550844562914
// 把 04xy 的非压缩公钥 转成 02x 或 03x 的压缩公钥
@ -1385,7 +1424,7 @@ class TICrypto {
* @return {*}
* @memberof TICrypto
*/
static decompressPubkey(compressed) {
static decompressPubkey (compressed) {
// uncompress: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/53478265#53478265
// https://en.bitcoin.it/wiki/Secp256k1
// 把 02x 或 03x 的压缩公钥 转成 04xy 的非压缩公钥
@ -1394,13 +1433,73 @@ class TICrypto {
const pIdent = new BigInt('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
var signY = new Number(compressed[1]) - 2
var x = new BigInt(compressed.substr(2), 16)
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 the parity doesn't match it's the *other* root
y = prime.subtract(y) // y = prime - y
}
return '04' + this.padStart(x.toString(16), 64, '0') + this.padStart(y.toString(16), 64, '0')
}
static cid_to_cosh ({ cid }) {
if (/^[Q|1]/.test(cid)) {
return this.b58_to_hex(cid).slice(4)
} else if (/^b/.test(cid)) {
return this.b32_to_hex(cid.substr(1)).slice(8)
} else if (/^z/.test(cid)) {
return this.b58_to_hex(cid.substr(1)).slice(8)
}
}
static cosh_to_cid ({ cosh, cidBase = 'b32', cidVersion = 1, cidCodec = 'raw', cidAlgo = 'sha256' }) {
const multibase = {
identity: 0x00,
b2: '0',
b8: '7',
b10: '9',
b16: 'f',
B16: 'F',
b32: 'b',
B32: 'B',
b32h: 'v',
B32h: 'V',
b36: 'k',
b64: 'm',
b64p: 'M',
b64u: 'u',
b64up: 'U',
b58: 'z',
}
const multicodec = {
dagpb: '70',
p2pkey: '72',
raw: '55'
}
const multialgo = {
identify: '00',
sha256: '12'
}
if (cidVersion === 0) {
return this.hex_to_b58(`${multialgo[cidAlgo]}${Number(cosh.length/2).toString(16)}${cosh}`)
}
if (cidVersion === 1) {
let fullHex = `01${multicodec[cidCodec]}${multialgo[cidAlgo]}${Number(cosh.length/2).toString(16)}${cosh}`
if (cidBase==='b32') {
return multibase[cidBase] + this.hex_to_b32(fullHex).toLowerCase().replace(/=/g,'')
}else if (cidBase==='b58') {
return multibase[cidBase] + this.hex_to_b58(fullHex)
}
}
}
static string_to_raw_cid (str) {
return this.cosh_to_cid({ cosh: this.hash(str), cidVersion:1, cidCodec:'raw' })
}
}
// 必须单独写 module.exports不要和类定义写在一起否则会导致 jsdoc 解析不到类内文档。