@unknownncat/curve25519-node 1.0.1 → 2.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.
Files changed (65) hide show
  1. package/NOTICE.md +89 -0
  2. package/README.en.md +422 -0
  3. package/README.md +309 -112
  4. package/SECURITY.md +23 -0
  5. package/THIRD_PARTY_NOTICE.md +3 -0
  6. package/THIRD_PARTY_NOTICES.md +5 -0
  7. package/dist/axlsign.d.ts +31 -0
  8. package/dist/axlsign.d.ts.map +1 -0
  9. package/dist/axlsign.js +118 -0
  10. package/dist/axlsign.js.map +1 -0
  11. package/dist/cjs/axlsign.js +127 -0
  12. package/dist/cjs/axlsign.js.map +1 -0
  13. package/dist/cjs/ed25519.js +46 -10
  14. package/dist/cjs/ed25519.js.map +1 -1
  15. package/dist/cjs/index.js +41 -1
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/internal/assert.js.map +1 -1
  18. package/dist/cjs/internal/axlsign-wasm/LICENSE +21 -0
  19. package/dist/cjs/internal/axlsign-wasm/axlsign_wasm.d.ts +12 -0
  20. package/dist/cjs/internal/axlsign-wasm/axlsign_wasm.js +171 -0
  21. package/dist/cjs/internal/axlsign-wasm/axlsign_wasm_bg.wasm +0 -0
  22. package/dist/cjs/internal/axlsign-wasm/axlsign_wasm_bg.wasm.d.ts +13 -0
  23. package/dist/cjs/internal/axlsign-wasm/package.json +17 -0
  24. package/dist/cjs/internal/curve25519-wasm/LICENSE +21 -0
  25. package/dist/cjs/internal/curve25519-wasm/curve25519_wasm.d.ts +12 -0
  26. package/dist/cjs/internal/curve25519-wasm/curve25519_wasm.js +165 -0
  27. package/dist/cjs/internal/curve25519-wasm/curve25519_wasm_bg.wasm +0 -0
  28. package/dist/cjs/internal/curve25519-wasm/curve25519_wasm_bg.wasm.d.ts +13 -0
  29. package/dist/cjs/internal/curve25519-wasm/package.json +17 -0
  30. package/dist/cjs/wasm.js +228 -0
  31. package/dist/cjs/wasm.js.map +1 -0
  32. package/dist/cjs/x25519.js +73 -12
  33. package/dist/cjs/x25519.js.map +1 -1
  34. package/dist/ed25519.d.ts +21 -0
  35. package/dist/ed25519.d.ts.map +1 -1
  36. package/dist/ed25519.js +44 -13
  37. package/dist/ed25519.js.map +1 -1
  38. package/dist/index.d.ts +109 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +40 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/internal/assert.js.map +1 -1
  43. package/dist/internal/axlsign-wasm/LICENSE +21 -0
  44. package/dist/internal/axlsign-wasm/axlsign_wasm.d.ts +12 -0
  45. package/dist/internal/axlsign-wasm/axlsign_wasm.js +171 -0
  46. package/dist/internal/axlsign-wasm/axlsign_wasm_bg.wasm +0 -0
  47. package/dist/internal/axlsign-wasm/axlsign_wasm_bg.wasm.d.ts +13 -0
  48. package/dist/internal/axlsign-wasm/package.json +17 -0
  49. package/dist/internal/curve25519-wasm/LICENSE +21 -0
  50. package/dist/internal/curve25519-wasm/curve25519_wasm.d.ts +12 -0
  51. package/dist/internal/curve25519-wasm/curve25519_wasm.js +165 -0
  52. package/dist/internal/curve25519-wasm/curve25519_wasm_bg.wasm +0 -0
  53. package/dist/internal/curve25519-wasm/curve25519_wasm_bg.wasm.d.ts +13 -0
  54. package/dist/internal/curve25519-wasm/package.json +17 -0
  55. package/dist/types.d.ts +2 -5
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/wasm.d.ts +92 -0
  58. package/dist/wasm.d.ts.map +1 -0
  59. package/dist/wasm.js +204 -0
  60. package/dist/wasm.js.map +1 -0
  61. package/dist/x25519.d.ts +29 -0
  62. package/dist/x25519.d.ts.map +1 -1
  63. package/dist/x25519.js +66 -12
  64. package/dist/x25519.js.map +1 -1
  65. package/package.json +38 -5
