Compare commits

..

122 Commits

Author SHA1 Message Date
Luk
c77f28fe30 rename git.faronear.org to git.tic.cc 2025-04-11 16:49:02 +08:00
luk
af48ee6f10 u 2024-11-03 14:04:26 +08:00
luk
62acb1c453 让各个编码转换的方法 (b58_to_hex, hex_to_b32, ...) 在错误情况下返回 '' 而不是 null。不知道会不会导致其它地方的问题。 2024-11-03 11:50:21 +08:00
Luk
decdcab1e5 crypto.randomUUID().replace(/-/g,'').substring(0,12) 2024-10-14 15:23:50 +08:00
Luk
ced82b18d7 用 secword.trim() 取代 secword.replace(/^\s+/, '').replace(/\s+$/, '') 2024-10-10 13:04:17 +08:00
Luk
acabbfa787 用原生 BigInt 实现了 modPow 从而重新实现了 decompress_pubkey; 调整 prikey_to_pubkey({compress}) 为 prikey_to_pubkey({uncompress}) 2024-10-05 18:04:24 +08:00
Luk
40197b2905 发现 BigInt=require('big-integer') 可能导致和原生 BigInt 的命名冲突,改为 bigint,同时把可以用原生 BigInt 的都用 BigInt。发现 /^[b|B]/ 这种写法会识别 |,改为 /^[bB]/ 2024-10-05 15:47:17 +08:00
Luk
43b9bd6898 ignore *nogit* and *nosf*; 似乎 hash_to_sig_distance 用的还是之前的 BigNumber,现在是失效的,重新改写。 2024-09-22 15:50:33 +08:00
Luk
ec367b9bbd improve gitignore and seafile-ignore 2024-04-24 14:17:25 +08:00
Luk
2b4325a03f updated .gitignore and seafile-ignore.txt using npm/sysconfig/*-ignore-find2merge.sh 2024-01-28 12:16:39 +08:00
81436304fa update seafile-ignore.txt 2023-09-12 14:20:54 +08:00
8836721791 hex_to_eip55 returns 0x... if the paramhex is 0x... 2023-08-03 17:35:28 +08:00
16e45503be u 2023-04-14 09:12:41 +08:00
b34b3b4cd5 customize_account 2023-03-10 22:05:09 +08:00
90dda262da 'add seafile-ignore.txt to every git repo' 2023-01-12 20:10:13 +08:00
e304a5004a 如果不提供pathIndex,默认为0 2023-01-10 13:11:45 +08:00
c5b0d40150 fix: pathIndex 为0时不该被忽略 2023-01-03 18:34:41 +08:00
c0e237984a 让 secword_to_keypair 的默认结果和 ethers.Wallet.fromMnemonic保持一致 2023-01-01 17:08:57 +08:00
cdeb44f43f update to standard .gitignore 2022-12-10 19:12:14 +08:00
ab105032ad rename ethrsa to ecrsa 2022-11-12 08:42:06 +08:00
ec359d0d98 add: 通过 ethereum-rsa 实现了椭圆曲线公私钥加解密!fix: is_secword 2022-11-11 16:17:18 +08:00
c9b147aeaf 增加 coinFamily 参数 2022-10-28 12:09:31 +08:00
eed025c4e1 update [.gitignore] to ignore /unpackage/* except for !/unpackage/res/ 2022-10-06 20:06:04 +08:00
3c9226d5f0 [.prettierrc.js] update jsxBracketSameLine:false to bracketSameLine:true so that HTML closing bracket not in a seperate line; [package.json] delete standardx 2022-10-05 13:10:03 +08:00
93eb774457 rename pathRoot to pathSeed 2022-09-17 15:10:16 +08:00
dc834c8157 rename 'boot:windows' to 'boot:win'; install 'run-script-os' with '--save-dev'. 2022-09-13 11:49:13 +08:00
a077f95b23 reverse 'base32-encode' version to 1.2.0 because 2.0.0 must 'import' no longer support 'require' 2022-08-26 20:10:12 +08:00
4e8730e989 delete 'global.inDev', use 'global.envar.inDev' instead. 2022-08-25 15:17:31 +08:00
a5b0e68efc use standard [.gitignore] file 2022-08-20 10:24:53 +08:00
98f055dfb3 upgrade npm libraries to latest major versions 2022-08-18 13:06:22 +08:00
a43a43ed16 u 2022-08-17 12:22:44 +08:00
d54cc58c80 u 2022-08-16 12:50:24 +08:00
71eb5d5dce Merge branch 'main' of https://git.faronear.org/npm/tic-crypto 2022-08-16 11:59:32 +08:00
1893a0ae9d <secword_to_keypair> should check secword because secword_to_seed accepts even empty secword 2022-08-16 11:59:28 +08:00
e7f538b472 rename seckey to prikey 2022-08-14 15:51:19 +08:00
7a8ea70608 u 2022-08-14 14:50:23 +08:00
741eb70245 u 2022-08-12 12:03:02 +08:00
a9dac7e858 add secp256k1 library 2022-08-03 16:34:57 +08:00
34cf26e168 把大多数ticc方法的参数类型统一为 { ... } 对象 2022-08-03 16:30:09 +08:00
0cb2547d17 rename this.hash to this.hash_easy 2022-08-03 14:53:31 +08:00
cc01e71f48 rename ticc.sign/verify/encrypt/decrypt/hash to ticc.xxx_easy 2022-07-23 16:03:21 +08:00
1d79a3b560 updated 2022-07-23 12:40:04 +08:00
78c4e16868 rename pass2keypair to pass_to_keypair 2022-07-23 10:08:14 +08:00
f73f4beb23 u 2022-07-03 23:01:29 +08:00
5bfd536c03 u 2022-07-03 16:03:09 +08:00
dff0b62d64 rename all libs from xxx.yyy to xxx-yyy 2022-06-04 12:08:49 +08:00
24450a4022 优化 2022-06-04 08:17:40 +08:00
c2adfd5a14 u 2022-06-02 13:06:42 +08:00
32b36dff0a u 2022-05-28 13:32:19 +08:00
74694866bc 添加 cid 转换函数 2022-05-28 09:08:55 +08:00
447a3eb442 rename b64u to b64t; rename x2y 为 x_to_; add b32 的转换函数 2022-05-12 08:52:48 +08:00
b2abb16e81 优化 b64u 的定义,用 . 代替 -,因为 - 和空格一样导致 css white-space 自动换行 2022-04-29 15:21:52 +08:00
fcf41dd77e substring change to substr 2022-04-03 14:32:30 +08:00
7fb2590670 fix: b582hex 里错误的调用了 bs58check 2021-11-16 09:57:02 +08:00
50a88a7efd add hex2b58 and b582hex 2021-11-15 23:20:50 +08:00
8df15515a6 u 2021-11-15 23:06:28 +08:00
31384ce340 u 2021-10-15 19:40:50 +08:00
cd37a2b9bb move aiid2regcode/regcode2aiid to core.tool 2021-09-24 16:43:29 +08:00
f0d90036b1 u 2021-09-23 14:46:25 +08:00
53c6603ac4 Merge branch 'main' of https://git.faronear.org/npm/tic.crypto 2021-09-23 10:15:21 +08:00
45f748afb6 add support for EXT coin 2021-09-23 10:15:18 +08:00
a276bbe703 u 2021-09-22 21:19:27 +08:00
4f23d79919 bugfix: seed2path 忘了解析出part5 2021-09-07 15:39:24 +08:00
99e748e269 [README.md] 补充一些测试记录 2021-08-24 16:05:25 +08:00
7ac41c9f32 buf fix: 把 options 换成 {...} 写法后,secword2address 没能把 world 参数传递到 position2address,导致生成地址错误。 2021-06-16 11:09:20 +08:00
d0500a9730 简化 jsdoc 相关设置 2021-06-07 10:50:13 +08:00
812ce057a2 rename w1dev to comet 2021-05-22 14:54:49 +08:00
437ba95af4 u 2021-05-12 16:17:59 +08:00
5eb589ac2c 换回 crypto.createVerify 方案 2021-05-08 22:42:53 +08:00
30706da9fb crypto.sign/verify 在浏览器中不存在,换回原来的方案 2021-05-08 22:42:08 +08:00
8873cd8884 删掉所有 option 参数,换用具体的 {...} 2021-05-08 22:34:01 +08:00
8780cd1c40 crypto.createVerify(必须要有algorithm参数) 2021-05-08 17:23:59 +08:00
d2e9f368cc 优化 sign/verify/encrypt/decrypt 的参数为一整个对象 {...} 2021-05-08 16:48:34 +08:00
1a329157bd 必须添加 tool 参数 2021-05-08 10:24:40 +08:00
88ee0cd8df 优化 sign 和 verify 2021-05-07 20:15:01 +08:00
00287a0944 把 eccrypto 换成 eccrypto-js 2021-05-07 16:29:20 +08:00
43b913c4b4 u 2021-03-10 15:59:46 +08:00
4a43057f3c 优化 seed2path 2021-02-14 10:56:46 +08:00
73c33e3175 优化 isHashable 2021-01-24 11:57:38 +08:00
069a3ff28c 改造成 jsdoc 的注释格式 2020-11-11 19:36:44 +08:00
8c0fcdc3be 格式化 2020-11-05 10:25:35 +08:00
34b4a8b440 添加 .prettierrc.js 2020-11-05 09:48:13 +08:00
ae96a990f1 改进 README.md 2020-10-31 12:20:02 +08:00
df71cf346a 统一更新一批库的版本 2020-10-31 12:19:51 +08:00
ffad2cc8f6 fixed: big-integer 没有 sub 方法,改成 subtract 2020-05-07 16:57:19 +08:00
3145d4864c 似乎 secp256k1 导致 hdkey 的 derive 抛出异常,删掉试试。 2020-05-06 14:17:12 +08:00
4e1d6ae98b 把 net 改名 world, testnet 改名 moon, mainnet 改名 earth, devnet 改名 w1dev 2020-05-06 10:29:54 +08:00
6154eaef29 u 2020-05-04 15:37:09 +08:00
b6b90b6a38 u 2020-05-03 08:22:35 +08:00
c5334b6c8f 默认采用 crypto 的 sign/verify,不再默认用 eccrypto 的。 2020-03-31 08:50:07 +08:00
36491d495f 把 TIC的bip44路径从66改成 60000 2020-03-27 18:15:33 +08:00
fdeb2cca9b u 2020-03-14 16:35:39 +08:00
0baa845a16 给 isSecword设置了easy|strict模式。 2020-03-13 18:48:10 +08:00
e96b61ed0d 在 secword2seed 和 secword2keypair 里 放松对 isSecword 的检查,因为isSecword在安卓和安卓微信里不成功! 2020-03-13 18:02:35 +08:00
7e1d2ef6e1 继续改进兼容性:浏览器目前不支持 string.padStart; HBuilderX打包app不能够 crypto 的 setPrivateKey(...) 2020-03-13 16:00:47 +08:00
0eaed0f118 u 2020-03-13 08:37:47 +08:00
42128a6c8e u 2020-03-12 21:05:05 +08:00
c9dbded768 纠正isSecword,使其对所有语言的secword都能用。 2020-03-12 20:52:25 +08:00
b923fec9cc 完善 isSecword,使用 bip39.mnemonicToEntropy 来精确判断。 2020-02-29 07:28:33 +08:00
9454bfd5a0 修复一个bug: isAddress()里比特币长度不可能为32,否则会和TIC地址冲突。 2020-02-27 12:39:01 +08:00
7345c64de6 u 2020-02-26 10:13:43 +08:00
983750e7f5 小改进 2020-02-26 10:08:34 +08:00
cbd46d62a7 为避免 nodejs 的 crypto 的 sign 产生的签名不固定,换用 eccrypto 的 sign 做为默认。 2020-02-26 09:35:22 +08:00
354a90a8ad 优化 randomSecword,使得接受更多格式的语言代码 2020-02-23 22:02:41 +08:00
e1985e73e6 用 bip39 和 hdkey 替换掉 bitcore-mnemonic,前两者的node_modules有6M,而bcm 的有10M 而且无法在app里使用。 2020-02-23 13:24:52 +08:00
0d1d768a68 犯了愚蠢小错误,没有计算 secword.split()的长度 2020-02-23 11:22:10 +08:00
20169a56ee 改进 isSecword 防止数量不对导致 bitcore-mnemonics 异常。改进 isAddress 把 prefix 也纳入 TIC地址的校验。 2020-02-23 10:46:19 +08:00
bdec8f6acb TIC 地址采用 b64u 编码 2020-02-21 10:13:05 +08:00
2a0692e9be sign和verify应当对数据的哈希进行,而不是对数据本身。 2020-02-20 17:05:25 +08:00
72b89fe997 tic 默认使用和 btc, eth 一样的公私钥算法 2020-02-20 16:30:47 +08:00
09d831af00 用eccrypto加解密,用crypto签名。互相转换压缩和非压缩公钥 2020-02-20 13:36:44 +08:00
c494dd51de 更新一些库的版本 2020-02-18 12:25:58 +08:00
53b0c4a6c0 improve randomSecword to accept zhCN, enUS, ... as lang 2020-02-14 18:18:44 +08:00
5a378f4daa 研究了 BIP44,添加了注解。 2019-12-12 15:14:47 +08:00
cf4b5efac3 修正 [package.json],添加 keccak 库。 2019-12-11 23:39:15 +08:00
5b4bceb2eb 实现了以太坊地址的生成 2019-12-11 16:13:51 +08:00
887035f496 add .gitignore 2019-10-24 20:56:18 +08:00
ac18d4d8c8 更新 'README.md' 2019-08-27 07:32:11 +00:00
1d45249200 整理 GIT 项目库导航 结构 2019-08-26 15:19:40 +08:00
2b86f3721d [index.js] set BTC address type default to mainnet 2019-08-22 15:40:51 +08:00
d3277971ad 文档改进:latest_stable => RELEASE_OR_BRANCH 2019-06-17 11:17:27 +08:00
3a74f9c2f1 添加 [README.md] 2019-05-18 16:37:34 +08:00
9 changed files with 2155 additions and 490 deletions

113
.gitignore vendored Normal file
View File

@ -0,0 +1,113 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# how to include another gitignore?
# https://stackoverflow.com/questions/7005142/can-i-include-other-gitignore-file-in-a-gitignore-file-like-include-in-c-li
# https://github.com/github/gitignore
# https://github.com/SlideWave/gitignore-include?tab=readme-ov-file#examples
# https://gitignore.io
### .gitignore.global.txt ###
# Self defined pattern to ignore
?*.gitignore
?*.gitignore/
?*.gitignore.*
?*.gitignore.*/
*.gitomit
*.gitomit.*
*.gitomit/
*.gitomit.*/
*.nogit
*.nogit.*
*.nogit/
*.nogit.*/
# 保留
!.gitignore
!.gitignore.*
!.gitkeep
# 通用
.svn/
.deploy_git/
.idea/
.sass-cache/
.wrangler
/test/unit/coverage/
/test/e2e/reports/
node_modules/
*.aab
*.apk
*.ipa
*.min.js
*.min.css
*.min.html
*.iml
*.njsproj
*.ntvs*
*.sw*
*.sln
*.suo
.gitattributes
.umi
.umi-production
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
selenium-debug.log
Thumbs.db
thumbs.db
_desktop.ini
# vue-cli 项目
/dist/
# 来自 vue-cli 创建项目的 .gitignore
.project
# hexo
/public/
# Hardhat
/artifacts/
/cache/
# seafile 临时文件
._*
.$*
# office 暂存文件
~$*
# 用户shell配置脚本
.bashrc_custom
# 苹果系统临时文件
.DS_Store
# 安卓缓存文件夹
.thumbnails
# local env files
.env.local
.env.*.local
# hexo
/db.json
# wo
# 服务端
/_archive/*
/_datastore/*
/_filestore/*
/_logstore/*
/_webroot/*
/_ssl/*
# uniapp 客户端
/unpackage/*
!/unpackage/res/
package-lock.json
pages4loader.json5
### .gitignore.local.txt ###

14
.prettierrc.js Normal file
View File

@ -0,0 +1,14 @@
/* VSCode Prettier Prettier TrailingComma
VSCode Prettier Standard 无效似乎是集成了不能修改的配置 */
module.exports = {
printWidth: 160, // default 80
tabWidth: 2, // default 2
useTabs: false,
semi: false, // default true
singleQuote: true, // default false
trailingComma: "es5", // none (default in v 1.*), es5 (default in v2.0.0), all
bracketSpacing: true, // default true
bracketSameLine: true, // default false
arrowParens: "always", // avoid (default in v1.9.0), always (default since v2.0.0)
quoteProps: "as-needed" // as-needed (default), consistent, preserve
};

