@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.
- package/NOTICE.md +89 -0
- package/README.en.md +422 -0
- package/README.md +309 -112
- package/SECURITY.md +23 -0
- package/THIRD_PARTY_NOTICE.md +3 -0
- package/THIRD_PARTY_NOTICES.md +5 -0
- package/dist/axlsign.d.ts +31 -0
- package/dist/axlsign.d.ts.map +1 -0
- package/dist/axlsign.js +118 -0
- package/dist/axlsign.js.map +1 -0
- package/dist/cjs/axlsign.js +127 -0
- package/dist/cjs/axlsign.js.map +1 -0
- package/dist/cjs/ed25519.js +46 -10
- package/dist/cjs/ed25519.js.map +1 -1
- package/dist/cjs/index.js +41 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/assert.js.map +1 -1
- package/dist/cjs/internal/axlsign-wasm/LICENSE +21 -0
- package/dist/cjs/internal/axlsign-wasm/axlsign_wasm.d.ts +12 -0
- package/dist/cjs/internal/axlsign-wasm/axlsign_wasm.js +171 -0
- package/dist/cjs/internal/axlsign-wasm/axlsign_wasm_bg.wasm +0 -0
- package/dist/cjs/internal/axlsign-wasm/axlsign_wasm_bg.wasm.d.ts +13 -0
- package/dist/cjs/internal/axlsign-wasm/package.json +17 -0
- package/dist/cjs/internal/curve25519-wasm/LICENSE +21 -0
- package/dist/cjs/internal/curve25519-wasm/curve25519_wasm.d.ts +12 -0
- package/dist/cjs/internal/curve25519-wasm/curve25519_wasm.js +165 -0
- package/dist/cjs/internal/curve25519-wasm/curve25519_wasm_bg.wasm +0 -0
- package/dist/cjs/internal/curve25519-wasm/curve25519_wasm_bg.wasm.d.ts +13 -0
- package/dist/cjs/internal/curve25519-wasm/package.json +17 -0
- package/dist/cjs/wasm.js +228 -0
- package/dist/cjs/wasm.js.map +1 -0
- package/dist/cjs/x25519.js +73 -12
- package/dist/cjs/x25519.js.map +1 -1
- package/dist/ed25519.d.ts +21 -0
- package/dist/ed25519.d.ts.map +1 -1
- package/dist/ed25519.js +44 -13
- package/dist/ed25519.js.map +1 -1
- package/dist/index.d.ts +109 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/assert.js.map +1 -1
- package/dist/internal/axlsign-wasm/LICENSE +21 -0
- package/dist/internal/axlsign-wasm/axlsign_wasm.d.ts +12 -0
- package/dist/internal/axlsign-wasm/axlsign_wasm.js +171 -0
- package/dist/internal/axlsign-wasm/axlsign_wasm_bg.wasm +0 -0
- package/dist/internal/axlsign-wasm/axlsign_wasm_bg.wasm.d.ts +13 -0
- package/dist/internal/axlsign-wasm/package.json +17 -0
- package/dist/internal/curve25519-wasm/LICENSE +21 -0
- package/dist/internal/curve25519-wasm/curve25519_wasm.d.ts +12 -0
- package/dist/internal/curve25519-wasm/curve25519_wasm.js +165 -0
- package/dist/internal/curve25519-wasm/curve25519_wasm_bg.wasm +0 -0
- package/dist/internal/curve25519-wasm/curve25519_wasm_bg.wasm.d.ts +13 -0
- package/dist/internal/curve25519-wasm/package.json +17 -0
- package/dist/types.d.ts +2 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/wasm.d.ts +92 -0
- package/dist/wasm.d.ts.map +1 -0
- package/dist/wasm.js +204 -0
- package/dist/wasm.js.map +1 -0
- package/dist/x25519.d.ts +29 -0
- package/dist/x25519.d.ts.map +1 -1
- package/dist/x25519.js +66 -12
- package/dist/x25519.js.map +1 -1
- package/package.json +38 -5
package/README.md
CHANGED
|
@@ -1,56 +1,28 @@
|
|
|
1
1
|
# @unknownncat/curve25519-node
|
|
2
2
|
|
|
3
|
+
> 🇺🇸 English version: [README.en.md](./README.en.md)
|
|
4
|
+
|
|
5
|
+
Implementação sem dependências de runtime de:
|
|
6
|
+
|
|
7
|
+
- X25519 + Ed25519 (modo moderno via OpenSSL em `node:crypto`)
|
|
8
|
+
- X25519 + Ed25519 (modo moderno opcional via WASM)
|
|
9
|
+
- axlsign legado (modo opcional via WASM, compatível com `curve25519-js`)
|
|
10
|
+
|
|
3
11
|
[](https://www.npmjs.com/package/@unknownncat/curve25519-node)
|
|
4
|
-
[](https://www.npmjs.com/package/@unknownncat/curve25519-node)
|
|
5
|
-
[](./dist/index.d.ts)
|
|
6
|
-
[](./LICENSE)
|
|
7
12
|
[](https://nodejs.org/)
|
|
13
|
+
[](./dist/index.d.ts)
|
|
8
14
|

|
|
9
15
|

|
|
16
|
+
[](./LICENSE)
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- **TypeScript:** `strict`, **ESM-first**
|
|
16
|
-
- **Compat:** ESM + CJS (`import` e `require`)
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Why
|
|
21
|
-
|
|
22
|
-
O projeto original ([curve25519-js](https://github.com/harveyconnor/curve25519-js)) faz aritmética de campo manual em `Float64Array` (derivado de TweetNaCl) para Curve25519/Ed25519.
|
|
23
|
-
|
|
24
|
-
Este pacote troca isso por primitivas nativas do OpenSSL (via `node:crypto`), com foco em:
|
|
25
|
-
|
|
26
|
-
- **segurança** por implementação consolidada
|
|
27
|
-
- **performance** melhor em Node moderno
|
|
28
|
-
- **API pequena** e **bem tipada**
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## Compatibility notes (importante)
|
|
33
|
-
|
|
34
|
-
Este pacote **não replica o esquema `axlsign`** do `curve25519-js`.
|
|
35
|
-
|
|
36
|
-
Aqui a API é **padrão moderna**:
|
|
37
|
-
|
|
38
|
-
- acordo de chave: **X25519**
|
|
39
|
-
- assinatura: **Ed25519**
|
|
40
|
-
|
|
41
|
-
Consequências:
|
|
42
|
-
|
|
43
|
-
- Chaves de X25519 e Ed25519 são **diferentes** (public keys diferentes).
|
|
44
|
-
- `sign`/`verify` usam chaves **Ed25519**.
|
|
45
|
-
- Conversão X25519 public key ↔ Ed25519 public key **não é exposta** por `node:crypto`.
|
|
46
|
-
- `opt_random` (64 bytes) do legado **não é suportado** em Ed25519 com `node:crypto`.
|
|
47
|
-
- O compat layer aceita um 3º argumento apenas por compatibilidade de chamada, mas **sempre lança erro** se ele for fornecido.
|
|
48
|
-
- As assinaturas Ed25519 aqui são determinísticas (comportamento padrão do OpenSSL para Ed25519).
|
|
49
|
-
- No legado, `openMessage` pode alterar o `signedMsg` recebido (bit de sinal); nesta implementação, as entradas não são modificadas.
|
|
18
|
+
- Node: `>= 20`
|
|
19
|
+
- Dependências de runtime: `0`
|
|
20
|
+
- TypeScript: `strict`
|
|
21
|
+
- Formatos de módulo: ESM + CJS
|
|
50
22
|
|
|
51
23
|
---
|
|
52
24
|
|
|
53
|
-
##
|
|
25
|
+
## Instalação
|
|
54
26
|
|
|
55
27
|
```bash
|
|
56
28
|
npm i @unknownncat/curve25519-node
|
|
@@ -58,152 +30,266 @@ npm i @unknownncat/curve25519-node
|
|
|
58
30
|
|
|
59
31
|
---
|
|
60
32
|
|
|
61
|
-
##
|
|
62
|
-
|
|
63
|
-
### ESM (TypeScript / Node moderno)
|
|
33
|
+
## Uso Rápido
|
|
64
34
|
|
|
65
35
|
```ts
|
|
66
|
-
import {
|
|
67
|
-
|
|
68
|
-
x25519,
|
|
69
|
-
ed25519,
|
|
70
|
-
sign, // compat top-level (Ed25519)
|
|
71
|
-
verify, // compat top-level (Ed25519)
|
|
72
|
-
} from "@unknownncat/curve25519-node";
|
|
36
|
+
import { randomBytes } from "node:crypto";
|
|
37
|
+
import { asBytes32, x25519, ed25519 } from "@unknownncat/curve25519-node";
|
|
73
38
|
|
|
74
|
-
const aliceSeed = asBytes32(
|
|
75
|
-
const bobSeed = asBytes32(
|
|
39
|
+
const aliceSeed = asBytes32(randomBytes(32));
|
|
40
|
+
const bobSeed = asBytes32(randomBytes(32));
|
|
76
41
|
|
|
77
42
|
const aliceX = x25519.generateKeyPair(aliceSeed);
|
|
78
43
|
const bobX = x25519.generateKeyPair(bobSeed);
|
|
79
44
|
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
const signerSeed = asBytes32(crypto.getRandomValues(new Uint8Array(32)));
|
|
85
|
-
const ed = ed25519.generateKeyPair(signerSeed);
|
|
45
|
+
const segredo1 = x25519.sharedKey(aliceX.private, bobX.public);
|
|
46
|
+
const segredo2 = x25519.sharedKey(bobX.private, aliceX.public);
|
|
47
|
+
// segredo1 === segredo2
|
|
86
48
|
|
|
49
|
+
const signerSeed = asBytes32(randomBytes(32));
|
|
50
|
+
const signer = ed25519.generateKeyPair(signerSeed);
|
|
87
51
|
const msg = new TextEncoder().encode("hello");
|
|
88
52
|
|
|
89
53
|
const sig = ed25519.sign(signerSeed, msg);
|
|
90
|
-
const ok = verify(
|
|
91
|
-
|
|
92
|
-
const signedMsg = ed25519.signMessage(signerSeed, msg);
|
|
93
|
-
const opened = ed25519.openMessage(ed.public, signedMsg);
|
|
54
|
+
const ok = ed25519.verify(signer.public, msg, sig);
|
|
94
55
|
```
|
|
95
56
|
|
|
96
|
-
|
|
57
|
+
CommonJS:
|
|
97
58
|
|
|
98
59
|
```js
|
|
99
60
|
const { x25519, ed25519, asBytes32 } = require("@unknownncat/curve25519-node");
|
|
61
|
+
```
|
|
100
62
|
|
|
101
|
-
|
|
102
|
-
const kp = x25519.generateKeyPair(seed);
|
|
63
|
+
Legado axlsign via WASM:
|
|
103
64
|
|
|
104
|
-
|
|
65
|
+
```ts
|
|
66
|
+
import { asBytes32, axlsign } from "@unknownncat/curve25519-node";
|
|
67
|
+
|
|
68
|
+
const seed = asBytes32(new Uint8Array(32));
|
|
69
|
+
const kp = axlsign.generateKeyPair(seed); // X25519 keypair compatível com curve25519-js
|
|
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);
|
|
105
72
|
```
|
|
106
73
|
|
|
107
|
-
|
|
74
|
+
Moderno via WASM (`wasm`):
|
|
108
75
|
|
|
109
76
|
```ts
|
|
110
|
-
import {
|
|
111
|
-
|
|
112
|
-
|
|
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);
|
|
113
86
|
```
|
|
114
87
|
|
|
115
88
|
---
|
|
116
89
|
|
|
117
90
|
## API
|
|
118
91
|
|
|
119
|
-
###
|
|
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` (rejeita segredo all-zero)
|
|
101
|
+
- `sharedKeyStrictFromKeyObjects(privateKey: KeyObject, publicKey: KeyObject): Bytes32` (rejeita segredo all-zero)
|
|
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` (`assinatura || mensagem`)
|
|
117
|
+
- `openMessage(publicKey32: Bytes32, signedMsg: Uint8Array): Uint8Array | null`
|
|
118
|
+
|
|
119
|
+
### `axlsign` (compatibilidade legado, 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` (modo moderno opcional, via WASM)
|
|
120
130
|
|
|
131
|
+
`wasm.x25519`:
|
|
132
|
+
|
|
133
|
+
- `createPrivateKeyObject(secretKey32: Bytes32): WasmX25519PrivateKeyObject`
|
|
134
|
+
- `createPublicKeyObject(publicKey32: Bytes32): WasmX25519PublicKeyObject`
|
|
135
|
+
- `publicKeyFromPrivateKeyObject(privateKey: WasmX25519PrivateKeyObject): Bytes32`
|
|
121
136
|
- `publicKey(secretKey32: Bytes32): Bytes32`
|
|
137
|
+
- `sharedKeyFromKeyObjects(privateKey: WasmX25519PrivateKeyObject, publicKey: WasmX25519PublicKeyObject): Bytes32`
|
|
122
138
|
- `sharedKey(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32`
|
|
139
|
+
- `sharedKeyStrict(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32` (rejeita segredo all-zero)
|
|
140
|
+
- `sharedKeyStrictFromKeyObjects(privateKey: WasmX25519PrivateKeyObject, publicKey: WasmX25519PublicKeyObject): Bytes32` (rejeita segredo all-zero)
|
|
141
|
+
- `isAllZero32(bytes32: Bytes32): boolean`
|
|
123
142
|
- `generateKeyPair(seed32: Bytes32): { public: Bytes32; private: Bytes32 }`
|
|
124
143
|
|
|
125
|
-
|
|
144
|
+
`wasm.ed25519`:
|
|
126
145
|
|
|
146
|
+
- `createPrivateKeyObject(secretSeed32: Bytes32): WasmEd25519PrivateKeyObject`
|
|
147
|
+
- `createPublicKeyObject(publicKey32: Bytes32): WasmEd25519PublicKeyObject`
|
|
148
|
+
- `publicKeyFromPrivateKeyObject(privateKey: WasmEd25519PrivateKeyObject): Bytes32`
|
|
127
149
|
- `publicKey(secretSeed32: Bytes32): Bytes32`
|
|
128
150
|
- `generateKeyPair(seed32: Bytes32): { public: Bytes32; private: Bytes32 }`
|
|
129
151
|
- `sign(secretSeed32: Bytes32, msg: Uint8Array): Bytes64`
|
|
152
|
+
- `signWithPrivateKey(privateKey: WasmEd25519PrivateKeyObject, msg: Uint8Array): Bytes64`
|
|
130
153
|
- `verify(publicKey32: Bytes32, msg: Uint8Array, signature64: Bytes64): boolean`
|
|
154
|
+
- `verifyWithPublicKey(publicKey: WasmEd25519PublicKeyObject, msg: Uint8Array, signature64: Bytes64): boolean`
|
|
131
155
|
- `signMessage(secretSeed32: Bytes32, msg: Uint8Array): Uint8Array`
|
|
132
156
|
- `openMessage(publicKey32: Bytes32, signedMsg: Uint8Array): Uint8Array | null`
|
|
133
157
|
|
|
134
|
-
###
|
|
158
|
+
### Aliases de compatibilidade (top-level)
|
|
135
159
|
|
|
136
160
|
- `sharedKey = x25519.sharedKey`
|
|
161
|
+
- `sharedKeyStrict = x25519.sharedKeyStrict`
|
|
137
162
|
- `generateKeyPair = x25519.generateKeyPair`
|
|
138
|
-
- `sign`, `verify`, `signMessage`, `openMessage` (
|
|
163
|
+
- `sign`, `verify`, `signMessage`, `openMessage` (semântica Ed25519)
|
|
139
164
|
- `generateKeyPairX25519`, `generateKeyPairEd25519`
|
|
140
165
|
|
|
141
|
-
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Notas de Compatibilidade
|
|
169
|
+
|
|
170
|
+
Este pacote suporta três modos:
|
|
171
|
+
|
|
172
|
+
- **moderno nativo (recomendado):** `x25519` + `ed25519` via `node:crypto`
|
|
173
|
+
- **moderno WASM (opcional):** namespace `wasm` (`wasm.x25519` + `wasm.ed25519`)
|
|
174
|
+
- **legado:** `axlsign` via WASM para compatibilidade com `curve25519-js`
|
|
175
|
+
|
|
176
|
+
| Recurso | `curve25519-js` | `curve25519-node` |
|
|
177
|
+
| ----------------------------------- | --------------- | -------------------------------------------- |
|
|
178
|
+
| Esquema de assinatura (moderno) | axlsign | Ed25519 (padrão) |
|
|
179
|
+
| Esquema moderno alternativo | não | Ed25519 via WASM (`wasm.ed25519`) |
|
|
180
|
+
| Esquema de assinatura (legado) | axlsign | axlsign (namespace `axlsign`) |
|
|
181
|
+
| Acordo de chave | X25519 | X25519 |
|
|
182
|
+
| Acordo moderno alternativo | não | X25519 via WASM (`wasm.x25519`) |
|
|
183
|
+
| Mesma chave para assinatura + ECDH | sim | apenas no namespace `axlsign` |
|
|
184
|
+
| `opt_random` nas APIs de assinatura | sim | sim no `axlsign`, não no top-level/`ed25519` |
|
|
185
|
+
| Backend OpenSSL | não | sim |
|
|
186
|
+
|
|
187
|
+
Importante:
|
|
188
|
+
|
|
189
|
+
- Chaves públicas X25519 e Ed25519 são diferentes.
|
|
190
|
+
- Para fluxos de protocolo mais rígidos (estilo Signal), prefira `sharedKeyStrict` para rejeitar segredo compartilhado all-zero.
|
|
191
|
+
- `node:crypto` não expõe API para converter public key X25519 ↔ Ed25519.
|
|
192
|
+
- Top-level `sign`/`signMessage` e namespace `ed25519` continuam com semântica Ed25519 e rejeitam `opt_random`.
|
|
193
|
+
- Para compatibilidade com `curve25519-js` (incluindo `opt_random`), use o namespace `axlsign`.
|
|
194
|
+
- Assinaturas Ed25519 continuam determinísticas (comportamento padrão do OpenSSL).
|
|
195
|
+
- Os módulos WASM (`axlsign` e `wasm`) são carregados sob demanda na primeira chamada (importar apenas `x25519`/`ed25519` não inicializa WASM).
|
|
142
196
|
|
|
143
197
|
---
|
|
144
198
|
|
|
145
|
-
##
|
|
199
|
+
## Motivação
|
|
200
|
+
|
|
201
|
+
O `curve25519-js` é um projeto importante, mas usa aritmética de campo manual em JS (`Float64Array`, estilo TweetNaCl).
|
|
202
|
+
|
|
203
|
+
Este pacote foca em Node moderno com primitivas do OpenSSL:
|
|
204
|
+
|
|
205
|
+
- caminho de implementação mais seguro
|
|
206
|
+
- melhor desempenho em Node >= 20
|
|
207
|
+
- API menor e explícita
|
|
208
|
+
- tipagem forte com zero dependências de runtime
|
|
209
|
+
|
|
210
|
+
Além disso:
|
|
211
|
+
|
|
212
|
+
- o namespace `axlsign` via WASM permite migração progressiva de código legado;
|
|
213
|
+
- o namespace `wasm` via WASM oferece uma alternativa moderna sem dependência de `node:crypto` no caminho criptográfico.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Tipos Branded
|
|
146
218
|
|
|
147
219
|
- `Bytes32`
|
|
148
220
|
- `Bytes64`
|
|
149
221
|
|
|
150
|
-
Helpers:
|
|
222
|
+
Helpers (validam sem copiar):
|
|
151
223
|
|
|
152
224
|
- `asBytes32(u8)`
|
|
153
225
|
- `asBytes64(u8)`
|
|
154
226
|
|
|
155
|
-
✅ Os helpers validam tamanho e retornam o **mesmo objeto** (sem cópia).
|
|
156
|
-
|
|
157
227
|
---
|
|
158
228
|
|
|
159
|
-
##
|
|
229
|
+
## Mapa de RFCs (uso no projeto)
|
|
160
230
|
|
|
161
|
-
|
|
231
|
+
| RFC | Seções usadas | Uso no projeto | Onde no código |
|
|
232
|
+
| --------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
|
233
|
+
| RFC 7748 (X25519) | Seção 5 (`The X25519 and X448 Functions`) | Regras de clamping/decoding do escalar e comportamento da função X25519 (zera 3 bits baixos, zera bit mais alto, seta o segundo bit mais alto). | `src/x25519.ts` |
|
|
234
|
+
| RFC 7748 (X25519) | Seção 5.2 (`Test Vectors`), Seção 6.1 (`Diffie-Hellman / Curve25519`) | Vetores oficiais para validação de interoperabilidade e corretude. | `test/x25519.test.mjs` |
|
|
235
|
+
| RFC 8032 (Ed25519) | Seção 5.1.5 (`Key Generation`), 5.1.6 (`Sign`), 5.1.7 (`Verify`) | Semântica de keygen/sign/verify Ed25519 (executada por OpenSSL via `node:crypto`). | `src/ed25519.ts` |
|
|
236
|
+
| RFC 8032 (Ed25519) | Seção 7.1 (`Test Vectors for Ed25519`) | Vetores determinísticos para validação de chave pública e assinatura. | `test/ed25519.test.mjs` |
|
|
237
|
+
| RFC 8410 (X25519/Ed25519 em PKIX) | Seção 3 (identificadores de algoritmo), Seção 4 (`Subject Public Key Fields`), Seção 7 (`Private Key Format`) | Estrutura DER para import/export de chaves raw de 32 bytes em SPKI/PKCS#8 com OIDs de X25519 e Ed25519. | `src/internal/der.ts` |
|
|
162
238
|
|
|
163
|
-
|
|
164
|
-
- X25519 SPKI prefix: `302a300506032b656e032100`
|
|
165
|
-
- Ed25519 PKCS#8 prefix: `302e020100300506032b657004220420`
|
|
166
|
-
- Ed25519 SPKI prefix: `302a300506032b6570032100`
|
|
239
|
+
Referências indiretas por estrutura ASN.1/PKIX:
|
|
167
240
|
|
|
168
|
-
|
|
241
|
+
- RFC 5958 (OneAsymmetricKey / família PKCS#8)
|
|
242
|
+
- RFC 5280, Seção 4.1.2.7 (`Subject Public Key Info`)
|
|
169
243
|
|
|
170
|
-
|
|
244
|
+
Observações:
|
|
171
245
|
|
|
172
|
-
|
|
246
|
+
- O projeto não reimplementa aritmética de curva em JS; as operações criptográficas usam OpenSSL via `node:crypto`.
|
|
247
|
+
- A suíte de testes cobre vetores oficiais do RFC 7748 e RFC 8032.
|
|
173
248
|
|
|
174
|
-
|
|
175
|
-
- `Uint8Array` → `Buffer` usa view zero-copy:
|
|
176
|
-
- `Buffer.from(u8.buffer, u8.byteOffset, u8.byteLength)`
|
|
249
|
+
Rodar testes:
|
|
177
250
|
|
|
178
|
-
|
|
179
|
-
|
|
251
|
+
```bash
|
|
252
|
+
npm test
|
|
253
|
+
```
|
|
180
254
|
|
|
181
255
|
---
|
|
182
256
|
|
|
183
|
-
##
|
|
257
|
+
## Detalhes Técnicos (DER / RFC 8410)
|
|
184
258
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
259
|
+
Chaves raw de 32 bytes são importadas/exportadas com prefixos fixos:
|
|
260
|
+
|
|
261
|
+
- X25519 PKCS#8: `302e020100300506032b656e04220420`
|
|
262
|
+
- X25519 SPKI: `302a300506032b656e032100`
|
|
263
|
+
- Ed25519 PKCS#8: `302e020100300506032b657004220420`
|
|
264
|
+
- Ed25519 SPKI: `302a300506032b6570032100`
|
|
265
|
+
|
|
266
|
+
Notas de implementação:
|
|
267
|
+
|
|
268
|
+
- buffers prealocados + `.set`
|
|
269
|
+
- views zero-copy de `Uint8Array` quando seguro
|
|
270
|
+
- sem `Buffer.concat` em hot path
|
|
188
271
|
|
|
189
272
|
---
|
|
190
273
|
|
|
191
|
-
##
|
|
274
|
+
## Notas de Performance
|
|
192
275
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
276
|
+
- Evita cópias desnecessárias de bytes nos caminhos críticos.
|
|
277
|
+
- `signMessage` monta `assinatura || mensagem` com um único `Uint8Array` prealocado.
|
|
278
|
+
- Para throughput máximo em loops longos, use os helpers de `KeyObject` (`create*KeyObject`, `*FromKeyObjects`) para reduzir overhead de parse ASN.1.
|
|
196
279
|
|
|
197
|
-
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Notas de Segurança
|
|
198
283
|
|
|
199
|
-
-
|
|
200
|
-
-
|
|
284
|
+
- validação estrita de tipo/tamanho nas APIs públicas
|
|
285
|
+
- sem log de segredos
|
|
286
|
+
- `timingSafeEqual` em comparações internas de tamanho fixo quando necessário
|
|
201
287
|
|
|
202
288
|
---
|
|
203
289
|
|
|
204
290
|
## Benchmarks
|
|
205
291
|
|
|
206
|
-
|
|
292
|
+
A suíte de benchmark fica isolada em `bench/` (subprojeto separado) e compara com `curve25519-js`.
|
|
207
293
|
|
|
208
294
|
```bash
|
|
209
295
|
npm run build
|
|
@@ -212,14 +298,125 @@ npm install
|
|
|
212
298
|
npm run bench
|
|
213
299
|
```
|
|
214
300
|
|
|
301
|
+
### Snapshot real de benchmark (`npm run bench:ci`) no GitHub Codespaces
|
|
302
|
+
|
|
303
|
+
Comando:
|
|
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
|
+
Ambiente:
|
|
310
|
+
|
|
311
|
+
- Node: `v24.11.1`
|
|
312
|
+
- OpenSSL: `3.5.4`
|
|
313
|
+
- CPU: `AMD EPYC 7763 64-Core Processor`
|
|
314
|
+
- Cores lógicos: `4`
|
|
315
|
+
- Vetores: `64`
|
|
316
|
+
|
|
317
|
+
### Tabela 1 - API moderna (`x25519` + `ed25519`)
|
|
318
|
+
|
|
319
|
+
`sign`/`verify` abaixo comparam throughput de API, não equivalência criptográfica (Ed25519 vs axlsign legado).
|
|
320
|
+
|
|
321
|
+
| Operação | Moderno raw | Legado raw (`curve25519-js`) | Speedup raw | Moderno cached | Legado cached (`curve25519-js`) | Speedup cached |
|
|
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
|
+
### Tabela 2 - Compatibilidade `axlsign` (equivalente ao `curve25519-js`)
|
|
333
|
+
|
|
334
|
+
Aqui a comparação é de mesmo esquema criptográfico (equivalência + throughput).
|
|
335
|
+
|
|
336
|
+
| Operação | Moderno raw | Legado raw (`curve25519-js`) | Speedup raw | Moderno cached | Legado cached (`curve25519-js`) | Speedup cached |
|
|
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
|
+
Notas:
|
|
352
|
+
|
|
353
|
+
- `raw` inclui custo fim-a-fim da API.
|
|
354
|
+
- `cached` reduz overhead de setup para evidenciar melhor o throughput criptográfico.
|
|
355
|
+
- Fonte dos números: saída JSON de `bench:ci` (`results/bench-results.json`).
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Build dos namespaces WASM (`axlsign` e `wasm`)
|
|
360
|
+
|
|
361
|
+
No pacote publicado no npm, os artefatos WASM já vêm prontos em `dist/`.
|
|
362
|
+
|
|
363
|
+
Para buildar a partir do código-fonte, você precisa:
|
|
364
|
+
|
|
365
|
+
- Rust toolchain
|
|
366
|
+
- `wasm-pack` instalado
|
|
367
|
+
|
|
368
|
+
Com isso, `npm run build` executa:
|
|
369
|
+
|
|
370
|
+
1. `wasm-pack build` (`wasm/axlsign`)
|
|
371
|
+
2. `wasm-pack build` (`wasm/curve25519-wasm`)
|
|
372
|
+
3. `tsc` ESM + CJS
|
|
373
|
+
4. cópia dos artefatos WASM para `dist/internal/axlsign-wasm` e `dist/internal/curve25519-wasm`
|
|
374
|
+
|
|
375
|
+
Referência dos crates Rust: [wasm/README.md](./wasm/README.md)
|
|
376
|
+
|
|
215
377
|
---
|
|
216
378
|
|
|
217
|
-
##
|
|
379
|
+
## Contribuição
|
|
218
380
|
|
|
219
|
-
|
|
381
|
+
- Guia: [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
382
|
+
- Código de conduta: [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
|
|
383
|
+
- Segurança: [SECURITY.md](./SECURITY.md)
|
|
384
|
+
|
|
385
|
+
Validação local completa:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
npm run ci
|
|
389
|
+
```
|
|
220
390
|
|
|
221
391
|
---
|
|
222
392
|
|
|
223
|
-
##
|
|
393
|
+
## Licença
|
|
394
|
+
|
|
395
|
+
MIT
|
|
396
|
+
|
|
397
|
+
Documentos complementares:
|
|
398
|
+
|
|
399
|
+
- [NOTICE.md](./NOTICE.md) (aviso oficial de terceiros)
|
|
400
|
+
- [THIRD_PARTY_NOTICE.md](./THIRD_PARTY_NOTICE.md) e [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) (aliases de compatibilidade)
|
|
401
|
+
- [SECURITY.md](./SECURITY.md) (política de segurança e reporte de vulnerabilidades)
|
|
402
|
+
|
|
403
|
+
---
|
|
224
404
|
|
|
225
|
-
|
|
405
|
+
## Créditos
|
|
406
|
+
|
|
407
|
+
- [curve25519-js](https://github.com/harveyconnor/curve25519-js) (Harvey Connor, Dmitry Chestnykh)
|
|
408
|
+
- [TweetNaCl.js](https://tweetnacl.js.org/)
|
|
409
|
+
- Trevor Perrin, ideia de assinaturas Curve25519: <https://moderncrypto.org/mail-archive/curves/2014/000205.html>
|
|
410
|
+
- [Documentação Node.js `crypto`](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)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | --------- |
|
|
7
|
+
| 2.x | Yes |
|
|
8
|
+
| < 2.0.0 | No |
|
|
9
|
+
|
|
10
|
+
## Reporting a Vulnerability
|
|
11
|
+
|
|
12
|
+
Please use GitHub private vulnerability reporting whenever possible:
|
|
13
|
+
|
|
14
|
+
1. Go to the repository `Security` tab.
|
|
15
|
+
2. Click `Report a vulnerability`.
|
|
16
|
+
3. Submit impact details and a minimal proof-of-concept.
|
|
17
|
+
|
|
18
|
+
If private reporting is not available, open a public issue without sensitive details and request private contact.
|
|
19
|
+
|
|
20
|
+
## Scope
|
|
21
|
+
|
|
22
|
+
- Cryptographic flaws, incorrect input validation, and integrity/confidentiality issues are high priority.
|
|
23
|
+
- Include package version, runtime environment, and reproducible steps.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Bytes32, Bytes64, KeyPair32 } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Derives an axlsign-compatible public key (Montgomery/X25519 format).
|
|
4
|
+
*/
|
|
5
|
+
export declare function publicKey(secretKey32: Bytes32): Bytes32;
|
|
6
|
+
/**
|
|
7
|
+
* Computes an axlsign-compatible X25519 shared key.
|
|
8
|
+
*/
|
|
9
|
+
export declare function sharedKey(secretKey32: Bytes32, publicKey32: Bytes32): Bytes32;
|
|
10
|
+
/**
|
|
11
|
+
* Generates an axlsign-compatible key pair from a 32-byte seed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateKeyPair(seed32: Bytes32): KeyPair32;
|
|
14
|
+
/**
|
|
15
|
+
* Detached axlsign signature using X25519-format secret key.
|
|
16
|
+
* opt_random (64 bytes) enables randomized signing as in curve25519-js.
|
|
17
|
+
*/
|
|
18
|
+
export declare function sign(secretKey32: Bytes32, msg: Uint8Array, opt_random?: Uint8Array): Bytes64;
|
|
19
|
+
/**
|
|
20
|
+
* Verifies detached axlsign signature.
|
|
21
|
+
*/
|
|
22
|
+
export declare function verify(publicKey32: Bytes32, msg: Uint8Array, signature64: Bytes64): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Returns signature || message (axlsign mode).
|
|
25
|
+
*/
|
|
26
|
+
export declare function signMessage(secretKey32: Bytes32, msg: Uint8Array, opt_random?: Uint8Array): Uint8Array;
|
|
27
|
+
/**
|
|
28
|
+
* Verifies signature || message and returns original message on success.
|
|
29
|
+
*/
|
|
30
|
+
export declare function openMessage(publicKey32: Bytes32, signedMsg: Uint8Array): Uint8Array | null;
|
|
31
|
+
//# sourceMappingURL=axlsign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"axlsign.d.ts","sourceRoot":"","sources":["../src/axlsign.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAqD9D;;GAEG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAIvD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAK7E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAQ1D;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,OAAO,CAU5F;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAK3F;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,OAAO,EACpB,GAAG,EAAE,UAAU,EACf,UAAU,CAAC,EAAE,UAAU,GACtB,UAAU,CAUZ;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAe1F"}
|