package/NOTICE.md ADDED
@@ -0,0 +1,89 @@
1
+ # NOTICE
2
+
3
+ This repository is distributed under the MIT license and includes third-party software components.
4
+
5
+ Primary package: `@unknownncat/curve25519-node`
6
+ Repository: <https://github.com/unknownncat/curve25519-node>
7
+
8
+ ## Security and Disclosure
9
+
10
+ - Security policy: [SECURITY.md](./SECURITY.md)
11
+ - Vulnerability reporting should follow the private disclosure flow described in that file.
12
+
13
+ ## Direct ecosystem references
14
+
15
+ - `curve25519-js` — MIT or CC0 (as declared upstream)
16
+ <https://github.com/harveyconnor/curve25519-js>
17
+ - `TweetNaCl.js` — Public Domain (as declared upstream)
18
+ <https://tweetnacl.js.org/>
19
+ - OpenSSL (used through Node.js `node:crypto`) — Apache-2.0
20
+ <https://www.openssl.org/>
21
+
22
+ ## Rust/WASM dependency notices
23
+
24
+ The project contains two Rust/WASM crates:
25
+
26
+ - `wasm/curve25519-wasm`
27
+ - `wasm/axlsign`
28
+
29
+ License inventory was collected with:
30
+
31
+ ```bash
32
+ cargo license -t
33
+ ```
34
+
35
+ ### Crates present in both Rust projects
36
+
37
+ | Crate | License |
38
+ | -------------------------- | ----------------------------------- |
39
+ | block-buffer | Apache-2.0 OR MIT |
40
+ | bumpalo | Apache-2.0 OR MIT |
41
+ | cfg-if | Apache-2.0 OR MIT |
42
+ | cpufeatures | Apache-2.0 OR MIT |
43
+ | crypto-common | Apache-2.0 OR MIT |
44
+ | curve25519-dalek | BSD-3-Clause |
45
+ | curve25519-dalek-derive | Apache-2.0 OR MIT |
46
+ | digest | Apache-2.0 OR MIT |
47
+ | ed25519 | Apache-2.0 OR MIT |
48
+ | ed25519-dalek | BSD-3-Clause |
49
+ | fiat-crypto | Apache-2.0 OR BSD-1-Clause OR MIT |
50
+ | generic-array | MIT |
51
+ | libc | Apache-2.0 OR MIT |
52
+ | once_cell | Apache-2.0 OR MIT |
53
+ | proc-macro2 | Apache-2.0 OR MIT |
54
+ | quote | Apache-2.0 OR MIT |
55
+ | rand_core | Apache-2.0 OR MIT |
56
+ | rustc_version | Apache-2.0 OR MIT |
57
+ | rustversion | Apache-2.0 OR MIT |
58
+ | semver | Apache-2.0 OR MIT |
59
+ | sha2 | Apache-2.0 OR MIT |
60
+ | signature | Apache-2.0 OR MIT |
61
+ | subtle | BSD-3-Clause |
62
+ | syn | Apache-2.0 OR MIT |
63
+ | typenum | Apache-2.0 OR MIT |
64
+ | unicode-ident | (Apache-2.0 OR MIT) AND Unicode-3.0 |
65
+ | version_check | Apache-2.0 OR MIT |
66
+ | wasm-bindgen | Apache-2.0 OR MIT |
67
+ | wasm-bindgen-macro | Apache-2.0 OR MIT |
68
+ | wasm-bindgen-macro-support | Apache-2.0 OR MIT |
69
+ | wasm-bindgen-shared | Apache-2.0 OR MIT |
70
+ | x25519-dalek | BSD-3-Clause |
71
+
72
+ ### Crates present only in `wasm/axlsign`
73
+
74
+ | Crate | License |
75
+ | ------- | ----------------- |
76
+ | zeroize | Apache-2.0 OR MIT |
77
+
78
+ ### Local Rust crates
79
+
80
+ | Crate | License |
81
+ | ----------------------- | ------- |
82
+ | curve25519-node-wasm | MIT |
83
+ | curve25519-node-axlsign | MIT |
84
+
85
+ ## Notes
86
+
87
+ - Upstream licenses remain with their respective authors and projects.
88
+ - This NOTICE summarizes dependencies and does not replace upstream license texts.
89
+ - For full build/runtime details, see `README.md`, `README.en.md`, and `wasm/README.md`.
package/README.en.md ADDED
@@ -0,0 +1,422 @@
1
+ # @unknownncat/curve25519-node
2
+
3
+ > 🇧🇷 Versão em português (principal): [README.md](./README.md)
4
+
5
+ Zero-runtime-dependency implementation of:
6
+
7
+ - X25519 + Ed25519 (modern mode via OpenSSL in `node:crypto`)
8
+ - X25519 + Ed25519 (optional modern mode via WASM)
9
+ - legacy axlsign (optional WASM mode, compatible with `curve25519-js`)
10
+
11
+ [![npm](https://img.shields.io/npm/v/@unknownncat/curve25519-node)](https://www.npmjs.com/package/@unknownncat/curve25519-node)
12
+ [![node](https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
13
+ [![types](https://img.shields.io/badge/types-included-blue)](./dist/index.d.ts)
14
+ ![runtime deps](https://img.shields.io/badge/runtime%20deps-0-brightgreen)
15
+ ![esm+cjs](https://img.shields.io/badge/ESM%20%2B%20CJS-compatible-blue)
16
+ [![license](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
17
+
18
+ - Node: `>= 20`
19
+ - Runtime dependencies: `0`
20
+ - TypeScript: `strict`
21
+ - Module formats: ESM + CJS
22
+
23
+ ---
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ npm i @unknownncat/curve25519-node
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Quick Usage
34
+
35
+ ```ts
36
+ import { randomBytes } from "node:crypto";
37
+ import { asBytes32, x25519, ed25519 } from "@unknownncat/curve25519-node";
38
+
39
+ const aliceSeed = asBytes32(randomBytes(32));
40
+ const bobSeed = asBytes32(randomBytes(32));
41
+
42
+ const aliceX = x25519.generateKeyPair(aliceSeed);
43
+ const bobX = x25519.generateKeyPair(bobSeed);
44
+
45
+ const secret1 = x25519.sharedKey(aliceX.private, bobX.public);
46
+ const secret2 = x25519.sharedKey(bobX.private, aliceX.public);
47
+ // secret1 === secret2
48
+
49
+ const signerSeed = asBytes32(randomBytes(32));
50
+ const signer = ed25519.generateKeyPair(signerSeed);
51
+ const msg = new TextEncoder().encode("hello");
52
+
53
+ const sig = ed25519.sign(signerSeed, msg);
54
+ const ok = ed25519.verify(signer.public, msg, sig);
55
+ ```
56
+
57
+ CommonJS:
58
+
59
+ ```js
60
+ const { x25519, ed25519, asBytes32 } = require("@unknownncat/curve25519-node");
61
+ ```
62
+
63
+ Legacy axlsign via WASM:
64
+
65
+ ```ts
66
+ import { asBytes32, axlsign } from "@unknownncat/curve25519-node";
67
+
68
+ const seed = asBytes32(new Uint8Array(32));
69
+ const kp = axlsign.generateKeyPair(seed); // curve25519-js-compatible X25519 keypair
70
+ const sig = axlsign.sign(kp.private, new TextEncoder().encode("hello"), new Uint8Array(64));
71
+ const ok = axlsign.verify(kp.public, new TextEncoder().encode("hello"), sig);
72
+ ```
73
+
74
+ Modern WASM mode (`wasm`):
75
+
76
+ ```ts
77
+ import { asBytes32, wasm } from "@unknownncat/curve25519-node";
78
+
79
+ const seed = asBytes32(new Uint8Array(32));
80
+ const kp = wasm.x25519.generateKeyPair(seed);
81
+ const shared = wasm.x25519.sharedKey(kp.private, kp.public);
82
+
83
+ const msg = new TextEncoder().encode("hello");
84
+ const sig = wasm.ed25519.sign(seed, msg);
85
+ const ok = wasm.ed25519.verify(wasm.ed25519.publicKey(seed), msg, sig);
86
+ ```
87
+
88
+ ---
89
+
90
+ ## API
91
+
92
+ ### `x25519`
93
+
94
+ - `createPrivateKeyObject(secretKey32: Bytes32): KeyObject`
95
+ - `createPublicKeyObject(publicKey32: Bytes32): KeyObject`
96
+ - `publicKeyFromPrivateKeyObject(privateKey: KeyObject): Bytes32`
97
+ - `publicKey(secretKey32: Bytes32): Bytes32`
98
+ - `sharedKeyFromKeyObjects(privateKey: KeyObject, publicKey: KeyObject): Bytes32`
99
+ - `sharedKey(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32`
100
+ - `sharedKeyStrict(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32` (rejects all-zero shared secret)
101
+ - `sharedKeyStrictFromKeyObjects(privateKey: KeyObject, publicKey: KeyObject): Bytes32` (rejects all-zero shared secret)
102
+ - `isAllZero32(bytes32: Bytes32): boolean`
103
+ - `generateKeyPair(seed32: Bytes32): { public: Bytes32; private: Bytes32 }`
104
+
105
+ ### `ed25519`
106
+
107
+ - `createPrivateKeyObject(secretSeed32: Bytes32): KeyObject`
108
+ - `createPublicKeyObject(publicKey32: Bytes32): KeyObject`
109
+ - `publicKeyFromPrivateKeyObject(privateKey: KeyObject): Bytes32`
110
+ - `publicKey(secretSeed32: Bytes32): Bytes32`
111
+ - `generateKeyPair(seed32: Bytes32): { public: Bytes32; private: Bytes32 }`
112
+ - `sign(secretSeed32: Bytes32, msg: Uint8Array): Bytes64`
113
+ - `signWithPrivateKey(privateKey: KeyObject, msg: Uint8Array): Bytes64`
114
+ - `verify(publicKey32: Bytes32, msg: Uint8Array, signature64: Bytes64): boolean`
115
+ - `verifyWithPublicKey(publicKey: KeyObject, msg: Uint8Array, signature64: Bytes64): boolean`
116
+ - `signMessage(secretSeed32: Bytes32, msg: Uint8Array): Uint8Array` (`signature || message`)
117
+ - `openMessage(publicKey32: Bytes32, signedMsg: Uint8Array): Uint8Array | null`
118
+
119
+ ### `axlsign` (legacy compatibility via WASM)
120
+
121
+ - `publicKey(secretKey32: Bytes32): Bytes32`
122
+ - `sharedKey(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32`
123
+ - `generateKeyPair(seed32: Bytes32): { public: Bytes32; private: Bytes32 }`
124
+ - `sign(secretKey32: Bytes32, msg: Uint8Array, opt_random?: Bytes64): Bytes64`
125
+ - `verify(publicKey32: Bytes32, msg: Uint8Array, signature64: Bytes64): boolean`
126
+ - `signMessage(secretKey32: Bytes32, msg: Uint8Array, opt_random?: Bytes64): Uint8Array`
127
+ - `openMessage(publicKey32: Bytes32, signedMsg: Uint8Array): Uint8Array | null`
128
+
129
+ ### `wasm` (optional modern mode via WASM)
130
+
131
+ `wasm.x25519`:
132
+
133
+ - `createPrivateKeyObject(secretKey32: Bytes32): WasmX25519PrivateKeyObject`
134
+ - `createPublicKeyObject(publicKey32: Bytes32): WasmX25519PublicKeyObject`
135
+ - `publicKeyFromPrivateKeyObject(privateKey: WasmX25519PrivateKeyObject): Bytes32`
136
+ - `publicKey(secretKey32: Bytes32): Bytes32`
137
+ - `sharedKeyFromKeyObjects(privateKey: WasmX25519PrivateKeyObject, publicKey: WasmX25519PublicKeyObject): Bytes32`
138
+ - `sharedKey(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32`
139
+ - `sharedKeyStrict(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32`
140
+ - `sharedKeyStrictFromKeyObjects(privateKey: WasmX25519PrivateKeyObject, publicKey: WasmX25519PublicKeyObject): Bytes32`
141
+ - `isAllZero32(bytes32: Bytes32): boolean`
142
+ - `generateKeyPair(seed32: Bytes32): { public: Bytes32; private: Bytes32 }`
143
+
144
+ `wasm.ed25519`:
145
+
146
+ - `createPrivateKeyObject(secretSeed32: Bytes32): WasmEd25519PrivateKeyObject`
147
+ - `createPublicKeyObject(publicKey32: Bytes32): WasmEd25519PublicKeyObject`
148
+ - `publicKeyFromPrivateKeyObject(privateKey: WasmEd25519PrivateKeyObject): Bytes32`
149
+ - `publicKey(secretSeed32: Bytes32): Bytes32`
150
+ - `generateKeyPair(seed32: Bytes32): { public: Bytes32; private: Bytes32 }`
151
+ - `sign(secretSeed32: Bytes32, msg: Uint8Array): Bytes64`
152
+ - `signWithPrivateKey(privateKey: WasmEd25519PrivateKeyObject, msg: Uint8Array): Bytes64`
153
+ - `verify(publicKey32: Bytes32, msg: Uint8Array, signature64: Bytes64): boolean`
154
+ - `verifyWithPublicKey(publicKey: WasmEd25519PublicKeyObject, msg: Uint8Array, signature64: Bytes64): boolean`
155
+ - `signMessage(secretSeed32: Bytes32, msg: Uint8Array): Uint8Array`
156
+ - `openMessage(publicKey32: Bytes32, signedMsg: Uint8Array): Uint8Array | null`
157
+
158
+ ### Top-level compatibility aliases
159
+
160
+ - `sharedKey = x25519.sharedKey`
161
+ - `sharedKeyStrict = x25519.sharedKeyStrict`
162
+ - `generateKeyPair = x25519.generateKeyPair`
163
+ - `sign`, `verify`, `signMessage`, `openMessage` (Ed25519 semantics)
164
+ - `generateKeyPairX25519`, `generateKeyPairEd25519`
165
+
166
+ ---
167
+
168
+ ## Compatibility Notes
169
+
170
+ This package provides three modes:
171
+
172
+ - **modern native (recommended):** `x25519` + `ed25519` via `node:crypto`
173
+ - **modern WASM (optional):** `wasm` namespace (`wasm.x25519` + `wasm.ed25519`)
174
+ - **legacy:** `axlsign` via WASM for `curve25519-js` compatibility
175
+
176
+ | Feature | `curve25519-js` | `curve25519-node` |
177
+ | -------------------------------- | --------------- | ------------------------------------------- |
178
+ | Signature scheme (modern) | axlsign | Ed25519 (standard) |
179
+ | Alternative modern scheme | no | Ed25519 via WASM (`wasm.ed25519`) |
180
+ | Signature scheme (legacy) | axlsign | axlsign (namespace `axlsign`) |
181
+ | Key agreement | X25519 | X25519 |
182
+ | Alternative modern key agreement | no | X25519 via WASM (`wasm.x25519`) |
183
+ | Same key for signing + ECDH | yes | only in `axlsign` namespace |
184
+ | `opt_random` in signing APIs | yes | yes in `axlsign`, no in top-level/`ed25519` |
185
+ | OpenSSL backend | no | yes |
186
+
187
+ Important:
188
+
189
+ - X25519 public keys and Ed25519 public keys are different.
190
+ - For stricter protocol flows (Signal-like), prefer `sharedKeyStrict` to reject all-zero shared secrets.
191
+ - `node:crypto` does not expose an API to convert X25519 public keys to/from Ed25519 public keys.
192
+ - Top-level `sign`/`signMessage` and `ed25519` keep Ed25519 semantics and reject `opt_random`.
193
+ - For `curve25519-js` compatibility (including `opt_random`), use namespace `axlsign`.
194
+ - Ed25519 signatures here are deterministic (OpenSSL default behavior).
195
+ - WASM modules (`axlsign` and `wasm`) are lazy-loaded on first call (importing only `x25519`/`ed25519` does not initialize WASM).
196
+
197
+ ---
198
+
199
+ ## Why This Exists
200
+
201
+ `curve25519-js` is an important project, but it relies on manual finite-field arithmetic in JS (`Float64Array`, TweetNaCl style internals).
202
+
203
+ This package targets modern Node using OpenSSL primitives:
204
+
205
+ - safer implementation path by default
206
+ - better performance on Node >= 20
207
+ - smaller, explicit API surface
208
+ - strong typing with zero runtime dependencies
209
+
210
+ In addition:
211
+
212
+ - WASM `axlsign` enables progressive migration of legacy code.
213
+ - WASM `wasm` provides a modern backend option without relying on `node:crypto` in the crypto execution path.
214
+
215
+ ---
216
+
217
+ ## Branded Types
218
+
219
+ - `Bytes32`
220
+ - `Bytes64`
221
+
222
+ Validation helpers (no copy):
223
+
224
+ - `asBytes32(u8)`
225
+ - `asBytes64(u8)`
226
+
227
+ ---
228
+
229
+ ## RFC Map (what this project uses)
230
+
231
+ | RFC | Sections used | How it is used | Where in code |
232
+ | --------------------------------- | ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | ----------------------- |
233
+ | RFC 7748 (X25519) | Section 5 (`The X25519 and X448 Functions`) | Scalar decoding/clamping and X25519 behavior (clear low 3 bits, clear top bit, set second-top bit). | `src/x25519.ts` |
234
+ | RFC 7748 (X25519) | Section 5.2 (`Test Vectors`), Section 6.1 (`Diffie-Hellman / Curve25519`) | Official vectors for interoperability and correctness checks. | `test/x25519.test.mjs` |
235
+ | RFC 8032 (Ed25519) | Section 5.1.5 (`Key Generation`), 5.1.6 (`Sign`), 5.1.7 (`Verify`) | Ed25519 keygen/sign/verify semantics (performed by OpenSSL via `node:crypto`). | `src/ed25519.ts` |
236
+ | RFC 8032 (Ed25519) | Section 7.1 (`Test Vectors for Ed25519`) | Deterministic vector checks for public key and signature correctness. | `test/ed25519.test.mjs` |
237
+ | RFC 8410 (X25519/Ed25519 in PKIX) | Section 3 (algorithm identifiers), Section 4 (`Subject Public Key Fields`), Section 7 (`Private Key Format`) | DER layout for raw 32-byte key import/export to SPKI/PKCS#8 with X25519/Ed25519 OIDs. | `src/internal/der.ts` |
238
+
239
+ Indirect references via RFC 8410 structures:
240
+
241
+ - RFC 5958 (OneAsymmetricKey / PKCS#8 family)
242
+ - RFC 5280 Section 4.1.2.7 (`Subject Public Key Info`)
243
+
244
+ Notes:
245
+
246
+ - This project does not reimplement curve arithmetic in JS; cryptographic operations are delegated to OpenSSL via `node:crypto`.
247
+ - Tests include official vectors from RFC 7748 and RFC 8032.
248
+
249
+ Run tests:
250
+
251
+ ```bash
252
+ npm test
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Technical Details (DER / RFC 8410)
258
+
259
+ Raw 32-byte keys are imported/exported using fixed DER prefixes:
260
+
261
+ - X25519 PKCS#8: `302e020100300506032b656e04220420`
262
+ - X25519 SPKI: `302a300506032b656e032100`
263
+ - Ed25519 PKCS#8: `302e020100300506032b657004220420`
264
+ - Ed25519 SPKI: `302a300506032b6570032100`
265
+
266
+ Implementation notes:
267
+
268
+ - preallocated buffers + `.set`
269
+ - zero-copy `Uint8Array` views when safe
270
+ - no `Buffer.concat` in hot paths
271
+
272
+ ---
273
+
274
+ ## Performance Notes
275
+
276
+ - Avoids unnecessary byte copies in critical paths.
277
+ - `signMessage` builds `signature || message` with a single preallocated `Uint8Array`.
278
+ - For high-throughput loops, use `KeyObject` helpers (`create*KeyObject`, `*FromKeyObjects`) to reduce ASN.1 parse overhead.
279
+
280
+ ---
281
+
282
+ ## Security Notes
283
+
284
+ - strict type/length validation on public APIs
285
+ - no secret logging
286
+ - `timingSafeEqual` for internal fixed-size comparisons where needed
287
+
288
+ ---
289
+
290
+ ## Benchmarks
291
+
292
+ Benchmark suite is isolated in `bench/` (separate subproject) and compares against `curve25519-js`.
293
+
294
+ ```bash
295
+ npm run build
296
+ cd bench
297
+ npm install
298
+ npm run bench
299
+ ```
300
+
301
+ ### Real benchmark snapshot (`npm run bench:ci`) on GitHub Codespaces
302
+
303
+ Command:
304
+
305
+ ```bash
306
+ node --expose-gc bench.mjs --rounds=16 --roundMs=350 --warmupMs=500 --vectors=64 --variants=raw,cached --strict --verifyEvery=64 --jsonFile=results/bench-results.json
307
+ ```
308
+
309
+ Environment:
310
+
311
+ - Node: `v24.11.1`
312
+ - OpenSSL: `3.5.4`
313
+ - CPU: `AMD EPYC 7763 64-Core Processor`
314
+ - Logical cores: `4`
315
+ - Vectors: `64`
316
+
317
+ ### Table 1 - Modern API (`x25519` + `ed25519`)
318
+
319
+ `sign`/`verify` rows below compare API throughput, not cryptographic equivalence (Ed25519 vs legacy axlsign).
320
+
321
+ | Operation | Modern raw | Legacy raw (`curve25519-js`) | Raw speedup | Modern cached | Legacy cached (`curve25519-js`) | Cached speedup |
322
+ | ------------------------------ | ---------: | ---------------------------: | ----------: | ------------: | ------------------------------: | -------------: |
323
+ | `x25519.generateKeyPair` | 14,378 | 1,591 | 9.04x | 41,120 | 1,478 | 27.83x |
324
+ | `x25519.sharedKey` | 9,970 | 1,591 | 6.27x | 23,995 | 1,554 | 15.44x |
325
+ | `ed25519.sign (msg32)` | 11,273 | 143 | 78.95x | 23,696 | 133 | 178.10x |
326
+ | `ed25519.sign (msg1024)` | 10,800 | 138 | 78.07x | 22,502 | 147 | 152.92x |
327
+ | `ed25519.verify (msg32)` | 7,280 | 136 | 53.36x | 8,271 | 155 | 53.37x |
328
+ | `ed25519.verify (msg1024)` | 7,160 | 132 | 54.33x | 8,159 | 154 | 52.90x |
329
+ | `ed25519.signMessage (msg256)` | 10,624 | 131 | 81.09x | 23,304 | 148 | 156.97x |
330
+ | `ed25519.openMessage (msg256)` | 6,574 | 124 | 52.93x | 8,129 | 154 | 52.64x |
331
+
332
+ ### Table 2 - `axlsign` compatibility mode (equivalent to `curve25519-js`)
333
+
334
+ This table compares the same cryptographic scheme (equivalence + throughput).
335
+
336
+ | Operation | Modern raw | Legacy raw (`curve25519-js`) | Raw speedup | Modern cached | Legacy cached (`curve25519-js`) | Cached speedup |
337
+ | ----------------------------------------- | ---------: | ---------------------------: | ----------: | ------------: | ------------------------------: | -------------: |
338
+ | `axlsign.generateKeyPair` | 8,429 | 1,583 | 5.33x | 8,384 | 1,585 | 5.29x |
339
+ | `axlsign.sharedKey` | 8,452 | 1,583 | 5.34x | 8,396 | 1,570 | 5.35x |
340
+ | `axlsign.sign (msg32)` | 3,973 | 144 | 27.61x | 3,952 | 140 | 28.28x |
341
+ | `axlsign.sign (msg32,opt_random)` | 3,969 | 147 | 27.03x | 3,984 | 139 | 28.58x |
342
+ | `axlsign.sign (msg1024)` | 3,881 | 143 | 27.16x | 3,864 | 139 | 27.72x |
343
+ | `axlsign.verify (msg32)` | 6,527 | 146 | 44.70x | 6,534 | 143 | 45.72x |
344
+ | `axlsign.verify (msg32,opt_random)` | 6,506 | 144 | 45.07x | 6,469 | 141 | 45.80x |
345
+ | `axlsign.verify (msg1024)` | 6,361 | 141 | 45.03x | 6,337 | 135 | 46.92x |
346
+ | `axlsign.signMessage (msg256)` | 3,902 | 140 | 27.79x | 3,935 | 141 | 27.98x |
347
+ | `axlsign.signMessage (msg256,opt_random)` | 3,885 | 142 | 27.40x | 3,864 | 145 | 26.60x |
348
+ | `axlsign.openMessage (msg256)` | 6,441 | 138 | 46.57x | 6,300 | 131 | 47.93x |
349
+ | `axlsign.openMessage (msg256,opt_random)` | 6,362 | 141 | 45.24x | 6,285 | 130 | 48.22x |
350
+
351
+ Notes:
352
+
353
+ - `raw` includes end-to-end API cost.
354
+ - `cached` reduces setup overhead to better expose cryptographic throughput.
355
+ - Numbers are sourced from the `bench:ci` JSON output (`results/bench-results.json`).
356
+
357
+ ---
358
+
359
+ ## Building WASM namespaces (`axlsign` and `wasm`)
360
+
361
+ In the npm package, WASM artifacts are already prebuilt under `dist/`.
362
+
363
+ To build from source, you need:
364
+
365
+ - Rust toolchain
366
+ - `wasm-pack` installed
367
+
368
+ Then `npm run build` runs:
369
+
370
+ 1. `wasm-pack build` (`wasm/axlsign`)
371
+ 2. `wasm-pack build` (`wasm/curve25519-wasm`)
372
+ 3. TypeScript ESM + CJS build
373
+ 4. copy of WASM artifacts to `dist/internal/axlsign-wasm` and `dist/internal/curve25519-wasm`
374
+
375
+ Rust crates reference: [wasm/README.md](./wasm/README.md)
376
+
377
+ ---
378
+
379
+ ## Contributing
380
+
381
+ - Guide: [CONTRIBUTING.md](./CONTRIBUTING.md)
382
+ - Code of conduct: [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
383
+ - Security: [SECURITY.md](./SECURITY.md)
384
+
385
+ Full local validation:
386
+
387
+ ```bash
388
+ npm run ci
389
+ ```
390
+
391
+ ---
392
+
393
+ ## License
394
+
395
+ MIT
396
+
397
+ Additional compliance/security documents:
398
+
399
+ - [NOTICE.md](./NOTICE.md) (canonical third-party notice)
400
+ - [THIRD_PARTY_NOTICE.md](./THIRD_PARTY_NOTICE.md) and [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) (compatibility aliases)
401
+ - [SECURITY.md](./SECURITY.md) (security policy and vulnerability reporting)
402
+
403
+ ---
404
+
405
+ ## Credits
406
+
407
+ - [curve25519-js](https://github.com/harveyconnor/curve25519-js) (Harvey Connor, Dmitry Chestnykh)
408
+ - [TweetNaCl.js](https://tweetnacl.js.org/)
409
+ - Trevor Perrin, Curve25519 signatures idea: <https://moderncrypto.org/mail-archive/curves/2014/000205.html>
410
+ - [Node.js `crypto` docs](https://nodejs.org/api/crypto.html)
411
+ - [OpenSSL](https://www.openssl.org/)
412
+ - [RustCrypto](https://github.com/RustCrypto)
413
+ - [wasm-bindgen](https://github.com/wasm-bindgen/wasm-bindgen)
414
+ - [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek)
415
+ - [ed25519-dalek](https://github.com/dalek-cryptography/ed25519-dalek)
416
+ - [x25519-dalek](https://github.com/dalek-cryptography/x25519-dalek)
417
+ - [zeroize](https://github.com/RustCrypto/utils/tree/master/zeroize)
418
+ - [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748)
419
+ - [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032)
420
+ - [RFC 8410](https://www.rfc-editor.org/rfc/rfc8410)
421
+ - [RFC 5958](https://www.rfc-editor.org/rfc/rfc5958)
422
+ - [RFC 5280](https://www.rfc-editor.org/rfc/rfc5280)