109
README.md Normal file
View File

@ -0,0 +1,109 @@
# tic-crypto
时光链区块链密码学算法工具库:为区块链相关应用开发提供一套底层的基础算法工具库,用来处理哈希、加解密、签名、助记词、等等。
- 支持 md5、sha256 等算法的哈希
- 基于 bip39 等算法的助记词生成、检验
- 基于 secp256k1 等曲线算法的签名、交易的加解密
- 其他辅助算法工具
## 硬件环境
- 机型Mac 或 PC 机
- 内存8GB 以上
- 硬盘500G 以上
## 软件环境
- 操作系统:跨平台通用,支持 MacOS, Linux, Windows
- 开发环境:推荐 Visual Studio Code
- 运行环境nodejs 12.16 版本
## 安装指南
在前后端软件的 package.json 的依赖清单中引入本库:
```
npm install git+https://git.tic.cc/npm/tic-crypto#RELEASE_OR_BRANCH --save
```
## 用法
基本用法示例:
```
let ticc=require('tic-crypto') // 引用
let sw=ticc.randomize_secword() // 生成一个随机的助记词(即密语)。或者使用现成的密语。
let kp=ticc.secword_to_keypair({secword:sw}) // 把密语转换成公私钥
let address=ticc.secword_to_address({secword:sw}) // 把密语转换成地址
```
## 其他
```
const keyPair = crypto.generateKeyPairSync('rsa', {
modulusLength: 520,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: ''
}
})
```
这样生成的 keyPair.privateKey 开头是 -----BEGIN ENCRYPTED PRIVATE KEY-----
如果直接
```
crypto.privateEncrypt(kp.privateKey, Buffer.from('sdafasfdsaf'))
```
会报错
```
Uncaught TypeError: Passphrase required for encrypted key
```
所以要这样才行
```
crypto.privateEncrypt({key:kp.privateKey, passphrase:''}, Buffer.from('sdafasfdsaf'))
```
我从 https://www.cnblogs.com/chyingp/p/nodejs-asymmetric-enc-dec.html 抄到一个 privateKey 可以直接使用,不需要 passphrase
返回 Buffer。每次结果都一样
这样生成的 keyPair.publicKey 开头是 -----BEGIN PUBLIC KEY-----
可以直接
```
crypto.publicEncrypt(kp.publicKey, Buffer.from('sdafasfdsaf'))
```
返回 Buffer。每次结果不一样
似乎 crypto 一定要 rsa 公私钥才可以用加解密ticc.randomize_keypair() 生成的 ecc 公私钥不行。
而 eccrypto 和 eccrypto-js 可以用。eccrypto.generateKeyPair() 生成的和 ticc.randomize_keypair() 一样
eccrypto 在 windows 上的安装有麻烦,一来需要手工安装 OpenSSL 到 c:\openssl-win64\,二来 openssl 1.1.0 起把 libeay32.lib 改名为 libcrypto.dll而 eccrypto 需要 c:\openssl-win64\lib\libeay32.lib会报错
eccrypto-js 在 devDependencies 里继承了 eccrypto因此 npm i --production 即可
base32 有多种字符集:[Base32 - Wikipedia](https://en.wikipedia.org/wiki/Base32)
IPFS 用的是 RFC4648 字符集
- 从数到数符串Number(数).toString(进制数)0x 数.toString(进制数), 0b 数.toString(进制数)
- 从数符串到数字parseInt(str, 进制数)
- Buffer 到数符串: Buffer.toString(编码方案例如'hex','base64',默认'utf8')
- 字符串到 Buffer: Buffer.from(data, 编码方案如'hex','base64',默认'utf8')

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

483
index.js
View File

@ -1,483 +0,0 @@
const BigNumber=require('bignumber.js') // https://github.com/MikeMcl/bignumber.js 几个库的比较: node-bignum: 使用到openssl在windows上需要下载二进制包有时下载失败。bigi: 不错。 bignumber.js不错。
const crypto=require('crypto')
const nacl = require('tweetnacl')
const bs58check = require('bs58check')
const uuid = require('uuid')
const Secword = require('bitcore-mnemonic') // https://bitcore.io/api/mnemonic/ https://github.com/bitpay/bitcore-mnemonic
// const bip39 = require('bip39') // https://github.com/bitcoinjs/bip39 // 有更多语言,但不方便选择语言,也不能使用 pass
// const HDKey = require('hdkey') // https://github.com/cryptocoinjs/hdkey // 或者用 bitcore-mnemonic 或者 ethers 里的相同功能
// 全部以hex为默认输入输出格式方便人的阅读以及方便函数之间统一接口
const my={}
my.HASHER='sha256' // 默认的哈希算法。could be md5, sha1, sha256, sha512, ripemd160。 可用 Crypto.getHashes/Ciphers/Curves() 查看支持的种类。
my.HASHER_LIST=crypto.getHashes()
my.CIPHER='aes-256-cfb' // 默认的加解密算法
my.CIPHER_LIST=crypto.getCiphers()
my.CURVE='secp256k1' // 默认的ECDH曲线用于把私钥转成公钥。
my.CURVE_LIST=['secp256k1'] // crypto.getCurves() 引入到浏览器里后出错,不支持 getCurves.
my.OUTPUT='hex' // 默认的哈希或加密的输入格式
my.OUTPUT_LIST=['hex','latin1','base64'] // or 'buf' to Buffer explicitly
my.INPUT='utf8' // 默认的加密方法的明文格式。utf8 能够兼容 latin1, ascii 的情形
my.INPUT_LIST=['utf8', 'ascii', 'latin1'] // ignored for Buffer/TypedArray/DataView
my.COIN='TIC' // 默认的币种
my.COIN_LIST=['TIC','BTC','ETH']
module.exports = {
hash:function(data, option){ // data can be anything, but converts to string or remains be Buffer/TypedArray/DataView
if (this.isHashable(data)) {
option=option||{}
if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView))
data=JSON.stringify(data)
if (option.salt && typeof(option.salt)==='string')
data=data+this.hash(option.salt)
let hasher= my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER // 默认为 sha256.
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.
let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // option.output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64'
return crypto.createHash(hasher).update(data, inputEncoding).digest(outputEncoding)
}
return null
}
,
isHashable:function(data, option){
option=option||{}
if (option.strict) {
return data && typeof(data)!=='boolean' && data!==Infinity // 允许大多数数据,除了空值、布尔值、无限数
}
return typeof(data)!=='undefined' // 允许一切数据,除非 undefined
}
,
isHash:function(hash, option){
option=option||{}
option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
switch(option.hasher){
case 'sha256': return /^[a-fA-F0-9]{64}$/.test(hash)
case 'md5': return /^[a-fA-F0-9]{32}$/.test(hash)
case 'ripemd160': case 'sha1': return /^[a-fA-F0-9]{40}$/.test(hash)
case 'sha512': return /^[a-fA-F0-9]{128}$/.test(hash)
}
return false
}
,
encrypt: function(data, pwd, option){
if (this.isHashable(data) && typeof(pwd)==='string') {
option=option||{}
let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8' by default, 'ascii', 'latin1' for string or ignored for Buffer/TypedArray/DataView
let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // 'latin1', 'base64', 'hex' by default or 'buf' to Buffer explicitly
let cipher=crypto.createCipher(
my.CIPHER_LIST.indexOf(option.cipher)>=0?option.cipher:my.CIPHER,
this.hash(pwd))
if (typeof(data)!=='string' && !(data instanceof Buffer) && !(data instanceof DataView))
data=JSON.stringify(data)
let encrypted = cipher.update(data, inputEncoding, outputEncoding)
encrypted += cipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
return encrypted
}
return null
}
,
decrypt: function(data, pwd, option){ // data 应当是 encrypt 输出的数据类型
if (data && (typeof(data)==='string' || data instanceof Buffer) && typeof(pwd)==='string') {
option=option||{}
let inputEncoding=my.OUTPUT_LIST.indexOf(option.input)>=0?option.input:my.OUTPUT // input (=output of encrypt) could be 'latin1', 'base64', 'hex' by default for string or ignored for Buffer
let outputEncoding=(option.output==='buf')?undefined:(my.INPUT_LIST.indexOf(option.output)>=0?option.output:my.INPUT) // output (=input of encrypt) could be 'latin1', 'ascii', 'utf8' by default or 'buf' to Buffer explicitly
let decipher=crypto.createDecipher(
my.CIPHER_LIST.indexOf(option.cipher)>=0?option.cipher:my.CIPHER,
this.hash(pwd))
let decrypted = decipher.update(data, inputEncoding, outputEncoding)
decrypted += decipher.final(outputEncoding) // 但是 Buffer + Buffer 还是会变成string
if (option.format==='json') { // 如果用户输入错误密码deciper也能返回结果。为了判断是否正确结果对应当是 json 格式的原文做解析来验证。
try{
JSON.parse(decrypted)
}catch(exception){
return null
}
}
return decrypted
}
return null
}
,
sign: function(data, seckey, option) { // data can be string or buffer or object, results are the same
if (this.isHashable(data) && this.isSeckey(seckey)) {
option=option||{}
// 使用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尚未彻底实现。
// 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.
// let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT)
// let signer=crypto.createSign(hasher)
// return signer.update(data, inputEncoding).sign(seckey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式因此这样写是不能用的。
}
return null
}
,
isSignature:function(signature){
return /^[a-fA-F0-9]{128}$/.test(signature)
}
,
verify: function (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)){
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
}
return null
}
,
pass2keypair:function(pass, option){ // 如果使用其他机制例如密码、随机数不使用secword也可生成keypair
if (this.isHashable(pass)){
option=option||{}
option.hasher=my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER
var hashBuf = crypto.createHash(option.hasher).update(pass).digest()
var keypair = nacl.sign.keyPair.fromSeed(hashBuf)
return {
hash: hashBuf.toString('hex'),
pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型
seckey: Buffer.from(keypair.secretKey).toString('hex')
}
}
return null
}
,
secword2keypair: function(secword, option){ // option.coin 币种option.passphase 密码默认为空option.path==='master' 生成 HD master key不定义则默认为相应币种的第一对公私钥。
if (Secword.isValid(secword)){
option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
if(option.coin==='TIC') {
// 采用自己的算法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()
let keypair = nacl.sign.keyPair.fromSeed(hashBuf) // nacl.sign.keyPair.fromSeed 要求32字节的种子而 this.secword2seed生成的是64字节种子所以要先做一次sha256
return {
coin: option.coin,
pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型
seckey: Buffer.from(keypair.secretKey).toString('hex') // nacl.sign.keyPair.fromSeed 得到的 seckey 是64字节的不同于比特币/以太坊的32字节密钥。
}
}else {
// 用 bip39 算法从 secword 到种子,再用 bip32 算法从种子到根私钥。这是比特币、以太坊的标准方式,结果一致。
// let hdmaster=HDKey.fromMasterSeed(new Buffer(this.secword2seed(secword, option.pass), 'hex')) // 和 new Secword(secword).toHDPrivateKey 求出的公私钥一样!
let hdmaster=new Secword(secword).toHDPrivateKey(option.pass) // 和 ethers.HDNode.fromMnemonic(secword)的公私钥一样。而 ethers.HDNode.fromMnemonic(secword).derivePath("m/44'/60'/0'/0/0")的公私钥===ethers.Wallet.fromMnemonic(secword [,"m/44'/60'/0'/0/0"])
let key=hdmaster
if (option.path==='master'){
key=hdmaster
}else if (!option.path) {
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
default: key=hdmaster.derive("m/44'/99'/0'/0/0"); break
}
}else { // 指定了路径 option.path例如 "m/44'/0'/0'/0/6" 或 "m/0/2147483647'/1"
key=hdmaster.derive(option.path)
}
return {
coin: option.coin,
seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果
pubkey: key.publicKey.toString('hex')
}
}
}
return null
}
,
seckey2pubkey:function(seckey, option){
option=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
let compress = ['compressed', 'uncompressed'].indexOf(option.compress)>=0?option.compress:'compressed' // 默认为压缩格式的公钥
return new crypto.ECDH(curve).setPrivateKey(seckey,'hex').getPublicKey('hex',compress).toString('hex') // ecdh.getPublicKey(不加参数) 默认为 'uncompressed'
// 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。
// 或者 require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'),compress).toString('hex')
// 或者 require('bitcore-lib').PublicKey.fromPrivateKey(new Btc.PrivateKey(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 私钥
let keypair=nacl.sign.keyPair.fromSecretKey(Buffer.from(seckey,'hex'))
return Buffer.from(keypair.publicKey).toString('hex') // 测试过 不能直接keypair.publicKey.toString('hex')不是buffer类型
}
return null
}
,
secword2account:function(secword, option){ // account 比 keypair 多了 address 字段。
option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
let kp=this.secword2keypair(secword, option)
if (kp) {
kp.address=this.pubkey2address(kp.pubkey, option)
return kp
}
return null
}
,
secword2address:function(secword, option){
option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
let address
let kp=this.secword2keypair(secword, option)
if (kp) {
return this.pubkey2address(kp.pubkey,option)
}
return null
}
,
isSecword:function(secword){
return Secword.isValid(secword)
}
,
isSeckey:function(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:function(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: function (address) {
return /^[m|t|d|T][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address) // && address.length>25 && bs58check.decode(address.slice(1)) && ['A'].indexOf(address[0]>=0)) {
}
,
pubkey2address:function (pubkey, option) { // pubkey 应当是string类型
option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
if (this.isPubkey(pubkey)) {
option = option||{}
let h256 = crypto.createHash('sha256').update(Buffer.from(pubkey, 'hex')).digest()
let h160 = crypto.createHash('ripemd160').update(h256).digest('hex')
let prefix
if (option.coin==='TIC'){
switch(option.netType){
case 'mainnet': prefix='42'; break; // '42'=>T, '6E'=>m
case 'testnet': prefix='7F'; break; // '7F'=>t
case 'devnet': prefix='5A'; break; // '5A'=>d
default: prefix='42' // 默认暂且为 42为了兼容已经运行的链。
}
}else if(option.coin==='BTC'){
switch (option.netType) {
case 'mainnet': prefix='00'; break; // 1
case 'testnet': prefix='6f'; break; // m or n
case 'p2sh': prefix='05'; break; // 3
default: prefix='6f'
}
}else { // 目前不支持 ETH或其他币种 地址转换因为这会大量增加前端打包的js。
return null
}
var wifAddress=bs58check.encode(Buffer.from(prefix+h160,'hex')) // wallet import format
return wifAddress
}
return null
}
,
secword2seed:function(secword, pass) { // 遵循bip39的算法。和 ether.HDNode.mnemonic2Seed 结果一样是64字节的种子。
if (Secword.isValid(secword)) { // bip39.validateMnemonic(secword)) {
return new Secword(secword).toSeed(pass).toString('hex') // 结果一致于 bip39.mnemonicToSeedHex(secword) 或 ethers.HDNode.mnemonic2Seed(secword)
}
return null
}
,
randomSecword:function(lang){ // Object.keys(Secword.Words) => [ 'CHINESE', 'ENGLISH', 'FRENCH', 'ITALIAN', 'JAPANESE', 'SPANISH' ]
lang = (lang && Secword.Words.hasOwnProperty(lang.toUpperCase())) ? lang.toUpperCase() : 'ENGLISH'
return new Secword(Secword.Words[lang]).phrase
}
,
randomSeckey:function(option){
option=option||{}
option.coin=my.COIN_LIST.indexOf(option.coin)>=0?option.coin:my.COIN
if (option.coin==='TIC'){
return Buffer.from(nacl.sign.keyPair().secretKey).toString('hex') // 64字节
}else{
return Buffer.from(nacl.box.keyPair().secretKey).toString('hex') // 32字节
}
}
,
randomKeypair:function(option){
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')
}
}
,
randomString:function (length=6, alphabet) { // 长度为 length字母表为 alphabet 的随机字符串
alphabet = alphabet||"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*@"
var text = ''
for (var i = 0; i < length; i++) {
text += alphabet.charAt(Math.floor(Math.random() * alphabet.length))
}
return text
}
,
randomNumber:function(option){ // 长度为 option.length 的随机数字,或者 (option.min||0) <= num < option.max
option=option||{}
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
}else{ // 如果 option 为空
num=Math.random()
}
return num
}
,
randomUuid:uuid.v4
,
getMerkleHash:function(hashList, option){
// merkle算法略有难度暂时用最简单的hash代替
if(Array.isArray(hashList)){
option=option||{}
let output=(option.output==='buf')?undefined:(option.output||my.OUTPUT)
let hasher=crypto.createHash(my.HASHER_LIST.indexOf(option.hasher)>=0?option.hasher:my.HASHER)
for (var hash of hashList){
hasher.update(hash)
}
return hasher.digest(output)
}
return null
}
,
getMerkleRoot:function(todoHashList, option){
//深拷贝传入数组,防止引用对象被改变
let hashList = [...todoHashList]
if(!Array.isArray(hashList))
return null
var border = hashList.length;
if(border == 0)
return this.hash('')
if(border == 1)
return this.hash(hashList[0]);
while(1){
let i = 1,j = 0;
for(; i < border; i = i + 2){
hashList[j] = this.hash(hashList[i - 1] + hashList[i]);
if(border == 2){
return hashList[0];
}
if(i + 1 == border) break;
j = j + 1;
if(i + 2 == border){
i = i + 1;
hashList[j] = this.hash(hashList[i]);
break;
}
}
border = j + 1;
}
return hashList
}
,
distanceSig:function(hash, sig){ // hash为64hex字符sig为128hex字符。返回用hex表达的距离。
if (this.isSignature(sig) && this.isHash(hash)){
var hashSig=this.hash(sig) // 把签名也转成32字节的哈希同样长度方便比较
return new BigNumber(hash,16).minus(new BigNumber(hashSig,16)).abs().toString(16)
}
return null
}
,
compareSig:function(hash, sig1, sig2){ // 返回距离hash更近的sig
if (this.isHash(hash)) {
if (this.isSignature(sig2) && this.isSignature(sig1)) {
var dis1=this.distanceSig(hash,sig1)
var dis2=this.distanceSig(hash,sig2)
if (dis1<dis2) {
return sig1
}else if (dis1>dis2) {
return sig2
}else if (dis1===dis2) { // 如果极其巧合的距离相等,也可能是一个在左、一个在右,那就按 signature 本身的字符串排序来比较。
return sig1<sig2 ? sig1 : sig1===sig2 ? sig1 : sig2
}
}else if (this.isSignature(sig2)){ // 允许其中一个signature是非法的例如undefined
return sig2
}else if (this.isSignature(sig1)){
return sig1
}
}
return null
}
,
sortSigList:function(hash, sigList) {
if (Array.isArray(sigList) && this.isHash(hash)){
sigList.sort(function(sig1, sig2){
if (this.isSignature(sig1) && this.isSignature(sig2)) {
var winner=this.compareSig(hash, sig1, sig2)
if (sig1===sig2) return 0
else if (winner===sig1) return -1
else if (winner===sig2) return 1
}else { // 如果 sig1 或 sig2 不是 signature 格式
throw 'Not a signature!'
}
})
return sigList
}
return null
}
,
/**
* 把数组所有元素按照参数=参数值的模式用&字符拼接成字符串
* @param $para 需要拼接的数组
* return 拼接完成以后的字符串
*/
getString2Sign: function (paramSet, converter, delimiter) {
if (paramSet && typeof paramSet==='object') {
var string2Sign = ''
var converter = converter || ''
var delimiter = delimiter || ''
for (var key of Object.keys(paramSet).sort()) {
var value=paramSet[key]
if (value && typeof value==='object'){ // 万一 bis_content 等对象直接送了进来。
value=JSON.stringify(value)
}
if ((typeof value==='string' && value!=='') || typeof value==='number') {
if (converter==='urlencode') value=encodeURIComponent(value)
string2Sign += (key + '=' + delimiter + value + delimiter + '&') // 根据产品、版本、请求或响应的不同有的需要key="vlaue"有的只要key=value。
}
}
string2Sign=string2Sign.replace(/&$/, '') // 删除末尾的 &
// if (get_magic_quotes_gpc()) { $string2Sign = stripslashes($string2Sign); }
// string2Sign=string2Sign.replace(/\\/g, ''); // 去除转义符 \ (似乎其实不去除,也完全不会影响,因为编程语言内部就会处理掉\)
// string2Sign=string2Sign.replace(/\//g, '\\/'); // 为了verify把正斜杠进行转义 / 参见 https://openclub.alipay.com/read.php?tid=559&fid=2
return string2Sign
}
return ''
}
,
rsaSign: function(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'))
}
,
rsaVerify: function(string2Verify, sign, 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')
}
}

