@hongxian/crypto 0.0.0-stage → 0.1.0
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/LICENSE +201 -0
- package/README.md +40 -3
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/kdf.d.ts +23 -0
- package/dist/kdf.d.ts.map +1 -0
- package/dist/kdf.js +34 -0
- package/dist/kdf.js.map +1 -0
- package/dist/keystore.d.ts +81 -0
- package/dist/keystore.d.ts.map +1 -0
- package/dist/keystore.js +174 -0
- package/dist/keystore.js.map +1 -0
- package/dist/mnemonic.d.ts +17 -0
- package/dist/mnemonic.d.ts.map +1 -0
- package/dist/mnemonic.js +42 -0
- package/dist/mnemonic.js.map +1 -0
- package/dist/primitives.d.ts +47 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +120 -0
- package/dist/primitives.js.map +1 -0
- package/package.json +47 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or Derivative
|
|
95
|
+
Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 The Hongxian Project Contributors
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @hongxian/crypto
|
|
2
|
+
|
|
3
|
+
红线密钥体系。**零网络、零 IO、无随机源之外的副作用**——所有函数都是纯计算。
|
|
4
|
+
|
|
5
|
+
## 密钥链路
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
24 词恢复短语 (BIP39)
|
|
9
|
+
│ Argon2id(内存硬,抗 GPU 爆破)
|
|
10
|
+
▼
|
|
11
|
+
主种子
|
|
12
|
+
├── Ed25519 身份密钥 签名:证明「这张卡是我发的」
|
|
13
|
+
├── X25519 协商密钥 封装:把对称密钥交给对方
|
|
14
|
+
└── HKDF 派生的层密钥 L1 / L2 / L3,各自独立
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
L3 是 sealed container,每个字段用 `HKDF(L3层密钥, "l3/<字段名>")` 单独派生一把子密钥。
|
|
18
|
+
所以「只给微信号、不给手机号」在密码学上成立,不是靠自觉。
|
|
19
|
+
|
|
20
|
+
## 用法
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { mnemonicToKeys, signCard, verifyCard, sealLayer, openLayer } from '@hongxian/crypto';
|
|
24
|
+
|
|
25
|
+
const keys = await mnemonicToKeys(mnemonic);
|
|
26
|
+
const sealed = sealLayer(keys.layers.L2, JSON.stringify(payload));
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 命令
|
|
30
|
+
|
|
31
|
+
- **密钥即身份**:没有邮箱、没有手机号、没有密码。恢复短语是唯一备份,丢了就没了,没有找回通道。
|
|
32
|
+
- 恢复短语只在本机生成与使用,永不外发。
|
|
33
|
+
- 封装用 AES-256-GCM;签名用 Ed25519;协商用 X25519。底层是 audited 的 noble / hash-wasm,不含自研密码学。
|
|
34
|
+
|
|
35
|
+
## 许可
|
|
36
|
+
|
|
37
|
+
Apache-2.0。
|
|
38
|
+
|
|
39
|
+
- 源码:https://github.com/hongxian-dev/hongxian/tree/main/packages/crypto
|
|
40
|
+
- 理论说明:https://github.com/hongxian-dev/hongxian/blob/main/docs/protocol-v1-spec.md
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hongxian/crypto —— 红线密钥体系
|
|
3
|
+
*
|
|
4
|
+
* 恢复短语(BIP39 中文 24 词)→ Argon2id → 三种子密钥;
|
|
5
|
+
* Ed25519 签名 / X25519 sealed box / AES-256-GCM;私钥永不落明文。
|
|
6
|
+
*/
|
|
7
|
+
export * from './mnemonic.js';
|
|
8
|
+
export * from './kdf.js';
|
|
9
|
+
export * from './primitives.js';
|
|
10
|
+
export * from './keystore.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hongxian/crypto —— 红线密钥体系
|
|
3
|
+
*
|
|
4
|
+
* 恢复短语(BIP39 中文 24 词)→ Argon2id → 三种子密钥;
|
|
5
|
+
* Ed25519 签名 / X25519 sealed box / AES-256-GCM;私钥永不落明文。
|
|
6
|
+
*/
|
|
7
|
+
export * from './mnemonic.js';
|
|
8
|
+
export * from './kdf.js';
|
|
9
|
+
export * from './primitives.js';
|
|
10
|
+
export * from './keystore.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
|
package/dist/kdf.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 第二层:Argon2id 主密钥派生(技术详设 §4 / 执行设计 E5)
|
|
3
|
+
*
|
|
4
|
+
* 恢复短语 → Argon2id → MK。每个密钥文件独立盐(备份/恢复单文件可行)。
|
|
5
|
+
* MK 仅存内存,进程退出即清零(zeroize)。
|
|
6
|
+
*/
|
|
7
|
+
/** Argon2id 参数(t=3, m=64MB, p=4)——OWASP 2023 推荐档,哈希前向兼容 */
|
|
8
|
+
export interface Argon2Params {
|
|
9
|
+
/** 时间成本(迭代次数) */
|
|
10
|
+
iterations: number;
|
|
11
|
+
/** 内存成本(KiB) */
|
|
12
|
+
memorySize: number;
|
|
13
|
+
/** 并行度 */
|
|
14
|
+
parallelism: number;
|
|
15
|
+
/** 输出长度(字节) */
|
|
16
|
+
hashLength: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const ARGON2_PARAMS: Argon2Params;
|
|
19
|
+
/** 从密码(BIP39 seed)与盐派生 32 字节密钥 */
|
|
20
|
+
export declare function deriveKey(password: Uint8Array, salt: Uint8Array, params?: Argon2Params): Promise<Uint8Array>;
|
|
21
|
+
/** 显式清零(MK 生命周期管理:lock / 进程退出) */
|
|
22
|
+
export declare function zeroize(...bufs: (Uint8Array | undefined)[]): void;
|
|
23
|
+
//# sourceMappingURL=kdf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kdf.d.ts","sourceRoot":"","sources":["../src/kdf.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,iBAAiB;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU;IACV,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,EAAE,YAK3B,CAAC;AAEF,kCAAkC;AAClC,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,UAAU,EAChB,MAAM,GAAE,YAA4B,GACnC,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED,kCAAkC;AAClC,wBAAgB,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,CAIjE"}
|
package/dist/kdf.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 第二层:Argon2id 主密钥派生(技术详设 §4 / 执行设计 E5)
|
|
3
|
+
*
|
|
4
|
+
* 恢复短语 → Argon2id → MK。每个密钥文件独立盐(备份/恢复单文件可行)。
|
|
5
|
+
* MK 仅存内存,进程退出即清零(zeroize)。
|
|
6
|
+
*/
|
|
7
|
+
import { argon2id } from 'hash-wasm';
|
|
8
|
+
export const ARGON2_PARAMS = {
|
|
9
|
+
iterations: 3,
|
|
10
|
+
memorySize: 65536, // KiB = 64 MB
|
|
11
|
+
parallelism: 4,
|
|
12
|
+
hashLength: 32,
|
|
13
|
+
};
|
|
14
|
+
/** 从密码(BIP39 seed)与盐派生 32 字节密钥 */
|
|
15
|
+
export async function deriveKey(password, salt, params = ARGON2_PARAMS) {
|
|
16
|
+
const key = await argon2id({
|
|
17
|
+
password,
|
|
18
|
+
salt,
|
|
19
|
+
parallelism: params.parallelism,
|
|
20
|
+
iterations: params.iterations,
|
|
21
|
+
memorySize: params.memorySize,
|
|
22
|
+
hashLength: params.hashLength,
|
|
23
|
+
outputType: 'binary',
|
|
24
|
+
});
|
|
25
|
+
return new Uint8Array(key);
|
|
26
|
+
}
|
|
27
|
+
/** 显式清零(MK 生命周期管理:lock / 进程退出) */
|
|
28
|
+
export function zeroize(...bufs) {
|
|
29
|
+
for (const buf of bufs) {
|
|
30
|
+
if (buf)
|
|
31
|
+
buf.fill(0);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=kdf.js.map
|
package/dist/kdf.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kdf.js","sourceRoot":"","sources":["../src/kdf.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAcrC,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,KAAK,EAAE,cAAc;IACjC,WAAW,EAAE,CAAC;IACd,UAAU,EAAE,EAAE;CACf,CAAC;AAEF,kCAAkC;AAClC,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAoB,EACpB,IAAgB,EAChB,SAAuB,aAAa;IAEpC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC;QACzB,QAAQ;QACR,IAAI;QACJ,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,QAAQ;KACrB,CAAC,CAAC;IACH,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,OAAO,CAAC,GAAG,IAAgC;IACzD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 第三层:加密密钥文件与 KeyMaterial 管理
|
|
3
|
+
*
|
|
4
|
+
* 布局(技术详设 §4/§5):
|
|
5
|
+
* ~/.hongxian/keys/id.enc 身份密钥对(Ed25519)
|
|
6
|
+
* ~/.hongxian/keys/kx.enc 协商密钥对(X25519)
|
|
7
|
+
* ~/.hongxian/keys/lk.enc 层密钥 key_L1/L2/L3
|
|
8
|
+
* 每文件独立盐 + Argon2id → AES-256-GCM。私钥永不落明文;
|
|
9
|
+
* 解锁会话内 MK 仅存内存,lock/退出即 zeroize。
|
|
10
|
+
*/
|
|
11
|
+
import { type Argon2Params } from './kdf.js';
|
|
12
|
+
/** 加密密钥文件格式(v1) */
|
|
13
|
+
export declare const EncryptedKeyFile: {
|
|
14
|
+
readonly version: 1;
|
|
15
|
+
readonly kdf: "argon2id";
|
|
16
|
+
readonly aead: "aes-256-gcm";
|
|
17
|
+
};
|
|
18
|
+
export interface SealedKeyFile {
|
|
19
|
+
v: 1;
|
|
20
|
+
kdf: 'argon2id';
|
|
21
|
+
params: {
|
|
22
|
+
t: number;
|
|
23
|
+
m: number;
|
|
24
|
+
p: number;
|
|
25
|
+
};
|
|
26
|
+
salt: string;
|
|
27
|
+
nonce: string;
|
|
28
|
+
ct: string;
|
|
29
|
+
}
|
|
30
|
+
/** 内存中的完整密钥材料(进程级,退出即消失) */
|
|
31
|
+
export interface KeyMaterial {
|
|
32
|
+
identityPrivate: Uint8Array;
|
|
33
|
+
identityPublic: Uint8Array;
|
|
34
|
+
kxPrivate: Uint8Array;
|
|
35
|
+
kxPublic: Uint8Array;
|
|
36
|
+
layerKeys: {
|
|
37
|
+
L1: Uint8Array;
|
|
38
|
+
L2: Uint8Array;
|
|
39
|
+
L3: Uint8Array;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export declare function generateKeyMaterial(): KeyMaterial;
|
|
43
|
+
/**
|
|
44
|
+
* 协议固定盐(Argon2id 派生 MK 用)——公开常量。
|
|
45
|
+
* seed 本身是 256-bit BIP39 输出(高熵),固定盐无安全损失
|
|
46
|
+
* (盐的使命是防彩虹表与并行化,对单用户单口令场景不适用)。
|
|
47
|
+
*/
|
|
48
|
+
export declare const PROTOCOL_SALT: NodeJS.NonSharedUint8Array;
|
|
49
|
+
/**
|
|
50
|
+
* 从恢复短语 seed 确定性派生 KeyMaterial("恢复短语即身份"的工程实现)。
|
|
51
|
+
*
|
|
52
|
+
* 同短语 → 同 MK → 同 identity/kx/layer 密钥 → 同 owner_key_id:
|
|
53
|
+
* 换设备输入旧短语即重建同一身份,旧卡片签名可验、旧 grant 信封可解、
|
|
54
|
+
* 姻缘线关系不断(D15③ 的成立前提)。MK 仅存内存。
|
|
55
|
+
*/
|
|
56
|
+
export declare function deriveKeyMaterial(seed: Uint8Array, params?: Argon2Params): Promise<KeyMaterial>;
|
|
57
|
+
/** owner_key_id(32 字节指纹) */
|
|
58
|
+
export declare function keyMaterialId(km: KeyMaterial): Uint8Array;
|
|
59
|
+
/** 锁定:清零全部敏感字节 */
|
|
60
|
+
export declare function lockKeyMaterial(km: KeyMaterial): void;
|
|
61
|
+
/**
|
|
62
|
+
* 用恢复短语 seed 加密一份明文 → SealedKeyFile。
|
|
63
|
+
* 盐独立随机(每文件不同盐:备份/恢复单文件可行,且盐无需保密)。
|
|
64
|
+
* 参数随文件记录(哈希前向兼容:未来调参不影响旧文件解密)。
|
|
65
|
+
*/
|
|
66
|
+
export declare function sealKeyFile(plaintext: Uint8Array, password: Uint8Array, params?: Argon2Params): Promise<SealedKeyFile>;
|
|
67
|
+
/** 解密 SealedKeyFile;密码错误/文件被篡改返回 null。参数取文件自带记录 */
|
|
68
|
+
export declare function openKeyFile(file: SealedKeyFile, password: Uint8Array): Promise<Uint8Array | null>;
|
|
69
|
+
/** 从恢复短语 seed 创建三份加密文件 */
|
|
70
|
+
export declare function createKeyFiles(password: Uint8Array, km: KeyMaterial, params?: Argon2Params): Promise<{
|
|
71
|
+
id: SealedKeyFile;
|
|
72
|
+
kx: SealedKeyFile;
|
|
73
|
+
lk: SealedKeyFile;
|
|
74
|
+
}>;
|
|
75
|
+
/** 从三份加密文件 + 恢复短语 seed 恢复 KeyMaterial(换设备场景,D15③) */
|
|
76
|
+
export declare function restoreKeyFiles(files: {
|
|
77
|
+
id: SealedKeyFile;
|
|
78
|
+
kx: SealedKeyFile;
|
|
79
|
+
lk: SealedKeyFile;
|
|
80
|
+
}, password: Uint8Array): Promise<KeyMaterial | null>;
|
|
81
|
+
//# sourceMappingURL=keystore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keystore.d.ts","sourceRoot":"","sources":["../src/keystore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAiB,KAAK,YAAY,EAAsB,MAAM,UAAU,CAAC;AAchF,mBAAmB;AACnB,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,CAAC,CAAC;IACL,GAAG,EAAE,UAAU,CAAC;IAChB,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,UAAU,CAAC;IAC5B,cAAc,EAAE,UAAU,CAAC;IAC3B,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,UAAU,CAAC;IACrB,SAAS,EAAE;QAAE,EAAE,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,UAAU,CAAA;KAAE,CAAC;CAC/D;AAED,wBAAgB,mBAAmB,IAAI,WAAW,CAUjD;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,4BAA8D,CAAC;AAWzF;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAgBrG;AAED,4BAA4B;AAC5B,wBAAgB,aAAa,CAAC,EAAE,EAAE,WAAW,GAAG,UAAU,CAEzD;AAED,kBAAkB;AAClB,wBAAgB,eAAe,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,CAErD;AA0BD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,UAAU,EACrB,QAAQ,EAAE,UAAU,EACpB,MAAM,GAAE,YAA4B,GACnC,OAAO,CAAC,aAAa,CAAC,CAgBxB;AAED,mDAAmD;AACnD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,UAAU,GACnB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAc5B;AAID,0BAA0B;AAC1B,wBAAsB,cAAc,CAClC,QAAQ,EAAE,UAAU,EACpB,EAAE,EAAE,WAAW,EACf,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,EAAE,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,aAAa,CAAA;CAAE,CAAC,CAOtE;AAED,qDAAqD;AACrD,wBAAsB,eAAe,CACnC,KAAK,EAAE;IAAE,EAAE,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,aAAa,CAAC;IAAC,EAAE,EAAE,aAAa,CAAA;CAAE,EAClE,QAAQ,EAAE,UAAU,GACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAoB7B"}
|
package/dist/keystore.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 第三层:加密密钥文件与 KeyMaterial 管理
|
|
3
|
+
*
|
|
4
|
+
* 布局(技术详设 §4/§5):
|
|
5
|
+
* ~/.hongxian/keys/id.enc 身份密钥对(Ed25519)
|
|
6
|
+
* ~/.hongxian/keys/kx.enc 协商密钥对(X25519)
|
|
7
|
+
* ~/.hongxian/keys/lk.enc 层密钥 key_L1/L2/L3
|
|
8
|
+
* 每文件独立盐 + Argon2id → AES-256-GCM。私钥永不落明文;
|
|
9
|
+
* 解锁会话内 MK 仅存内存,lock/退出即 zeroize。
|
|
10
|
+
*/
|
|
11
|
+
import { ARGON2_PARAMS, deriveKey, zeroize } from './kdf.js';
|
|
12
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
13
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
14
|
+
import { aesGcmDecrypt, aesGcmEncrypt, generateIdentityKeyPair, generateKexKeyPair, getKexPublicKey, identityPublicKeyFromSeed, keyIdFromPublicKey, randomKey, } from './primitives.js';
|
|
15
|
+
/** 加密密钥文件格式(v1) */
|
|
16
|
+
export const EncryptedKeyFile = {
|
|
17
|
+
version: 1,
|
|
18
|
+
kdf: 'argon2id',
|
|
19
|
+
aead: 'aes-256-gcm',
|
|
20
|
+
};
|
|
21
|
+
export function generateKeyMaterial() {
|
|
22
|
+
const id = generateIdentityKeyPair();
|
|
23
|
+
const kx = generateKexKeyPair();
|
|
24
|
+
return {
|
|
25
|
+
identityPrivate: id.privateKey,
|
|
26
|
+
identityPublic: id.publicKey,
|
|
27
|
+
kxPrivate: kx.privateKey,
|
|
28
|
+
kxPublic: kx.publicKey,
|
|
29
|
+
layerKeys: { L1: randomKey(32), L2: randomKey(32), L3: randomKey(32) },
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 协议固定盐(Argon2id 派生 MK 用)——公开常量。
|
|
34
|
+
* seed 本身是 256-bit BIP39 输出(高熵),固定盐无安全损失
|
|
35
|
+
* (盐的使命是防彩虹表与并行化,对单用户单口令场景不适用)。
|
|
36
|
+
*/
|
|
37
|
+
export const PROTOCOL_SALT = new TextEncoder().encode('hongxian/protocol-v0/mk-salt/v1');
|
|
38
|
+
/** HKDF 域分离 info(各子密钥独立派生路径) */
|
|
39
|
+
const INFO = {
|
|
40
|
+
identity: new TextEncoder().encode('hongxian/protocol-v0/identity-seed/v1'),
|
|
41
|
+
kx: new TextEncoder().encode('hongxian/protocol-v0/keyexchange-seed/v1'),
|
|
42
|
+
L1: new TextEncoder().encode('hongxian/protocol-v0/layer-key/L1/v1'),
|
|
43
|
+
L2: new TextEncoder().encode('hongxian/protocol-v0/layer-key/L2/v1'),
|
|
44
|
+
L3: new TextEncoder().encode('hongxian/protocol-v0/layer-key/L3/v1'),
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* 从恢复短语 seed 确定性派生 KeyMaterial("恢复短语即身份"的工程实现)。
|
|
48
|
+
*
|
|
49
|
+
* 同短语 → 同 MK → 同 identity/kx/layer 密钥 → 同 owner_key_id:
|
|
50
|
+
* 换设备输入旧短语即重建同一身份,旧卡片签名可验、旧 grant 信封可解、
|
|
51
|
+
* 姻缘线关系不断(D15③ 的成立前提)。MK 仅存内存。
|
|
52
|
+
*/
|
|
53
|
+
export async function deriveKeyMaterial(seed, params) {
|
|
54
|
+
const mk = await deriveKey(seed, PROTOCOL_SALT, params ?? ARGON2_PARAMS);
|
|
55
|
+
try {
|
|
56
|
+
const sub = (info) => hkdf(sha256, mk, undefined, info, 32);
|
|
57
|
+
const identityPrivate = sub(INFO.identity);
|
|
58
|
+
const kxPrivate = sub(INFO.kx);
|
|
59
|
+
return {
|
|
60
|
+
identityPrivate,
|
|
61
|
+
identityPublic: identityPublicKeyFromSeed(identityPrivate),
|
|
62
|
+
kxPrivate,
|
|
63
|
+
kxPublic: getKexPublicKey(kxPrivate),
|
|
64
|
+
layerKeys: { L1: sub(INFO.L1), L2: sub(INFO.L2), L3: sub(INFO.L3) },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
zeroize(mk);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** owner_key_id(32 字节指纹) */
|
|
72
|
+
export function keyMaterialId(km) {
|
|
73
|
+
return keyIdFromPublicKey(km.identityPublic);
|
|
74
|
+
}
|
|
75
|
+
/** 锁定:清零全部敏感字节 */
|
|
76
|
+
export function lockKeyMaterial(km) {
|
|
77
|
+
zeroize(km.identityPrivate, km.kxPrivate, km.layerKeys.L1, km.layerKeys.L2, km.layerKeys.L3);
|
|
78
|
+
}
|
|
79
|
+
// ---------- 文件序列化 ----------
|
|
80
|
+
function toB64(bytes) {
|
|
81
|
+
return Buffer.from(bytes).toString('base64');
|
|
82
|
+
}
|
|
83
|
+
function fromB64(s) {
|
|
84
|
+
return new Uint8Array(Buffer.from(s, 'base64'));
|
|
85
|
+
}
|
|
86
|
+
/** 层密钥明文结构(lk.enc 的内容) */
|
|
87
|
+
function serializeLayerKeys(km) {
|
|
88
|
+
const json = JSON.stringify({
|
|
89
|
+
L1: toB64(km.layerKeys.L1),
|
|
90
|
+
L2: toB64(km.layerKeys.L2),
|
|
91
|
+
L3: toB64(km.layerKeys.L3),
|
|
92
|
+
});
|
|
93
|
+
return new TextEncoder().encode(json);
|
|
94
|
+
}
|
|
95
|
+
function parseLayerKeys(bytes) {
|
|
96
|
+
const obj = JSON.parse(new TextDecoder().decode(bytes));
|
|
97
|
+
return { L1: fromB64(obj.L1), L2: fromB64(obj.L2), L3: fromB64(obj.L3) };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 用恢复短语 seed 加密一份明文 → SealedKeyFile。
|
|
101
|
+
* 盐独立随机(每文件不同盐:备份/恢复单文件可行,且盐无需保密)。
|
|
102
|
+
* 参数随文件记录(哈希前向兼容:未来调参不影响旧文件解密)。
|
|
103
|
+
*/
|
|
104
|
+
export async function sealKeyFile(plaintext, password, params = ARGON2_PARAMS) {
|
|
105
|
+
const salt = randomKey(16);
|
|
106
|
+
const fileKey = await deriveKey(password, salt, params);
|
|
107
|
+
try {
|
|
108
|
+
const { nonce, ct } = aesGcmEncrypt(fileKey, plaintext);
|
|
109
|
+
return {
|
|
110
|
+
v: EncryptedKeyFile.version,
|
|
111
|
+
kdf: EncryptedKeyFile.kdf,
|
|
112
|
+
params: { t: params.iterations, m: params.memorySize, p: params.parallelism },
|
|
113
|
+
salt: toB64(salt),
|
|
114
|
+
nonce: toB64(nonce),
|
|
115
|
+
ct: toB64(ct),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
zeroize(fileKey);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** 解密 SealedKeyFile;密码错误/文件被篡改返回 null。参数取文件自带记录 */
|
|
123
|
+
export async function openKeyFile(file, password) {
|
|
124
|
+
if (file.v !== EncryptedKeyFile.version)
|
|
125
|
+
return null;
|
|
126
|
+
const salt = fromB64(file.salt);
|
|
127
|
+
const fileKey = await deriveKey(password, salt, {
|
|
128
|
+
iterations: file.params.t,
|
|
129
|
+
memorySize: file.params.m,
|
|
130
|
+
parallelism: file.params.p,
|
|
131
|
+
hashLength: 32,
|
|
132
|
+
});
|
|
133
|
+
try {
|
|
134
|
+
return aesGcmDecrypt(fileKey, fromB64(file.nonce), fromB64(file.ct));
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
zeroize(fileKey);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ---------- 三份密钥文件的高层存取 ----------
|
|
141
|
+
/** 从恢复短语 seed 创建三份加密文件 */
|
|
142
|
+
export async function createKeyFiles(password, km, params) {
|
|
143
|
+
const [id, kx, lk] = await Promise.all([
|
|
144
|
+
sealKeyFile(km.identityPrivate, password, params),
|
|
145
|
+
sealKeyFile(km.kxPrivate, password, params),
|
|
146
|
+
sealKeyFile(serializeLayerKeys(km), password, params),
|
|
147
|
+
]);
|
|
148
|
+
return { id, kx, lk };
|
|
149
|
+
}
|
|
150
|
+
/** 从三份加密文件 + 恢复短语 seed 恢复 KeyMaterial(换设备场景,D15③) */
|
|
151
|
+
export async function restoreKeyFiles(files, password) {
|
|
152
|
+
const [idBytes, kxBytes, lkBytes] = await Promise.all([
|
|
153
|
+
openKeyFile(files.id, password),
|
|
154
|
+
openKeyFile(files.kx, password),
|
|
155
|
+
openKeyFile(files.lk, password),
|
|
156
|
+
]);
|
|
157
|
+
if (!idBytes || !kxBytes || !lkBytes)
|
|
158
|
+
return null;
|
|
159
|
+
try {
|
|
160
|
+
const layers = parseLayerKeys(lkBytes);
|
|
161
|
+
const identityPrivate = idBytes;
|
|
162
|
+
return {
|
|
163
|
+
identityPrivate,
|
|
164
|
+
identityPublic: identityPublicKeyFromSeed(identityPrivate),
|
|
165
|
+
kxPrivate: kxBytes,
|
|
166
|
+
kxPublic: getKexPublicKey(kxBytes),
|
|
167
|
+
layerKeys: layers,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=keystore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keystore.js","sourceRoot":"","sources":["../src/keystore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 第一层:BIP39 恢复短语(技术详设 §4)
|
|
3
|
+
*
|
|
4
|
+
* 恢复短语 = 24 个中文简体词 = 身份的唯一备份通道(D15③)。
|
|
5
|
+
* 丢失不可恢复(设计如此);密钥文件只是短语的加密容器——容器可复制,短语才是身份。
|
|
6
|
+
*/
|
|
7
|
+
/** 24 词 = 256 bit 熵(协议定稿口径) */
|
|
8
|
+
export declare const MNEMONIC_WORDS = 24;
|
|
9
|
+
/** 生成 24 词中文简体恢复短语 */
|
|
10
|
+
export declare function generateMnemonicZh(): string;
|
|
11
|
+
/** 校验中文恢复短语(词表 + 校验和) */
|
|
12
|
+
export declare function validateMnemonicZh(phrase: string): boolean;
|
|
13
|
+
/** 归一化用户输入(全角空格/多余空白 → 单空格分隔) */
|
|
14
|
+
export declare function normalizeMnemonic(phrase: string): string;
|
|
15
|
+
/** 短语 → 64 字节 BIP39 seed(Argon2id 的密码输入) */
|
|
16
|
+
export declare function mnemonicToSeed(phrase: string): Uint8Array;
|
|
17
|
+
//# sourceMappingURL=mnemonic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mnemonic.d.ts","sourceRoot":"","sources":["../src/mnemonic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,+BAA+B;AAC/B,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,sBAAsB;AACtB,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,yBAAyB;AACzB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAQ1D;AAED,iCAAiC;AACjC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED,4CAA4C;AAC5C,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAKzD"}
|
package/dist/mnemonic.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 第一层:BIP39 恢复短语(技术详设 §4)
|
|
3
|
+
*
|
|
4
|
+
* 恢复短语 = 24 个中文简体词 = 身份的唯一备份通道(D15③)。
|
|
5
|
+
* 丢失不可恢复(设计如此);密钥文件只是短语的加密容器——容器可复制,短语才是身份。
|
|
6
|
+
*/
|
|
7
|
+
import { generateMnemonic as scureGenerate, mnemonicToSeedSync, validateMnemonic as scureValidate } from '@scure/bip39';
|
|
8
|
+
import { wordlist } from '@scure/bip39/wordlists/simplified-chinese';
|
|
9
|
+
/** 24 词 = 256 bit 熵(协议定稿口径) */
|
|
10
|
+
export const MNEMONIC_WORDS = 24;
|
|
11
|
+
/** 生成 24 词中文简体恢复短语 */
|
|
12
|
+
export function generateMnemonicZh() {
|
|
13
|
+
return scureGenerate(wordlist, 256);
|
|
14
|
+
}
|
|
15
|
+
/** 校验中文恢复短语(词表 + 校验和) */
|
|
16
|
+
export function validateMnemonicZh(phrase) {
|
|
17
|
+
const words = phrase.trim().split(/\s+/).filter(Boolean);
|
|
18
|
+
if (words.length !== MNEMONIC_WORDS)
|
|
19
|
+
return false;
|
|
20
|
+
try {
|
|
21
|
+
return scureValidate(phrase.trim(), wordlist);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** 归一化用户输入(全角空格/多余空白 → 单空格分隔) */
|
|
28
|
+
export function normalizeMnemonic(phrase) {
|
|
29
|
+
return phrase
|
|
30
|
+
.replace(/\u3000/g, ' ')
|
|
31
|
+
.trim()
|
|
32
|
+
.split(/\s+/)
|
|
33
|
+
.join(' ');
|
|
34
|
+
}
|
|
35
|
+
/** 短语 → 64 字节 BIP39 seed(Argon2id 的密码输入) */
|
|
36
|
+
export function mnemonicToSeed(phrase) {
|
|
37
|
+
if (!validateMnemonicZh(phrase)) {
|
|
38
|
+
throw new Error('恢复短语无效:必须是 24 个中文词且校验和正确');
|
|
39
|
+
}
|
|
40
|
+
return mnemonicToSeedSync(normalizeMnemonic(phrase));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=mnemonic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mnemonic.js","sourceRoot":"","sources":["../src/mnemonic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,IAAI,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,IAAI,aAAa,EAAE,MAAM,cAAc,CAAC;AACxH,OAAO,EAAE,QAAQ,EAAE,MAAM,2CAA2C,CAAC;AAErE,+BAA+B;AAC/B,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC,sBAAsB;AACtB,MAAM,UAAU,kBAAkB;IAChC,OAAO,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AACtC,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzD,IAAI,KAAK,CAAC,MAAM,KAAK,cAAc;QAAE,OAAO,KAAK,CAAC;IAClD,IAAI,CAAC;QACH,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,MAAM;SACV,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,kBAAkB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 原语:Ed25519 身份签名 / X25519 密钥交换 / AES-256-GCM
|
|
3
|
+
*
|
|
4
|
+
* 纯 JS(@noble),无原生依赖——跨平台安装零摩擦(技术详设 §2 选型第一原则)。
|
|
5
|
+
*/
|
|
6
|
+
export type IdentityKeyPair = {
|
|
7
|
+
/** 32 字节私钥种子(加密存储,永不出设备) */
|
|
8
|
+
privateKey: Uint8Array;
|
|
9
|
+
publicKey: Uint8Array;
|
|
10
|
+
};
|
|
11
|
+
export declare function generateIdentityKeyPair(): IdentityKeyPair;
|
|
12
|
+
export declare function identityPublicKeyFromSeed(seed: Uint8Array): Uint8Array;
|
|
13
|
+
/** 签名(对 canonical bytes 签名,调用方负责序列化规范) */
|
|
14
|
+
export declare function signDetached(message: Uint8Array, privateKey: Uint8Array): Uint8Array;
|
|
15
|
+
export declare function verifyDetached(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean;
|
|
16
|
+
/** owner_key_id = SHA-256(公钥) 的 32 字节指纹(Schema §2,非身份信息) */
|
|
17
|
+
export declare function keyIdFromPublicKey(publicKey: Uint8Array): Uint8Array;
|
|
18
|
+
export type KexKeyPair = {
|
|
19
|
+
privateKey: Uint8Array;
|
|
20
|
+
publicKey: Uint8Array;
|
|
21
|
+
};
|
|
22
|
+
export declare function generateKexKeyPair(): KexKeyPair;
|
|
23
|
+
export declare function getKexPublicKey(privateKey: Uint8Array): Uint8Array;
|
|
24
|
+
/** Sealed box:临时密钥对 + ECDH + AES-GCM。输出 = ephPub(32) || nonce(12) || ct||tag */
|
|
25
|
+
export declare function sealBox(plaintext: Uint8Array, recipientPublicKey: Uint8Array): Uint8Array;
|
|
26
|
+
/** 解开 sealed box;失败(伪造/篡改/非本方)返回 null */
|
|
27
|
+
export declare function openBox(sealed: Uint8Array, recipientPrivateKey: Uint8Array): Uint8Array | null;
|
|
28
|
+
/** 加密。ct = ciphertext || tag(16B);nonce 12 字节随机 */
|
|
29
|
+
export declare function aesGcmEncrypt(key: Uint8Array, plaintext: Uint8Array): {
|
|
30
|
+
nonce: Uint8Array;
|
|
31
|
+
ct: Uint8Array;
|
|
32
|
+
};
|
|
33
|
+
/** 解密;认证失败返回 null(不降级、不部分解密——Schema §7.4) */
|
|
34
|
+
export declare function aesGcmDecrypt(key: Uint8Array, nonce: Uint8Array, ct: Uint8Array): Uint8Array | null;
|
|
35
|
+
/** 随机 n 字节 */
|
|
36
|
+
export declare function randomKey(n: number): Uint8Array;
|
|
37
|
+
/**
|
|
38
|
+
* 由层密钥派生用途子密钥:HKDF-SHA256(master, info = prefix + label)。
|
|
39
|
+
*
|
|
40
|
+
* 见面层用它把单一的 L3 层密钥摊成「每字段一把」——
|
|
41
|
+
* 交付 contact_wechat 的子密钥不等于交付 contact_phone 的子密钥,
|
|
42
|
+
* D13「给微信号但不给手机号」由此在密码学层面成立,而不只是 UI 上的开关。
|
|
43
|
+
*
|
|
44
|
+
* 单向性:子密钥推不出主密钥,也无法推到别的字段(不同 info)。
|
|
45
|
+
*/
|
|
46
|
+
export declare function deriveSubkey(masterKey: Uint8Array, label: string): Uint8Array;
|
|
47
|
+
//# sourceMappingURL=primitives.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,MAAM,eAAe,GAAG;IAC5B,4BAA4B;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC;AAEF,wBAAgB,uBAAuB,IAAI,eAAe,CAIzD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAEtE;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAEpF;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,GACpB,OAAO,CAMT;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,UAAU,GAAG,UAAU,CAEpE;AAID,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC;AAEF,wBAAgB,kBAAkB,IAAI,UAAU,CAI/C;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAElE;AAWD,gFAAgF;AAChF,wBAAgB,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,GAAG,UAAU,CAUzF;AAED,yCAAyC;AACzC,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,mBAAmB,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAW9F;AAID,mDAAmD;AACnD,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAK3G;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAQnG;AAED,cAAc;AACd,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAE/C;AAOD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAI7E"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 红线密钥体系 · 原语:Ed25519 身份签名 / X25519 密钥交换 / AES-256-GCM
|
|
3
|
+
*
|
|
4
|
+
* 纯 JS(@noble),无原生依赖——跨平台安装零摩擦(技术详设 §2 选型第一原则)。
|
|
5
|
+
*/
|
|
6
|
+
import { gcm } from '@noble/ciphers/aes';
|
|
7
|
+
import { ed25519, x25519 } from '@noble/curves/ed25519';
|
|
8
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
9
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
10
|
+
import { randomBytes } from 'node:crypto';
|
|
11
|
+
export function generateIdentityKeyPair() {
|
|
12
|
+
const privateKey = ed25519.utils.randomPrivateKey();
|
|
13
|
+
const publicKey = ed25519.getPublicKey(privateKey);
|
|
14
|
+
return { privateKey, publicKey };
|
|
15
|
+
}
|
|
16
|
+
export function identityPublicKeyFromSeed(seed) {
|
|
17
|
+
return ed25519.getPublicKey(seed);
|
|
18
|
+
}
|
|
19
|
+
/** 签名(对 canonical bytes 签名,调用方负责序列化规范) */
|
|
20
|
+
export function signDetached(message, privateKey) {
|
|
21
|
+
return ed25519.sign(message, privateKey);
|
|
22
|
+
}
|
|
23
|
+
export function verifyDetached(signature, message, publicKey) {
|
|
24
|
+
try {
|
|
25
|
+
return ed25519.verify(signature, message, publicKey);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** owner_key_id = SHA-256(公钥) 的 32 字节指纹(Schema §2,非身份信息) */
|
|
32
|
+
export function keyIdFromPublicKey(publicKey) {
|
|
33
|
+
return sha256(publicKey);
|
|
34
|
+
}
|
|
35
|
+
export function generateKexKeyPair() {
|
|
36
|
+
const privateKey = x25519.utils.randomPrivateKey();
|
|
37
|
+
const publicKey = x25519.getPublicKey(privateKey);
|
|
38
|
+
return { privateKey, publicKey };
|
|
39
|
+
}
|
|
40
|
+
export function getKexPublicKey(privateKey) {
|
|
41
|
+
return x25519.getPublicKey(privateKey);
|
|
42
|
+
}
|
|
43
|
+
/** 协议固定 HKDF info(域分离) */
|
|
44
|
+
const HKDF_INFO = new TextEncoder().encode('hongxian/protocol-v0/sealed-box/v1');
|
|
45
|
+
/** ECDH(ephPriv, recipientPub) → HKDF-SHA256 → AES-256-GCM 密钥 */
|
|
46
|
+
function deriveSharedKey(ephemeralPrivate, recipientPublic) {
|
|
47
|
+
const shared = x25519.getSharedSecret(ephemeralPrivate, recipientPublic);
|
|
48
|
+
return hkdf(sha256, shared, undefined, HKDF_INFO, 32);
|
|
49
|
+
}
|
|
50
|
+
/** Sealed box:临时密钥对 + ECDH + AES-GCM。输出 = ephPub(32) || nonce(12) || ct||tag */
|
|
51
|
+
export function sealBox(plaintext, recipientPublicKey) {
|
|
52
|
+
const eph = generateKexKeyPair();
|
|
53
|
+
const key = deriveSharedKey(eph.privateKey, recipientPublicKey);
|
|
54
|
+
const nonce = randomBytes(12);
|
|
55
|
+
const sealed = gcm(key, nonce).encrypt(plaintext);
|
|
56
|
+
const out = new Uint8Array(32 + 12 + sealed.length);
|
|
57
|
+
out.set(eph.publicKey, 0);
|
|
58
|
+
out.set(nonce, 32);
|
|
59
|
+
out.set(sealed, 44);
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/** 解开 sealed box;失败(伪造/篡改/非本方)返回 null */
|
|
63
|
+
export function openBox(sealed, recipientPrivateKey) {
|
|
64
|
+
if (sealed.length < 44 + 16)
|
|
65
|
+
return null;
|
|
66
|
+
const ephPub = sealed.slice(0, 32);
|
|
67
|
+
const nonce = sealed.slice(32, 44);
|
|
68
|
+
const ct = sealed.slice(44);
|
|
69
|
+
try {
|
|
70
|
+
const key = deriveSharedKey(recipientPrivateKey, ephPub);
|
|
71
|
+
return gcm(key, nonce).decrypt(ct);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ---------- AES-256-GCM(层密钥直接封装) ----------
|
|
78
|
+
/** 加密。ct = ciphertext || tag(16B);nonce 12 字节随机 */
|
|
79
|
+
export function aesGcmEncrypt(key, plaintext) {
|
|
80
|
+
if (key.length !== 32)
|
|
81
|
+
throw new Error('AES-256 密钥必须是 32 字节');
|
|
82
|
+
const nonce = randomBytes(12);
|
|
83
|
+
const aes = gcm(key, nonce);
|
|
84
|
+
return { nonce, ct: aes.encrypt(plaintext) };
|
|
85
|
+
}
|
|
86
|
+
/** 解密;认证失败返回 null(不降级、不部分解密——Schema §7.4) */
|
|
87
|
+
export function aesGcmDecrypt(key, nonce, ct) {
|
|
88
|
+
if (key.length !== 32)
|
|
89
|
+
throw new Error('AES-256 密钥必须是 32 字节');
|
|
90
|
+
try {
|
|
91
|
+
const aes = gcm(key, nonce);
|
|
92
|
+
return aes.decrypt(ct);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** 随机 n 字节 */
|
|
99
|
+
export function randomKey(n) {
|
|
100
|
+
return randomBytes(n);
|
|
101
|
+
}
|
|
102
|
+
// ---------- 域分离子密钥(D13 见面层逐字段 opt-in) ----------
|
|
103
|
+
/** 子密钥域分离前缀(改前缀 = 协议不兼容变更,不得随意改动) */
|
|
104
|
+
const SUBKEY_INFO_PREFIX = 'hongxian/protocol/v1/subkey/';
|
|
105
|
+
/**
|
|
106
|
+
* 由层密钥派生用途子密钥:HKDF-SHA256(master, info = prefix + label)。
|
|
107
|
+
*
|
|
108
|
+
* 见面层用它把单一的 L3 层密钥摊成「每字段一把」——
|
|
109
|
+
* 交付 contact_wechat 的子密钥不等于交付 contact_phone 的子密钥,
|
|
110
|
+
* D13「给微信号但不给手机号」由此在密码学层面成立,而不只是 UI 上的开关。
|
|
111
|
+
*
|
|
112
|
+
* 单向性:子密钥推不出主密钥,也无法推到别的字段(不同 info)。
|
|
113
|
+
*/
|
|
114
|
+
export function deriveSubkey(masterKey, label) {
|
|
115
|
+
if (masterKey.length !== 32)
|
|
116
|
+
throw new Error('主密钥必须是 32 字节');
|
|
117
|
+
const info = new TextEncoder().encode(SUBKEY_INFO_PREFIX + label);
|
|
118
|
+
return hkdf(sha256, masterKey, undefined, info, 32);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=primitives.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primitives.js","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hongxian/crypto",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@hongxian/crypto",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "红线密钥体系:BIP39 恢复短语 → Argon2id → 身份/协商/层密钥;Ed25519 签名、X25519 封装、AES-256-GCM",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Hongxian contributors (https://hongxian.dev)",
|
|
7
|
+
"homepage": "https://hongxian.dev",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/hongxian-dev/hongxian.git",
|
|
11
|
+
"directory": "packages/crypto"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/hongxian-dev/hongxian/issues"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"prepublishOnly": "node ../../scripts/guard-publish.mjs"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@noble/ciphers": "^1.3.0",
|
|
35
|
+
"@noble/curves": "^1.9.0",
|
|
36
|
+
"@noble/hashes": "^1.8.0",
|
|
37
|
+
"@scure/bip39": "^1.5.0",
|
|
38
|
+
"hash-wasm": "^4.12.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public",
|
|
45
|
+
"provenance": true
|
|
46
|
+
}
|
|
47
|
+
}
|