gamerpc 7.1.1 → 7.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth3.0.md +189 -0
- package/package.json +13 -15
- package/src/utils/secp256k1.js +79 -101
- package/src/utils/socket.io.min.js +7 -7
- package/test/game/auth.js +1 -1
- package/test/game/crm.js +1 -1
- package/test/utils/secp256k1.js +444 -0
- package/webpack.config.js +16 -9
- package/.babelrc +0 -16
- package/test/game/mgr.js +0 -80
package/auth3.0.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# auth3.0 分支整改记录
|
|
2
|
+
|
|
3
|
+
> 日期:2026-07-15
|
|
4
|
+
> 分支:auth3.0(基于 V7.1.2 双连接器架构,保留区块链 + 游戏云兼容)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 一、背景:版本演进回顾
|
|
9
|
+
|
|
10
|
+
由于年代久远,最初同时兼容游戏连接器和区块链连接器的架构在后续版本中发生了重大变化。通过 Git 历史梳理三个关键版本:
|
|
11
|
+
|
|
12
|
+
| 版本 | Commit | 日期 | 架构 |
|
|
13
|
+
|------|--------|------|------|
|
|
14
|
+
| V6.0.6 | `1c9fabd` | 2021-07-03 | 双连接器(`toolkit.conn` + `toolkit.gameconn`),工具函数在 index.js 统一挂载 |
|
|
15
|
+
| V7.1.2 | `f5ba0e3` | 2023-11-14 | 工具函数内聚到各模块,引入本地 socket.io 兼容浏览器,OpenSSL 兼容 |
|
|
16
|
+
| V8.0.8 | `d80155e` | 2024-02-03 | 合并为单一 Remote 类,删除区块链连接器,支持 HTTPS/URL |
|
|
17
|
+
|
|
18
|
+
**auth3.0 分支的定位**:基于 V7.1.2,保留双连接器架构(同时支持 `toolkit.conn` 区块链全节点 + `toolkit.gameconn` 游戏云),并进行安全性现代化改造。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 二、仓库与分支管理
|
|
23
|
+
|
|
24
|
+
### Git 仓库
|
|
25
|
+
- **Gitee**(主):`https://gitee.com/bianque/gamegoldtoolkit.git`
|
|
26
|
+
- **GitHub**(镜像):`https://github.com/bookmansoft/gamegoldtoolkit`
|
|
27
|
+
|
|
28
|
+
### 分支操作
|
|
29
|
+
- 删除本地 `auth2.0` 分支(指向 `d80155e` V8.0.8,未合并,强制删除)
|
|
30
|
+
- 推送 `auth3.0` 到远端 `origin/auth3.0`,设置跟踪
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 三、安全改造:elliptic → @noble/secp256k1
|
|
35
|
+
|
|
36
|
+
### 问题
|
|
37
|
+
`elliptic` 存在安全漏洞 [GHSA-848j-6mx2-7j84](https://github.com/advisories/GHSA-848j-6mx2-7j84)(ECDSA 密码学原语风险实现),且截至 2026-01 无修复版本。原有 `elliptic` 作为生产依赖被 `secp256k1.js` → `verifyData.js`/`secret.js` → `index.js` 链式引用,用于签名、验签、ECDH 密钥交换。
|
|
38
|
+
|
|
39
|
+
### 方案
|
|
40
|
+
用 `@noble/secp256k1` v1 + `@noble/hashes` v1(CJS 兼容)完全替换 `elliptic`。
|
|
41
|
+
|
|
42
|
+
### 改动的文件
|
|
43
|
+
|
|
44
|
+
| 文件 | 操作 |
|
|
45
|
+
|------|------|
|
|
46
|
+
| `src/utils/secp256k1.js` | **重写** — 15 个导出函数用 noble API 重新实现,保持 Buffer 输入/输出兼容 |
|
|
47
|
+
| `package.json` | 移除 `elliptic`,新增 `@noble/secp256k1@^1.7.2` + `@noble/hashes@^1.8.0` |
|
|
48
|
+
|
|
49
|
+
### API 映射(关键差异)
|
|
50
|
+
|
|
51
|
+
| 旧 elliptic API | 新 noble API | 注意事项 |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `elliptic.ec('secp256k1')` | `require('@noble/secp256k1')` | 需配置 `utils.hmacSha256Sync` |
|
|
54
|
+
| `.sign()` + `.toDER()` | `secp.signSync()` | noble v1 默认返回 DER 格式 |
|
|
55
|
+
| `ecdh(pub, priv)` | `getSharedSecret(priv, pub)` | 参数顺序**相反**,需提取 x 坐标 `[1,33)` |
|
|
56
|
+
| `verify(msg, sig, key)` | `verify(sig, msg, key)` | 参数顺序**不同**(sig 和 msg 交换) |
|
|
57
|
+
| `bn.js` 大数运算 | 原生 `BigInt` | 不再需要 `bn.js` 模块 |
|
|
58
|
+
| `Signature.fromDER()` | `Signature.fromDER(hex)` | noble 接受 hex 字符串非 Buffer |
|
|
59
|
+
|
|
60
|
+
### 验证
|
|
61
|
+
|
|
62
|
+
新增 `test/utils/secp256k1.js`,45 个测试用例全覆盖:
|
|
63
|
+
|
|
64
|
+
| 测试类别 | 数量 | 覆盖 |
|
|
65
|
+
|----------|------|------|
|
|
66
|
+
| 密钥生成 | 6 | `generatePrivateKey`, `privateKeyVerify` |
|
|
67
|
+
| 公钥操作 | 8 | `publicKeyCreate`, `publicKeyConvert`, `publicKeyVerify`(含 known-vector `priv=1`) |
|
|
68
|
+
| 签名与验证 | 8 | `sign`, `verify`,RFC 6979 确定性签名,篡改/错误密钥检测 |
|
|
69
|
+
| DER 转换 | 5 | `fromDER`, `toDER`, `isLowS` |
|
|
70
|
+
| ECDH | 3 | 双方一致性,长度验证 |
|
|
71
|
+
| 公钥恢复 | 2 | `recover` |
|
|
72
|
+
| Tweak | 2 | `privateKeyTweakAdd`, `publicKeyTweakAdd` |
|
|
73
|
+
| 上层集成 | 6 | `verifyData.generateKey/signObj/verifyObj`, `Secret` BIP151 握手 |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 四、依赖安全审计:53 → 2 漏洞
|
|
78
|
+
|
|
79
|
+
### 修复前(53 个漏洞)
|
|
80
|
+
|
|
81
|
+
| 来源 | 数量 | 严重度 | 根因 |
|
|
82
|
+
|------|------|--------|------|
|
|
83
|
+
| babel 6.x 全家桶 | ~34 | critical | `babel-core`/`babel-traverse` 废弃 |
|
|
84
|
+
| elliptic(webpack 间接) | ~2 | critical | webpack4 的 `crypto-browserify` 链 |
|
|
85
|
+
| braces/micromatch | ~14 | low-moderate | webpack-cli 间接依赖 |
|
|
86
|
+
| json5 / serialize-javascript | ~3 | high | babel-loader / mocha 间接依赖 |
|
|
87
|
+
|
|
88
|
+
### 措施
|
|
89
|
+
|
|
90
|
+
| 操作 | 效果 |
|
|
91
|
+
|------|------|
|
|
92
|
+
| 删除 babel 全家桶(`babel`, `babel-core`, `babel-loader`, `babel-preset-*`) | 消除 34 个 critical |
|
|
93
|
+
| webpack 4 → 5,webpack-cli 3 → 5 | 消除 14 个 low/moderate |
|
|
94
|
+
| elliptic → `@noble/secp256k1` | 消除运行时 elliptic 风险 |
|
|
95
|
+
| 新增 `hash.js` 为直接依赖(原为 babel 间接依赖) | 修复 digest.js 的 `require('hash.js')` |
|
|
96
|
+
| 删除 `.babelrc` | 不再需要 |
|
|
97
|
+
|
|
98
|
+
### 修复后(2 个漏洞)
|
|
99
|
+
|
|
100
|
+
剩余 2 个漏洞均来自 mocha 测试框架的 `serialize-javascript` 间接依赖(仅用于 HTML 报告器),需 mocha 10→11 大版本升级才能消除(有破坏性变更风险,暂缓)。
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 五、Webpack 5 构建适配
|
|
105
|
+
|
|
106
|
+
### 问题
|
|
107
|
+
|
|
108
|
+
webpack 最初用于将项目打包成可在网页中通过 `<script>` 标签引入的单一 JS 文件。webpack 4 → 5 升级后有两个关键变化:
|
|
109
|
+
1. **不再自动 polyfill Node.js 核心模块**(`assert`, `buffer`, `crypto`, `stream`, `util`)
|
|
110
|
+
2. **默认输出格式变化**:从 IIFE 改为箭头函数,不再自动暴露 `module.exports`
|
|
111
|
+
|
|
112
|
+
### 最终 webpack.config.js
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
const path = require('path');
|
|
116
|
+
const config = require('./package.json');
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
entry: './src/index.js',
|
|
120
|
+
output: {
|
|
121
|
+
path: path.resolve(__dirname, 'lib'),
|
|
122
|
+
filename: `gamerpc${config.version}.js`,
|
|
123
|
+
library: { name: 'toolkit', type: 'umd' }, // 兼容 <script> 和 require()
|
|
124
|
+
globalObject: 'this',
|
|
125
|
+
},
|
|
126
|
+
target: 'web',
|
|
127
|
+
mode: 'production',
|
|
128
|
+
resolve: {
|
|
129
|
+
fallback: {
|
|
130
|
+
"assert": require.resolve("assert/"),
|
|
131
|
+
"buffer": require.resolve("buffer/"),
|
|
132
|
+
"crypto": require.resolve("crypto-browserify"),
|
|
133
|
+
"stream": require.resolve("stream-browserify"),
|
|
134
|
+
"util": require.resolve("util/"),
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 验证
|
|
141
|
+
- `npm run build` → `lib/gamerpc7.1.3.js`(398 KiB)
|
|
142
|
+
- 浏览器使用:`<script src="lib/gamerpc7.1.3.js"></script>` → `window.toolkit.xxx`
|
|
143
|
+
- Node.js 使用:`require('./src/')` 直接加载源码
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 六、其他修复
|
|
148
|
+
|
|
149
|
+
| 问题 | 修复 |
|
|
150
|
+
|------|------|
|
|
151
|
+
| `"main": "./lib/gamerpc7.1.2.js"` 指向不存在的文件 | 改为 `"main": "index.js"` |
|
|
152
|
+
| `hash.js` 缺失(原为 babel 间接依赖) | 新增为直接依赖 `"hash.js": "^1.1.7"` |
|
|
153
|
+
| Build 命令过于复杂 | 简化为 `"build": "npx webpack --mode=production"` |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 七、当前 package.json 依赖
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"dependencies": {
|
|
162
|
+
"@noble/hashes": "^1.8.0",
|
|
163
|
+
"@noble/secp256k1": "^1.7.2",
|
|
164
|
+
"create-hmac": "1.1.7",
|
|
165
|
+
"hash.js": "^1.1.7",
|
|
166
|
+
"node-fetch": "^2.6.1",
|
|
167
|
+
"socket.io-client": "^4.7.2"
|
|
168
|
+
},
|
|
169
|
+
"devDependencies": {
|
|
170
|
+
"cross-env": "^5.2.1",
|
|
171
|
+
"mocha": "^10.2.0",
|
|
172
|
+
"webpack": "^5.88.0",
|
|
173
|
+
"webpack-cli": "^5.1.4"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
生产依赖仅 6 个,开发依赖 4 个,极度精简。
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 八、验证清单
|
|
183
|
+
|
|
184
|
+
- [x] `npm test` — 45 个单元测试全部通过
|
|
185
|
+
- [x] `npm run build` — 成功生成 `lib/gamerpc7.1.3.js`
|
|
186
|
+
- [x] `npm audit` — 仅剩 2 个 devDependencies 漏洞(测试工具链,不影响生产)
|
|
187
|
+
- [x] 双连接器导出正常(`toolkit.conn` + `toolkit.gameconn`)
|
|
188
|
+
- [x] 加密功能完整(签名、验签、ECDH、公钥恢复)
|
|
189
|
+
- [x] 浏览器打包产物 UMD 格式正确
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gamerpc",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.3",
|
|
4
4
|
"author": "bookmansoft <ceo@920.cc>",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"bookmansoft <ceo@920.cc>"
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"description": "connector for gamecloud & gamegold",
|
|
21
21
|
"main": "index.js",
|
|
22
22
|
"scripts": {
|
|
23
|
-
"test": "mocha",
|
|
24
|
-
"build": "
|
|
23
|
+
"test": "mocha --recursive test/utils/",
|
|
24
|
+
"build": "npx webpack --mode=production"
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
@@ -32,22 +32,20 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/bookmansoft/gamegoldtoolkit#readme",
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@noble/hashes": "^1.8.0",
|
|
36
|
+
"@noble/secp256k1": "^1.7.2",
|
|
35
37
|
"create-hmac": "1.1.7",
|
|
36
|
-
"
|
|
38
|
+
"hash.js": "^1.1.7",
|
|
37
39
|
"node-fetch": "^2.6.1",
|
|
38
40
|
"socket.io-client": "^4.7.2"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"babel-preset-stage-2": "^6.24.1",
|
|
49
|
-
"cross-env": "^5.2.1",
|
|
50
|
-
"webpack": "^4.46.0",
|
|
51
|
-
"webpack-cli": "^3.3.2"
|
|
43
|
+
"assert": "^2.1.0",
|
|
44
|
+
"buffer": "^6.0.3",
|
|
45
|
+
"crypto-browserify": "^3.12.1",
|
|
46
|
+
"stream-browserify": "^3.0.0",
|
|
47
|
+
"util": "^0.12.5",
|
|
48
|
+
"webpack": "^5.88.0",
|
|
49
|
+
"webpack-cli": "^5.1.4"
|
|
52
50
|
}
|
|
53
51
|
}
|
package/src/utils/secp256k1.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* secp256k1-
|
|
2
|
+
* secp256k1-noble.js - wrapper for @noble/secp256k1
|
|
3
3
|
* Copyright (c) 2018-2020, Bookman Software (MIT License).
|
|
4
4
|
* https://gitee.com/bookmanSoftware/ggserver
|
|
5
|
+
*
|
|
6
|
+
* Replaced elliptic with @noble/secp256k1 for security (GHSA-848j-6mx2-7j84).
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
'use strict';
|
|
8
10
|
|
|
9
11
|
const assert = require('assert');
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const secp = require('@noble/secp256k1');
|
|
13
|
+
const { hmac } = require('@noble/hashes/hmac');
|
|
14
|
+
const { sha256 } = require('@noble/hashes/sha256');
|
|
15
|
+
|
|
16
|
+
// Setup sync hmac for deterministic signatures (RFC 6979)
|
|
17
|
+
secp.utils.hmacSha256Sync = (key, ...msgs) =>
|
|
18
|
+
hmac(sha256, key, secp.utils.concatBytes(...msgs));
|
|
19
|
+
|
|
20
|
+
const { CURVE, Point, Signature } = secp;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Helpers: convert between Buffer and Uint8Array
|
|
24
|
+
*/
|
|
25
|
+
const toU8 = (buf) => new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
26
|
+
const toBuf = (u8) => Buffer.from(u8.buffer, u8.byteOffset, u8.byteLength);
|
|
27
|
+
const toHex = (buf) => Buffer.isBuffer(buf) ? buf.toString('hex') : Buffer.from(buf).toString('hex');
|
|
15
28
|
|
|
16
29
|
/**
|
|
17
|
-
* @exports crypto/secp256k1-
|
|
30
|
+
* @exports crypto/secp256k1-noble
|
|
18
31
|
* @ignore
|
|
19
32
|
*/
|
|
20
33
|
|
|
@@ -24,17 +37,15 @@ const ec = exports;
|
|
|
24
37
|
* Whether we're using native bindings.
|
|
25
38
|
* @const {Boolean}
|
|
26
39
|
*/
|
|
27
|
-
|
|
28
40
|
ec.binding = false;
|
|
29
41
|
|
|
30
42
|
/**
|
|
31
43
|
* Generate a private key.
|
|
32
44
|
* @returns {Buffer} Private key.
|
|
33
45
|
*/
|
|
34
|
-
|
|
35
46
|
ec.generatePrivateKey = function generatePrivateKey() {
|
|
36
|
-
const key =
|
|
37
|
-
return key
|
|
47
|
+
const key = secp.utils.randomPrivateKey();
|
|
48
|
+
return toBuf(key);
|
|
38
49
|
};
|
|
39
50
|
|
|
40
51
|
/**
|
|
@@ -43,31 +54,26 @@ ec.generatePrivateKey = function generatePrivateKey() {
|
|
|
43
54
|
* @param {Boolean?} compress
|
|
44
55
|
* @returns {Buffer}
|
|
45
56
|
*/
|
|
46
|
-
|
|
47
57
|
ec.publicKeyCreate = function publicKeyCreate(priv, compress) {
|
|
48
58
|
if (compress == null)
|
|
49
59
|
compress = true;
|
|
50
60
|
|
|
51
61
|
assert(Buffer.isBuffer(priv));
|
|
52
|
-
|
|
53
|
-
const key = secp256k1.keyPair({ priv: priv });
|
|
54
|
-
|
|
55
|
-
return Buffer.from(key.getPublic(compress, 'array'));
|
|
62
|
+
return toBuf(secp.getPublicKey(toU8(priv), compress));
|
|
56
63
|
};
|
|
57
64
|
|
|
58
65
|
/**
|
|
59
66
|
* Compress or decompress public key.
|
|
60
|
-
* @param {Buffer}
|
|
67
|
+
* @param {Buffer} key
|
|
68
|
+
* @param {Boolean?} compress
|
|
61
69
|
* @returns {Buffer}
|
|
62
70
|
*/
|
|
63
|
-
|
|
64
71
|
ec.publicKeyConvert = function publicKeyConvert(key, compress) {
|
|
65
72
|
if (compress == null)
|
|
66
73
|
compress = true;
|
|
67
74
|
|
|
68
|
-
const point =
|
|
69
|
-
|
|
70
|
-
return Buffer.from(point.encode('array', compress));
|
|
75
|
+
const point = Point.fromHex(toHex(key));
|
|
76
|
+
return Buffer.from(point.toRawBytes(compress));
|
|
71
77
|
};
|
|
72
78
|
|
|
73
79
|
/**
|
|
@@ -76,14 +82,12 @@ ec.publicKeyConvert = function publicKeyConvert(key, compress) {
|
|
|
76
82
|
* @param {Buffer} tweak
|
|
77
83
|
* @returns {Buffer} privateKey
|
|
78
84
|
*/
|
|
79
|
-
|
|
80
85
|
ec.privateKeyTweakAdd = function privateKeyTweakAdd(privateKey, tweak) {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
const n = BigInt('0x' + toHex(privateKey));
|
|
87
|
+
const t = BigInt('0x' + toHex(tweak));
|
|
88
|
+
const result = secp.utils._bigintTo32Bytes(secp.utils.mod(n + t, CURVE.n));
|
|
89
|
+
const key = toBuf(result);
|
|
85
90
|
|
|
86
|
-
// Only a 1 in 2^127 chance of happening.
|
|
87
91
|
if (!ec.privateKeyVerify(key))
|
|
88
92
|
throw new Error('Private key is invalid.');
|
|
89
93
|
|
|
@@ -94,16 +98,17 @@ ec.privateKeyTweakAdd = function privateKeyTweakAdd(privateKey, tweak) {
|
|
|
94
98
|
* ((g * tweak) + key)
|
|
95
99
|
* @param {Buffer} publicKey
|
|
96
100
|
* @param {Buffer} tweak
|
|
101
|
+
* @param {Boolean?} compress
|
|
97
102
|
* @returns {Buffer} publicKey
|
|
98
103
|
*/
|
|
99
|
-
|
|
100
104
|
ec.publicKeyTweakAdd = function publicKeyTweakAdd(publicKey, tweak, compress) {
|
|
101
105
|
if (compress == null)
|
|
102
106
|
compress = true;
|
|
103
107
|
|
|
104
|
-
const
|
|
105
|
-
const point =
|
|
106
|
-
const
|
|
108
|
+
const t = BigInt('0x' + toHex(tweak));
|
|
109
|
+
const point = Point.fromHex(toHex(publicKey));
|
|
110
|
+
const tweakedPoint = point.add(Point.BASE.multiply(t));
|
|
111
|
+
const pub = Buffer.from(tweakedPoint.toRawBytes(compress));
|
|
107
112
|
|
|
108
113
|
if (!ec.publicKeyVerify(pub))
|
|
109
114
|
throw new Error('Public key is invalid.');
|
|
@@ -112,27 +117,26 @@ ec.publicKeyTweakAdd = function publicKeyTweakAdd(publicKey, tweak, compress) {
|
|
|
112
117
|
};
|
|
113
118
|
|
|
114
119
|
/**
|
|
115
|
-
* Create an ecdh.
|
|
120
|
+
* Create an ecdh shared secret (x-coordinate only).
|
|
116
121
|
* @param {Buffer} pub
|
|
117
122
|
* @param {Buffer} priv
|
|
118
|
-
* @returns {Buffer}
|
|
123
|
+
* @returns {Buffer} 32-byte shared secret
|
|
119
124
|
*/
|
|
120
|
-
|
|
121
125
|
ec.ecdh = function ecdh(pub, priv) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
// noble: getSharedSecret(privKey, pubKey) returns full point (65 bytes)
|
|
127
|
+
// extract x-coordinate (32 bytes) to match old elliptic API
|
|
128
|
+
const shared = secp.getSharedSecret(toU8(priv), toU8(pub));
|
|
129
|
+
return Buffer.from(shared.slice(1, 33));
|
|
125
130
|
};
|
|
126
131
|
|
|
127
132
|
/**
|
|
128
|
-
* Recover a public key.
|
|
133
|
+
* Recover a public key from signature.
|
|
129
134
|
* @param {Buffer} msg
|
|
130
135
|
* @param {Buffer} sig
|
|
131
|
-
* @param {Number?} j
|
|
136
|
+
* @param {Number?} j Recovery id (0-3)
|
|
132
137
|
* @param {Boolean?} compress
|
|
133
|
-
* @returns {Buffer
|
|
138
|
+
* @returns {Buffer|null}
|
|
134
139
|
*/
|
|
135
|
-
|
|
136
140
|
ec.recover = function recover(msg, sig, j, compress) {
|
|
137
141
|
if (!j)
|
|
138
142
|
j = 0;
|
|
@@ -140,14 +144,12 @@ ec.recover = function recover(msg, sig, j, compress) {
|
|
|
140
144
|
if (compress == null)
|
|
141
145
|
compress = true;
|
|
142
146
|
|
|
143
|
-
let point;
|
|
144
147
|
try {
|
|
145
|
-
point =
|
|
148
|
+
const point = secp.recoverPublicKey(toU8(msg), toU8(sig), j, compress);
|
|
149
|
+
return toBuf(point);
|
|
146
150
|
} catch (e) {
|
|
147
151
|
return null;
|
|
148
152
|
}
|
|
149
|
-
|
|
150
|
-
return Buffer.from(point.encode('array', compress));
|
|
151
153
|
};
|
|
152
154
|
|
|
153
155
|
/**
|
|
@@ -157,7 +159,6 @@ ec.recover = function recover(msg, sig, j, compress) {
|
|
|
157
159
|
* @param {Buffer} key
|
|
158
160
|
* @returns {Boolean}
|
|
159
161
|
*/
|
|
160
|
-
|
|
161
162
|
ec.verify = function verify(msg, sig, key) {
|
|
162
163
|
assert(Buffer.isBuffer(msg));
|
|
163
164
|
assert(Buffer.isBuffer(sig));
|
|
@@ -169,13 +170,11 @@ ec.verify = function verify(msg, sig, key) {
|
|
|
169
170
|
if (key.length === 0)
|
|
170
171
|
return false;
|
|
171
172
|
|
|
172
|
-
//
|
|
173
|
-
// length before passing to elliptic.
|
|
174
|
-
// https://github.com/indutny/elliptic/issues/78
|
|
173
|
+
// Normalize DER length before passing to noble
|
|
175
174
|
sig = normalizeLength(sig);
|
|
176
175
|
|
|
177
176
|
try {
|
|
178
|
-
return
|
|
177
|
+
return secp.verify(toU8(sig), toU8(msg), toU8(key));
|
|
179
178
|
} catch (e) {
|
|
180
179
|
return false;
|
|
181
180
|
}
|
|
@@ -184,13 +183,11 @@ ec.verify = function verify(msg, sig, key) {
|
|
|
184
183
|
/**
|
|
185
184
|
* Validate a public key.
|
|
186
185
|
* @param {Buffer} key
|
|
187
|
-
* @returns {Boolean}
|
|
186
|
+
* @returns {Boolean}
|
|
188
187
|
*/
|
|
189
|
-
|
|
190
188
|
ec.publicKeyVerify = function publicKeyVerify(key) {
|
|
191
189
|
try {
|
|
192
|
-
|
|
193
|
-
return pub.validate();
|
|
190
|
+
return Point.fromHex(toHex(key)) instanceof Point;
|
|
194
191
|
} catch (e) {
|
|
195
192
|
return false;
|
|
196
193
|
}
|
|
@@ -199,94 +196,75 @@ ec.publicKeyVerify = function publicKeyVerify(key) {
|
|
|
199
196
|
/**
|
|
200
197
|
* Validate a private key.
|
|
201
198
|
* @param {Buffer} key
|
|
202
|
-
* @returns {Boolean}
|
|
199
|
+
* @returns {Boolean}
|
|
203
200
|
*/
|
|
204
|
-
|
|
205
201
|
ec.privateKeyVerify = function privateKeyVerify(key) {
|
|
206
202
|
if (key.length !== 32)
|
|
207
203
|
return false;
|
|
208
204
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
205
|
+
try {
|
|
206
|
+
return secp.utils.isValidPrivateKey(toU8(key));
|
|
207
|
+
} catch (e) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
212
210
|
};
|
|
213
211
|
|
|
214
212
|
/**
|
|
215
|
-
* Sign a message.
|
|
213
|
+
* Sign a message (returns DER format).
|
|
216
214
|
* @param {Buffer} msg
|
|
217
215
|
* @param {Buffer} key - Private key.
|
|
218
216
|
* @returns {Buffer} DER-formatted signature.
|
|
219
217
|
*/
|
|
220
|
-
|
|
221
218
|
ec.sign = function sign(msg, key) {
|
|
222
219
|
assert(Buffer.isBuffer(msg));
|
|
223
220
|
assert(Buffer.isBuffer(key));
|
|
224
221
|
|
|
225
|
-
//
|
|
226
|
-
const sig =
|
|
227
|
-
|
|
228
|
-
// Convert to DER
|
|
229
|
-
return Buffer.from(sig.toDER());
|
|
222
|
+
// noble v1 signSync returns DER with low-S by default
|
|
223
|
+
const sig = secp.signSync(toU8(msg), toU8(key));
|
|
224
|
+
return toBuf(sig);
|
|
230
225
|
};
|
|
231
226
|
|
|
232
227
|
/**
|
|
233
|
-
* Convert DER signature to R
|
|
234
|
-
* @param {Buffer} raw
|
|
235
|
-
* @returns {Buffer}
|
|
228
|
+
* Convert DER signature to compact R||S (64 bytes).
|
|
229
|
+
* @param {Buffer} raw DER-formatted signature
|
|
230
|
+
* @returns {Buffer} 64-byte compact signature
|
|
236
231
|
*/
|
|
237
|
-
|
|
238
232
|
ec.fromDER = function fromDER(raw) {
|
|
239
233
|
assert(Buffer.isBuffer(raw));
|
|
240
234
|
|
|
241
|
-
const sig =
|
|
242
|
-
const out = Buffer.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
sig.s.toArrayLike(Buffer, 'be', 32).copy(out, 32);
|
|
246
|
-
|
|
235
|
+
const sig = Signature.fromDER(toHex(raw));
|
|
236
|
+
const out = Buffer.alloc(64);
|
|
237
|
+
const compact = sig.toCompactRawBytes();
|
|
238
|
+
out.set(compact);
|
|
247
239
|
return out;
|
|
248
240
|
};
|
|
249
241
|
|
|
250
242
|
/**
|
|
251
|
-
* Convert R
|
|
252
|
-
* @param {Buffer}
|
|
253
|
-
* @returns {Buffer} DER-formatted signature
|
|
243
|
+
* Convert compact R||S signature to DER.
|
|
244
|
+
* @param {Buffer} raw 64-byte compact signature
|
|
245
|
+
* @returns {Buffer} DER-formatted signature
|
|
254
246
|
*/
|
|
255
|
-
|
|
256
247
|
ec.toDER = function toDER(raw) {
|
|
257
248
|
assert(Buffer.isBuffer(raw));
|
|
258
249
|
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
return Buffer.from(sig.toDER());
|
|
250
|
+
const r = BigInt('0x' + toHex(raw.slice(0, 32)));
|
|
251
|
+
const s = BigInt('0x' + toHex(raw.slice(32, 64)));
|
|
252
|
+
const sig = new Signature(r, s);
|
|
253
|
+
return Buffer.from(sig.toDERRawBytes());
|
|
265
254
|
};
|
|
266
255
|
|
|
267
256
|
/**
|
|
268
|
-
* Test whether a signature has a low S value.
|
|
269
|
-
* @param {Buffer}
|
|
257
|
+
* Test whether a DER signature has a low S value.
|
|
258
|
+
* @param {Buffer} raw DER-formatted signature
|
|
270
259
|
* @returns {Boolean}
|
|
271
260
|
*/
|
|
272
|
-
|
|
273
261
|
ec.isLowS = function isLowS(raw) {
|
|
274
|
-
let sig;
|
|
275
262
|
try {
|
|
276
|
-
sig =
|
|
263
|
+
const sig = Signature.fromDER(toHex(raw));
|
|
264
|
+
return !sig.hasHighS();
|
|
277
265
|
} catch (e) {
|
|
278
266
|
return false;
|
|
279
267
|
}
|
|
280
|
-
|
|
281
|
-
if (sig.s.isZero())
|
|
282
|
-
return false;
|
|
283
|
-
|
|
284
|
-
// If S is greater than half the order,
|
|
285
|
-
// it's too high.
|
|
286
|
-
if (sig.s.gt(secp256k1.nh))
|
|
287
|
-
return false;
|
|
288
|
-
|
|
289
|
-
return true;
|
|
290
268
|
};
|
|
291
269
|
|
|
292
270
|
/*
|