View File

@ -1,18 +1,28 @@
{ {
"name": "tic.crypto", "name": "tic-crypto",
"main": "ticc.js",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"bignumber.js": "^6.0.0", "base32-decode": "^1.0.0",
"bitcore-mnemonic": "^1.5.0", "base32-encode": "^1.2.0",
"bs58check": "^2.1.1", "big-integer": "^1.6.52",
"tweetnacl": "^1.0.0", "bip39": "^3.0.4",
"uuid": "^3.3.2" "bs58check": "^2.1.2",
"eccrypto-js": "^5.4.0",
"ethereum-rsa": "^1.0.5",
"hdkey": "^2.0.1",
"js-crypto-key-utils": "^1.0.4",
"keccak": "^3.0.2",
"secp256k1": "^4.0.3",
"tweetnacl": "^1.0.3"
}, },
"devDependencies": { "devDependencies": {
"docdash": "^1.2.0",
"jsdoc": "^3.6.6"
}, },
"scripts": { "scripts": {
"setup": "npm install" "doc": "jsdoc ./index.js -t node_modules/docdash --verbose"
}, },
"author": "" "author": ""
} }

72
seafile-ignore.txt Normal file
View File

@ -0,0 +1,72 @@
# https://help.seafile.com/syncing_client/excluding_files/
# 注释。通配符:* 匹配0到若干个字符包括代表目录的/。? 匹配1个字符包括/。
# seafile-ignore.txt 只能控制在客户端需要忽略哪些文件。你依然可以在 seahub 的 web 界面创建这些被客户端忽略的文件。
# 在这种情况下,
# 这些文件会被同步到客户端,但是用户在客户端对这些文件的后续修改会被忽略,不会被同步回服务器。
# 文件在服务器端的后续更改会被同步到客户端,如果客户端也同时修改了这些文件,系统会生成冲突文件。
# seafile-ignore.txt 只能忽略还没有被同步的文件。对于已经被同步的文件,如果后来把它添加到 seafile-ignore.txt 中,系统只会忽略后续更改,已经上传的版本不会受影响。
### seafile-ignore.global.txt ###
# 自定义的后缀名,凡有 sfignore 后缀的都不进行同步
*.sfignore
*.sfignore/
*.sfignore.*
*.sfignore.*/
*.sfomit
*.sfomit.*
*.sfomit/
*.sfomit.*/
*.nosf
*.nosf.*
*.nosf/
*.nosf.*/
.DS_Store
*/.DS_Store
.thumbnails
*/.thumbnails
Thumbs.db
*/Thumbs.db
thumbs.db
*/thumbs.db
_desktop.ini
*/_desktop.ini
._*
*/._*
.$*
*/.$*
~$*
*/~$*
node_modules/
*/node_modules/
package-lock.json
pages4loader.json5
.deploy_git/
*/.deploy_git/
# HBuilder 目录
unpackage/
*/unpackage/
Icon
OneDrive/Icon
# wrangler project
.dev.vars*
*/.dev.vars*
.wrangler/
*/.wrangler/
### seafile-ignore.local.txt ###

187
test.js Normal file
View File

@ -0,0 +1,187 @@
const bigInt = require('big-integer')
// Consts for secp256k1 curve. Adjust accordingly
// https://en.bitcoin.it/wiki/Secp256k1
const prime = new bigInt('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16), // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
pIdent = new bigInt('3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c', 16) // prime.add(1).divide(4);
console.log('pIdent=', pIdent.toString(), ' = ', pIdent.toString(16))
/**
* Point decompress secp256k1 curve
* @param {string} Compressed representation in hex string
* @return {string} Uncompressed representation in hex string
*/
function ECPointDecompress (comp) {
var signY = new Number(comp[1]) - 2
var x = new bigInt(comp.substring(2), 16)
// y mod p = +-(x^3 + 7)^((p+1)/4) mod p
console.log('ECP x=', x.toString(), ' = ', x.toString(16))
var y = x.modPow(3, prime).add(7).mod(prime).modPow(pIdent, prime)
// If the parity doesn't match it's the *other* root
console.log('ECP y=', y.toString(), ' = ', y.toString(16))
if (y.mod(2).toJSNumber() !== signY) {
// y = prime - y
y = prime.subtract(y)
}
console.log('ECP y=', y.toString(), ' = ', y.toString(16))
return '04' + x.toString(16).padStart(64, '0') + y.toString(16).padStart(64, '0')
}
let pubkey1 = ECPointDecompress('035d77c1e3eac37f685aeea2ae872c4e7e4d159756e57601db3bcccbc549f360b2')
console.log(pubkey1) // "045d77c1e3eac37f685aeea2ae872c4e7e4d159756e57601db3bcccbc549f360b24a323dd24b19c55f0a060ccd4bce314323bd7e804f3dfa8a77f14e3ab1cc4749"
correct = '045d77c1e3eac37f685aeea2ae872c4e7e4d159756e57601db3bcccbc549f360b2356d086fb7a78f3ce3359a4caee6dd4fcf0c19a961b1c36b5b442d031d219d75'
BigNumber = require('bignumber.js')
function uncompressPubkey (comp) {
// Consts for P256 curve. Adjust accordingly
const prime = new BigNumber('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16).integerValue(),
pIdent = prime.plus(1).idiv(4).integerValue()
console.log('pIdent=', pIdent.toString(), ' = ', pIdent.toString(16))
var signY = new Number(comp[1]) - 2
var x = new BigNumber(comp.substring(2), 16).integerValue()
console.log('x=', x.toString(), ' = ', x.toString(16))
// y^2 = x^3 - 3x + b
var y = x.pow(3).mod(prime).plus(7).mod(prime).pow(pIdent).mod(prime).integerValue()
console.log('y=', y.toString(), ' = ', y.toString(16))
// If the parity doesn't match it's the *other* root
if (y.mod(2).integerValue().toNumber() !== signY) {
// y = prime - y
y = prime.minus(y).integerValue()
}
console.log('yy=', y.toString(), ' = ', y.toString(16))
return '04' + x.toString(16).padStart(64, '0') + y.toString(16).padStart(64, '0')
}
let pubkey2 = uncompressPubkey('035d77c1e3eac37f685aeea2ae872c4e7e4d159756e57601db3bcccbc549f360b2')
console.log(pubkey2)
///////////////////////////////////////
const tic = require('./index')
const crypto = require('crypto')
const keyutil = require('js-crypto-key-utils') // https://github.com/junkurihara/jscu/tree/master/packages/js-crypto-key-utils
// https://github.com/arvati/crypto-keys
// nodejs cipher/decipher 用同一个密码在stream上操作。
let w = '驳 惊 而 煤 靠 客 示 待 诉 屈 屏 未' // tic.randomize_secword({lang:'chinese'})
console.log('secword = ', w)
let acc = tic.secword_to_account({ secword: w, coin: 'ETH' })
console.log('account = ', acc)
let add = tic.secword_to_address({ secword: w, coin: 'ETH' })
console.log('address = ', add)
/////////////////////// keyutil
let seckeyObject = new keyutil.Key('oct', Buffer.from(acc.prikey, 'hex'), { namedCurve: 'P-256K' }) // {P-256 : secp256r1, P-384 : secp384r1, P-521 : secp521r1, P-256K : secp256k1}
let seckeyObject2 = new keyutil.Key('oct', tic.hex_to_buf(acc.prikey, 'hex'), { namedCurve: 'P-256K' })
let seckeyPEM
seckeyObject.export('pem').then((data) => (seckeyPEM = data))
let seckeyDER
seckeyObject2.export('der').then((data) => (seckeyDER = data))
var signerKU = crypto.createSign('sha256')
signerKU.write('毛主席万岁')
signerKU.end()
var signatureKU = signerKU.sign(seckeyPEM) // specify format in [pem,der] and type in [pkcs1, pkcs8, sec1]
console.log('signature = ', signatureKU.toString('hex'))
console.log('length = ', signatureKU.toString('hex').length)
var signerKUDER = crypto.createSign('sha256')
signerKUDER.write('毛主席万岁')
signerKUDER.end()
var signatureKUDER = signerKUDER.sign({ key: seckeyDER, format: 'der', type: 'pkcs8' }) // specify format in [pem,der] and type in [pkcs1, pkcs8, sec1]
console.log('signature DER = ', signatureKUDER.toString('hex'))
console.log('length DER = ', signatureKUDER.toString('hex').length)
let pubkeyObject = new keyutil.Key('oct', Buffer.from(acc.pubkey, 'hex'), { namedCurve: 'P-256K' })
let pubkeyPEM
pubkeyObject.export('der').then((data) => (pubkeyPEM = data))
var verifyKU = crypto.createVerify('sha256')
verifyKU.write('毛主席万岁')
verifyKU.end()
var verified = verifyKU.verify(pubkeyPEM, signatureKU) // specify format in [pem,der] and type in [pkcs1,spki]
console.log('verified = ', verified) // 可以验证通过但是用的privatekey没有成功使用publickey。
crypto.createCipheriv('aes-256-cfb', Buffer.from(acc.prikey, 'hex'), Buffer.alloc(16))
////////////////////// crypto + PEM
toPEM = function (kp) {
let pubkey = crypto.createECDH('secp256k1').setPrivateKey(kp.prikey, 'hex').getPublicKey('hex', 'compressed')
console.log('ECDH created publickey = ', pubkey)
let mykey = '308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420' + kp.prikey + 'a144034200' + pubkey
console.log(mykey)
let privKey = '-----BEGIN PRIVATE KEY-----\n' + Buffer.from(mykey, 'hex').toString('base64') + '\n-----END PRIVATE KEY-----'
// pubKey2 = crypto.createPublicKey(privKey); //也可恢复出公钥。测试不成功。
return privKey
}
let privKeyPEM = toPEM(acc)
const signerPEM = crypto.createSign('sha256')
signerPEM.write('毛主席万岁')
signerPEM.end()
let signaturePEM = signerPEM.sign(privKeyPEM, 'hex') // 失败,无论对压缩或非压缩公钥
console.log('signaturePEM = ', signaturePEM)
let pemKP = toPEM(acc)
console.log('pemKP = ', pemKP)
//////////////////// crypto, DER
// https://stackoverflow.com/questions/58350484/why-nodejs-crypto-sign-function-only-accept-privatekey-pem-format
// https://www.shangyang.me/2017/05/24/encrypt-rsa-keyformat/
var buf1 = Buffer.from('308141020100301306072a8648ce3d020106082a8648ce3d030107042730250201010420', 'hex') // specific byte-sequence for curve prime256v1
var buf2 = Buffer.from(acc.prikey, 'hex') // raw private key (32 bytes)
var privateKeyPkcs8Der = Buffer.concat([buf1, buf2], buf1.length + buf2.length)
var sign = crypto.createSign('sha256')
sign.write('毛主席万岁')
sign.end()
var signature = sign.sign({ key: privateKeyPkcs8Der, format: 'der', type: 'pkcs8' }) // specify format in [pem,der] and type in [pkcs1, pkcs8, sec1]
console.log('signature = ', signature.toString('hex'))
console.log('length = ', signature.toString('hex').length)
var buf3 = Buffer.from('3059301306072a8648ce3d020106082a8648ce3d030107034200', 'hex') // specific byte-sequence for curve prime256v1
var buf4 = Buffer.from(acc.pubkey, 'hex') // raw public key (uncompressed, 65 bytes, startting with 04)
// 这个key无法sign。reason: 'too long'
//var publicKeyX509Der = Buffer.concat([buf3, buf4], buf3.length + buf4.length);
//var publicKey = crypto.createPublicKey({key:publicKeyX509Der, format:'der', type:'spki'})
var publicKey = crypto.createPublicKey({ key: privateKeyPkcs8Der, type: 'pkcs8', format: 'der' })
var publicKeyX509Der = publicKey.export({ type: 'spki', format: 'der' })
var verify = crypto.createVerify('sha256')
verify.write('毛主席万岁')
verify.end()
var verified = verify.verify({ key: publicKeyX509Der, format: 'der', type: 'spki' }, signature) // specify format in [pem,der] and type in [pkcs1,spki]
console.log('verified = ', verified) // 可以验证通过但是用的privatekey没有成功使用publickey。
/////////////////////// elliptic
var EC = require('elliptic').ec
// Create and initialize EC context
// (better do it once and reuse it)
var ec = new EC('secp256k1')
// Generate keys
//var key = ec.genKeyPair();
var key = ec.keyFromPrivate(acc.prikey) // 注意,不需要 'hex' 参数
// Sign the message's hash (input must be an array, or a hex-string)
var msgHash = tic.hash('毛主席万岁')
var msgHashBad = tic.hash('毛主席万岁 ')
var signature2 = key.sign(msgHash)
// Export DER encoded signature in Array
var derSign = signature2.toDER() // 无法直接导出成 hex。可以
console.log('signature by elliptic = ', Buffer.from(derSign).toString('hex'))
// 或者重新创建使用 pubkey也能成功
// ec.keyFromPublic(acc.pubkey, 'hex').verify(msgHash, signature2)
console.log(key.verify(msgHash, signature2))
console.log(key.verify(msgHashBad, signature2))
//////////////////
/*
createCipher/Decipher: 使用 pwd, 对称加解密已放弃
createCipheriv/Deciperiv: 使用 key, 对称加解密
private/publicEncrypt/Decrypt: 非对称加解密
crypto.privateEncrypt(crypto.generateKeyPairSync('rsa', {modulusLength:2048}).privateKey, Buffer.from('锦瑟无端五十弦'))
以上是唯一测出来可用的privatekey不能用 'ec', {namedCurve:'secp256k1'}, 也不能用crypto.createPrivateKey(pem格式的字符串)
*/

1643
ticc.js Normal file

File diff suppressed because it is too large Load Diff