pqc-vault 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/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # pqc-vault
2
+
3
+ > 🔐 Cifrado y firmas **post-cuánticas híbridas** para Node.js, con una API
4
+ > sencilla estilo libsodium. Protege datos **hoy** contra el ataque
5
+ > "guardar ahora, descifrar después" (_harvest-now, decrypt-later_).
6
+ >
7
+ > 🔐 **Hybrid post-quantum** encryption and signatures for Node.js, with a
8
+ > simple libsodium-style API. Protect data **today** against
9
+ > _harvest-now, decrypt-later_.
10
+
11
+ **ML-KEM-768 + ML-DSA-65** · híbrido con X25519 / AES-256-GCM · 130/130 NIST ACVP test vectors
12
+
13
+ ---
14
+
15
+ ## 📦 Instalación / Install
16
+
17
+ ```bash
18
+ npm install pqc-vault
19
+ ```
20
+
21
+ No compila nada en tu máquina: trae binarios listos para Linux, macOS y Windows.
22
+ Funciona con ESM (`import`) y CommonJS (`require`), Node 18+.
23
+
24
+ _No compilation on your machine: prebuilt binaries for Linux, macOS and Windows.
25
+ Works with ESM (`import`) and CommonJS (`require`), Node 18+._
26
+
27
+ ---
28
+
29
+ # 🇪🇸 Español
30
+
31
+ Todas las funciones aceptan un `Buffer` o un `Uint8Array` y devuelven un `Buffer`.
32
+ Como el cifrado trabaja con bytes, conviertes tu texto/objeto a bytes antes y
33
+ después.
34
+
35
+ ### 1. Cifrar y descifrar un mensaje
36
+
37
+ ```js
38
+ import { generateKeypair, seal, open } from "pqc-vault";
39
+
40
+ // Creas un par de claves: la pública para cifrar, la secreta para descifrar.
41
+ const { publicKey, secretKey } = await generateKeypair();
42
+
43
+ // Conviertes tu texto a bytes y lo ciframos con la clave pública.
44
+ const mensaje = Buffer.from("Hola, dato sensible");
45
+ const cifrado = await seal(mensaje, publicKey);
46
+
47
+ // Solo quien tiene la clave secreta puede recuperarlo.
48
+ const descifrado = await open(cifrado, secretKey);
49
+
50
+ console.log(descifrado.toString()); // "Hola, dato sensible"
51
+ ```
52
+
53
+ ### 2. Cifrar un objeto (por ejemplo, datos de un cliente)
54
+
55
+ ```js
56
+ import { generateKeypair, seal, open } from "pqc-vault";
57
+
58
+ const { publicKey, secretKey } = await generateKeypair();
59
+
60
+ const cliente = { nombre: "Ada", iban: "ES91...", dni: "12345678Z" };
61
+
62
+ // objeto -> texto JSON -> bytes
63
+ const cifrado = await seal(Buffer.from(JSON.stringify(cliente)), publicKey);
64
+
65
+ // bytes -> texto JSON -> objeto
66
+ const recuperado = JSON.parse((await open(cifrado, secretKey)).toString());
67
+
68
+ console.log(recuperado.iban); // "ES91..."
69
+ ```
70
+
71
+ ### 3. Firmar y verificar
72
+
73
+ ```js
74
+ import { generateSigningKeypair, sign, verify } from "pqc-vault";
75
+
76
+ // Claves de firma (distintas de las de cifrado).
77
+ const { publicKey, secretKey } = await generateSigningKeypair();
78
+
79
+ const orden = Buffer.from("transferir 1000 EUR");
80
+
81
+ // Firmas con la clave secreta.
82
+ const firma = await sign(orden, secretKey);
83
+
84
+ // Cualquiera verifica con la clave pública.
85
+ console.log(await verify(orden, firma, publicKey)); // true
86
+
87
+ // Si el mensaje cambia, la verificación da false.
88
+ const ordenFalsa = Buffer.from("transferir 9999 EUR");
89
+ console.log(await verify(ordenFalsa, firma, publicKey)); // false
90
+ ```
91
+
92
+ ### ¿Por qué "híbrido"?
93
+
94
+ Combina criptografía clásica (**X25519**) y post-cuántica (**ML-KEM-768**). Un
95
+ atacante tendría que romper **las dos** para leer tus datos. Así te proteges
96
+ incluso si en el futuro un ordenador cuántico rompe la criptografía clásica.
97
+
98
+ ### Errores
99
+
100
+ Si `open` falla (clave equivocada o datos manipulados) lanza un error genérico,
101
+ **sin** decir el motivo exacto (para no dar pistas a un atacante). `verify`
102
+ devuelve `false` ante una firma inválida.
103
+
104
+ ---
105
+
106
+ # 🇬🇧 English
107
+
108
+ Every function accepts a `Buffer` or a `Uint8Array` and returns a `Buffer`. Since
109
+ encryption works on bytes, convert your text/object to bytes before and after.
110
+
111
+ ### 1. Encrypt and decrypt a message
112
+
113
+ ```js
114
+ import { generateKeypair, seal, open } from "pqc-vault";
115
+
116
+ // Make a key pair: public key to encrypt, secret key to decrypt.
117
+ const { publicKey, secretKey } = await generateKeypair();
118
+
119
+ // Turn your text into bytes and encrypt with the public key.
120
+ const message = Buffer.from("Hello, sensitive data");
121
+ const encrypted = await seal(message, publicKey);
122
+
123
+ // Only the holder of the secret key can recover it.
124
+ const decrypted = await open(encrypted, secretKey);
125
+
126
+ console.log(decrypted.toString()); // "Hello, sensitive data"
127
+ ```
128
+
129
+ ### 2. Encrypt an object (e.g. customer data)
130
+
131
+ ```js
132
+ import { generateKeypair, seal, open } from "pqc-vault";
133
+
134
+ const { publicKey, secretKey } = await generateKeypair();
135
+
136
+ const customer = { name: "Ada", iban: "ES91...", dni: "12345678Z" };
137
+
138
+ // object -> JSON text -> bytes
139
+ const encrypted = await seal(Buffer.from(JSON.stringify(customer)), publicKey);
140
+
141
+ // bytes -> JSON text -> object
142
+ const recovered = JSON.parse((await open(encrypted, secretKey)).toString());
143
+
144
+ console.log(recovered.iban); // "ES91..."
145
+ ```
146
+
147
+ ### 3. Sign and verify
148
+
149
+ ```js
150
+ import { generateSigningKeypair, sign, verify } from "pqc-vault";
151
+
152
+ // Signing keys (different from the encryption keys).
153
+ const { publicKey, secretKey } = await generateSigningKeypair();
154
+
155
+ const order = Buffer.from("transfer 1000 EUR");
156
+
157
+ // Sign with the secret key.
158
+ const signature = await sign(order, secretKey);
159
+
160
+ // Anyone verifies with the public key.
161
+ console.log(await verify(order, signature, publicKey)); // true
162
+
163
+ // If the message changes, verification returns false.
164
+ const forged = Buffer.from("transfer 9999 EUR");
165
+ console.log(await verify(forged, signature, publicKey)); // false
166
+ ```
167
+
168
+ ### Why "hybrid"?
169
+
170
+ It combines classical (**X25519**) and post-quantum (**ML-KEM-768**)
171
+ cryptography. An attacker would have to break **both** to read your data — so you
172
+ stay protected even if a future quantum computer breaks classical crypto.
173
+
174
+ ### Errors
175
+
176
+ If `open` fails (wrong key or tampered data) it throws a generic error **without**
177
+ revealing the exact reason (so an attacker learns nothing). `verify` returns
178
+ `false` for an invalid signature.
179
+
180
+ ---
181
+
182
+ ## 📚 Más / More
183
+
184
+ - API completa, esquema híbrido detallado y ejemplo ejecutable en el
185
+ [repositorio](https://github.com/cjmont/pqc-vault).
186
+ - Reporte de vulnerabilidades: ver [SECURITY.md](https://github.com/cjmont/pqc-vault/blob/main/SECURITY.md).
187
+
188
+ ## License
189
+
190
+ [Apache-2.0](https://github.com/cjmont/pqc-vault/blob/main/LICENSE)
package/binding.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+ /** Generate a hybrid (X25519 + ML-KEM-768) keypair. */
4
+ export declare function generateKeypair(): Promise<{ publicKey: Buffer, secretKey: Buffer }>
5
+
6
+ /** Generate an ML-DSA-65 signing keypair. */
7
+ export declare function generateSigningKeypair(): Promise<{ publicKey: Buffer, secretKey: Buffer }>
8
+
9
+ /** A hybrid keypair returned to JS as `{ publicKey, secretKey }`. */
10
+ export interface KeyPair {
11
+ publicKey: Buffer
12
+ secretKey: Buffer
13
+ }
14
+
15
+ /** Decrypt a sealed message with the recipient secret key. Returns plaintext. */
16
+ export declare function open(ciphertext: Uint8Array, recipientSecretKey: Uint8Array): Promise<Buffer>
17
+
18
+ /** Hybrid-encrypt `data` to a recipient public key. Returns the sealed message. */
19
+ export declare function seal(data: Uint8Array, recipientPublicKey: Uint8Array): Promise<Buffer>
20
+
21
+ /** Sign a message with an ML-DSA-65 signing secret key. Returns the signature. */
22
+ export declare function sign(message: Uint8Array, secretKey: Uint8Array): Promise<Buffer>
23
+
24
+ /** Verify a signature over a message against an ML-DSA-65 public key. */
25
+ export declare function verify(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): Promise<boolean>
package/binding.js ADDED
@@ -0,0 +1,595 @@
1
+ // prettier-ignore
2
+ /* eslint-disable */
3
+ // @ts-nocheck
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ const { readFileSync } = require('fs')
7
+ let nativeBinding = null
8
+ const loadErrors = []
9
+
10
+ const isMusl = () => {
11
+ let musl = false
12
+ if (process.platform === 'linux') {
13
+ musl = isMuslFromFilesystem()
14
+ if (musl === null) {
15
+ musl = isMuslFromReport()
16
+ }
17
+ if (musl === null) {
18
+ musl = isMuslFromChildProcess()
19
+ }
20
+ }
21
+ return musl
22
+ }
23
+
24
+ const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
25
+
26
+ const isMuslFromFilesystem = () => {
27
+ try {
28
+ return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
29
+ } catch {
30
+ return null
31
+ }
32
+ }
33
+
34
+ const isMuslFromReport = () => {
35
+ let report = null
36
+ if (process.report && typeof process.report.getReport === 'function') {
37
+ process.report.excludeNetwork = true
38
+ report = process.report.getReport()
39
+ }
40
+ if (!report) {
41
+ return null
42
+ }
43
+ if (report.header && report.header.glibcVersionRuntime) {
44
+ return false
45
+ }
46
+ if (Array.isArray(report.sharedObjects)) {
47
+ if (report.sharedObjects.some(isFileMusl)) {
48
+ return true
49
+ }
50
+ }
51
+ return false
52
+ }
53
+
54
+ const isMuslFromChildProcess = () => {
55
+ try {
56
+ return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
57
+ } catch (e) {
58
+ // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
59
+ return false
60
+ }
61
+ }
62
+
63
+ function requireNative() {
64
+ if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
65
+ try {
66
+ return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
67
+ } catch (err) {
68
+ loadErrors.push(err)
69
+ }
70
+ } else if (process.platform === 'android') {
71
+ if (process.arch === 'arm64') {
72
+ try {
73
+ return require('./pqc-vault.android-arm64.node')
74
+ } catch (e) {
75
+ loadErrors.push(e)
76
+ }
77
+ try {
78
+ const binding = require('@post-quantum-cryptography/pqc-vault-android-arm64')
79
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-android-arm64/package.json').version
80
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
81
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
82
+ }
83
+ return binding
84
+ } catch (e) {
85
+ loadErrors.push(e)
86
+ }
87
+ } else if (process.arch === 'arm') {
88
+ try {
89
+ return require('./pqc-vault.android-arm-eabi.node')
90
+ } catch (e) {
91
+ loadErrors.push(e)
92
+ }
93
+ try {
94
+ const binding = require('@post-quantum-cryptography/pqc-vault-android-arm-eabi')
95
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-android-arm-eabi/package.json').version
96
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
97
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
98
+ }
99
+ return binding
100
+ } catch (e) {
101
+ loadErrors.push(e)
102
+ }
103
+ } else {
104
+ loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
105
+ }
106
+ } else if (process.platform === 'win32') {
107
+ if (process.arch === 'x64') {
108
+ if ((process.config && process.config.variables && process.config.variables.shlib_suffix === 'dll.a') || (process.config && process.config.variables && process.config.variables.node_target_type === 'shared_library')) {
109
+ try {
110
+ return require('./pqc-vault.win32-x64-gnu.node')
111
+ } catch (e) {
112
+ loadErrors.push(e)
113
+ }
114
+ try {
115
+ const binding = require('@post-quantum-cryptography/pqc-vault-win32-x64-gnu')
116
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-win32-x64-gnu/package.json').version
117
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
118
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
119
+ }
120
+ return binding
121
+ } catch (e) {
122
+ loadErrors.push(e)
123
+ }
124
+ } else {
125
+ try {
126
+ return require('./pqc-vault.win32-x64-msvc.node')
127
+ } catch (e) {
128
+ loadErrors.push(e)
129
+ }
130
+ try {
131
+ const binding = require('@post-quantum-cryptography/pqc-vault-win32-x64-msvc')
132
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-win32-x64-msvc/package.json').version
133
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
134
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
135
+ }
136
+ return binding
137
+ } catch (e) {
138
+ loadErrors.push(e)
139
+ }
140
+ }
141
+ } else if (process.arch === 'ia32') {
142
+ try {
143
+ return require('./pqc-vault.win32-ia32-msvc.node')
144
+ } catch (e) {
145
+ loadErrors.push(e)
146
+ }
147
+ try {
148
+ const binding = require('@post-quantum-cryptography/pqc-vault-win32-ia32-msvc')
149
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-win32-ia32-msvc/package.json').version
150
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
151
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
152
+ }
153
+ return binding
154
+ } catch (e) {
155
+ loadErrors.push(e)
156
+ }
157
+ } else if (process.arch === 'arm64') {
158
+ try {
159
+ return require('./pqc-vault.win32-arm64-msvc.node')
160
+ } catch (e) {
161
+ loadErrors.push(e)
162
+ }
163
+ try {
164
+ const binding = require('@post-quantum-cryptography/pqc-vault-win32-arm64-msvc')
165
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-win32-arm64-msvc/package.json').version
166
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
167
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
168
+ }
169
+ return binding
170
+ } catch (e) {
171
+ loadErrors.push(e)
172
+ }
173
+ } else {
174
+ loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
175
+ }
176
+ } else if (process.platform === 'darwin') {
177
+ try {
178
+ return require('./pqc-vault.darwin-universal.node')
179
+ } catch (e) {
180
+ loadErrors.push(e)
181
+ }
182
+ try {
183
+ const binding = require('@post-quantum-cryptography/pqc-vault-darwin-universal')
184
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-darwin-universal/package.json').version
185
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
186
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
187
+ }
188
+ return binding
189
+ } catch (e) {
190
+ loadErrors.push(e)
191
+ }
192
+ if (process.arch === 'x64') {
193
+ try {
194
+ return require('./pqc-vault.darwin-x64.node')
195
+ } catch (e) {
196
+ loadErrors.push(e)
197
+ }
198
+ try {
199
+ const binding = require('@post-quantum-cryptography/pqc-vault-darwin-x64')
200
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-darwin-x64/package.json').version
201
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
202
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
203
+ }
204
+ return binding
205
+ } catch (e) {
206
+ loadErrors.push(e)
207
+ }
208
+ } else if (process.arch === 'arm64') {
209
+ try {
210
+ return require('./pqc-vault.darwin-arm64.node')
211
+ } catch (e) {
212
+ loadErrors.push(e)
213
+ }
214
+ try {
215
+ const binding = require('@post-quantum-cryptography/pqc-vault-darwin-arm64')
216
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-darwin-arm64/package.json').version
217
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
218
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
219
+ }
220
+ return binding
221
+ } catch (e) {
222
+ loadErrors.push(e)
223
+ }
224
+ } else {
225
+ loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
226
+ }
227
+ } else if (process.platform === 'freebsd') {
228
+ if (process.arch === 'x64') {
229
+ try {
230
+ return require('./pqc-vault.freebsd-x64.node')
231
+ } catch (e) {
232
+ loadErrors.push(e)
233
+ }
234
+ try {
235
+ const binding = require('@post-quantum-cryptography/pqc-vault-freebsd-x64')
236
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-freebsd-x64/package.json').version
237
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
238
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
239
+ }
240
+ return binding
241
+ } catch (e) {
242
+ loadErrors.push(e)
243
+ }
244
+ } else if (process.arch === 'arm64') {
245
+ try {
246
+ return require('./pqc-vault.freebsd-arm64.node')
247
+ } catch (e) {
248
+ loadErrors.push(e)
249
+ }
250
+ try {
251
+ const binding = require('@post-quantum-cryptography/pqc-vault-freebsd-arm64')
252
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-freebsd-arm64/package.json').version
253
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
254
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
255
+ }
256
+ return binding
257
+ } catch (e) {
258
+ loadErrors.push(e)
259
+ }
260
+ } else {
261
+ loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
262
+ }
263
+ } else if (process.platform === 'linux') {
264
+ if (process.arch === 'x64') {
265
+ if (isMusl()) {
266
+ try {
267
+ return require('./pqc-vault.linux-x64-musl.node')
268
+ } catch (e) {
269
+ loadErrors.push(e)
270
+ }
271
+ try {
272
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-x64-musl')
273
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-x64-musl/package.json').version
274
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
275
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
276
+ }
277
+ return binding
278
+ } catch (e) {
279
+ loadErrors.push(e)
280
+ }
281
+ } else {
282
+ try {
283
+ return require('./pqc-vault.linux-x64-gnu.node')
284
+ } catch (e) {
285
+ loadErrors.push(e)
286
+ }
287
+ try {
288
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-x64-gnu')
289
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-x64-gnu/package.json').version
290
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
291
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
292
+ }
293
+ return binding
294
+ } catch (e) {
295
+ loadErrors.push(e)
296
+ }
297
+ }
298
+ } else if (process.arch === 'arm64') {
299
+ if (isMusl()) {
300
+ try {
301
+ return require('./pqc-vault.linux-arm64-musl.node')
302
+ } catch (e) {
303
+ loadErrors.push(e)
304
+ }
305
+ try {
306
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-arm64-musl')
307
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-arm64-musl/package.json').version
308
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
309
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
310
+ }
311
+ return binding
312
+ } catch (e) {
313
+ loadErrors.push(e)
314
+ }
315
+ } else {
316
+ try {
317
+ return require('./pqc-vault.linux-arm64-gnu.node')
318
+ } catch (e) {
319
+ loadErrors.push(e)
320
+ }
321
+ try {
322
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-arm64-gnu')
323
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-arm64-gnu/package.json').version
324
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
325
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
326
+ }
327
+ return binding
328
+ } catch (e) {
329
+ loadErrors.push(e)
330
+ }
331
+ }
332
+ } else if (process.arch === 'arm') {
333
+ if (isMusl()) {
334
+ try {
335
+ return require('./pqc-vault.linux-arm-musleabihf.node')
336
+ } catch (e) {
337
+ loadErrors.push(e)
338
+ }
339
+ try {
340
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-arm-musleabihf')
341
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-arm-musleabihf/package.json').version
342
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
343
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
344
+ }
345
+ return binding
346
+ } catch (e) {
347
+ loadErrors.push(e)
348
+ }
349
+ } else {
350
+ try {
351
+ return require('./pqc-vault.linux-arm-gnueabihf.node')
352
+ } catch (e) {
353
+ loadErrors.push(e)
354
+ }
355
+ try {
356
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-arm-gnueabihf')
357
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-arm-gnueabihf/package.json').version
358
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
359
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
360
+ }
361
+ return binding
362
+ } catch (e) {
363
+ loadErrors.push(e)
364
+ }
365
+ }
366
+ } else if (process.arch === 'loong64') {
367
+ if (isMusl()) {
368
+ try {
369
+ return require('./pqc-vault.linux-loong64-musl.node')
370
+ } catch (e) {
371
+ loadErrors.push(e)
372
+ }
373
+ try {
374
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-loong64-musl')
375
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-loong64-musl/package.json').version
376
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
377
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
378
+ }
379
+ return binding
380
+ } catch (e) {
381
+ loadErrors.push(e)
382
+ }
383
+ } else {
384
+ try {
385
+ return require('./pqc-vault.linux-loong64-gnu.node')
386
+ } catch (e) {
387
+ loadErrors.push(e)
388
+ }
389
+ try {
390
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-loong64-gnu')
391
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-loong64-gnu/package.json').version
392
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
393
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
394
+ }
395
+ return binding
396
+ } catch (e) {
397
+ loadErrors.push(e)
398
+ }
399
+ }
400
+ } else if (process.arch === 'riscv64') {
401
+ if (isMusl()) {
402
+ try {
403
+ return require('./pqc-vault.linux-riscv64-musl.node')
404
+ } catch (e) {
405
+ loadErrors.push(e)
406
+ }
407
+ try {
408
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-riscv64-musl')
409
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-riscv64-musl/package.json').version
410
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
411
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
412
+ }
413
+ return binding
414
+ } catch (e) {
415
+ loadErrors.push(e)
416
+ }
417
+ } else {
418
+ try {
419
+ return require('./pqc-vault.linux-riscv64-gnu.node')
420
+ } catch (e) {
421
+ loadErrors.push(e)
422
+ }
423
+ try {
424
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-riscv64-gnu')
425
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-riscv64-gnu/package.json').version
426
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
427
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
428
+ }
429
+ return binding
430
+ } catch (e) {
431
+ loadErrors.push(e)
432
+ }
433
+ }
434
+ } else if (process.arch === 'ppc64') {
435
+ try {
436
+ return require('./pqc-vault.linux-ppc64-gnu.node')
437
+ } catch (e) {
438
+ loadErrors.push(e)
439
+ }
440
+ try {
441
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-ppc64-gnu')
442
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-ppc64-gnu/package.json').version
443
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
444
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
445
+ }
446
+ return binding
447
+ } catch (e) {
448
+ loadErrors.push(e)
449
+ }
450
+ } else if (process.arch === 's390x') {
451
+ try {
452
+ return require('./pqc-vault.linux-s390x-gnu.node')
453
+ } catch (e) {
454
+ loadErrors.push(e)
455
+ }
456
+ try {
457
+ const binding = require('@post-quantum-cryptography/pqc-vault-linux-s390x-gnu')
458
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-linux-s390x-gnu/package.json').version
459
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
460
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
461
+ }
462
+ return binding
463
+ } catch (e) {
464
+ loadErrors.push(e)
465
+ }
466
+ } else {
467
+ loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
468
+ }
469
+ } else if (process.platform === 'openharmony') {
470
+ if (process.arch === 'arm64') {
471
+ try {
472
+ return require('./pqc-vault.openharmony-arm64.node')
473
+ } catch (e) {
474
+ loadErrors.push(e)
475
+ }
476
+ try {
477
+ const binding = require('@post-quantum-cryptography/pqc-vault-openharmony-arm64')
478
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-openharmony-arm64/package.json').version
479
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
480
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
481
+ }
482
+ return binding
483
+ } catch (e) {
484
+ loadErrors.push(e)
485
+ }
486
+ } else if (process.arch === 'x64') {
487
+ try {
488
+ return require('./pqc-vault.openharmony-x64.node')
489
+ } catch (e) {
490
+ loadErrors.push(e)
491
+ }
492
+ try {
493
+ const binding = require('@post-quantum-cryptography/pqc-vault-openharmony-x64')
494
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-openharmony-x64/package.json').version
495
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
496
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
497
+ }
498
+ return binding
499
+ } catch (e) {
500
+ loadErrors.push(e)
501
+ }
502
+ } else if (process.arch === 'arm') {
503
+ try {
504
+ return require('./pqc-vault.openharmony-arm.node')
505
+ } catch (e) {
506
+ loadErrors.push(e)
507
+ }
508
+ try {
509
+ const binding = require('@post-quantum-cryptography/pqc-vault-openharmony-arm')
510
+ const bindingPackageVersion = require('@post-quantum-cryptography/pqc-vault-openharmony-arm/package.json').version
511
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
512
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
513
+ }
514
+ return binding
515
+ } catch (e) {
516
+ loadErrors.push(e)
517
+ }
518
+ } else {
519
+ loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
520
+ }
521
+ } else {
522
+ loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
523
+ }
524
+ }
525
+
526
+ nativeBinding = requireNative()
527
+
528
+ // NAPI_RS_FORCE_WASI is a tri-state flag:
529
+ // unset / any other value → native binding preferred, WASI is only a fallback
530
+ // 'true' → force WASI fallback even if native loaded
531
+ // 'error' → force WASI and throw if no WASI binding is found
532
+ // Treating any non-empty string as truthy (the historical behavior) meant
533
+ // NAPI_RS_FORCE_WASI=false, NAPI_RS_FORCE_WASI=0, etc. inadvertently triggered
534
+ // the WASI path, causing ENOENT for packages shipped without a .wasi.cjs file.
535
+ const forceWasi =
536
+ process.env.NAPI_RS_FORCE_WASI === 'true' || process.env.NAPI_RS_FORCE_WASI === 'error'
537
+
538
+ if (!nativeBinding || forceWasi) {
539
+ let wasiBinding = null
540
+ let wasiBindingError = null
541
+ try {
542
+ wasiBinding = require('./pqc-vault.wasi.cjs')
543
+ nativeBinding = wasiBinding
544
+ } catch (err) {
545
+ if (forceWasi) {
546
+ wasiBindingError = err
547
+ }
548
+ }
549
+ if (!nativeBinding || forceWasi) {
550
+ try {
551
+ wasiBinding = require('@post-quantum-cryptography/pqc-vault-wasm32-wasi')
552
+ nativeBinding = wasiBinding
553
+ } catch (err) {
554
+ if (forceWasi) {
555
+ if (!wasiBindingError) {
556
+ wasiBindingError = err
557
+ } else {
558
+ wasiBindingError.cause = err
559
+ }
560
+ loadErrors.push(err)
561
+ }
562
+ }
563
+ }
564
+ if (process.env.NAPI_RS_FORCE_WASI === 'error' && !wasiBinding) {
565
+ const error = new Error('WASI binding not found and NAPI_RS_FORCE_WASI is set to error')
566
+ error.cause = wasiBindingError
567
+ throw error
568
+ }
569
+ }
570
+
571
+ if (!nativeBinding) {
572
+ if (loadErrors.length > 0) {
573
+ const error = new Error(
574
+ `Cannot find native binding. ` +
575
+ `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
576
+ 'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
577
+ )
578
+ // assign instead of the `new Error(message, { cause })` options form,
579
+ // which Node < 16.9 silently ignores
580
+ error.cause = loadErrors.reduce((err, cur) => {
581
+ cur.cause = err
582
+ return cur
583
+ })
584
+ throw error
585
+ }
586
+ throw new Error(`Failed to load native binding`)
587
+ }
588
+
589
+ module.exports = nativeBinding
590
+ module.exports.generateKeypair = nativeBinding.generateKeypair
591
+ module.exports.generateSigningKeypair = nativeBinding.generateSigningKeypair
592
+ module.exports.open = nativeBinding.open
593
+ module.exports.seal = nativeBinding.seal
594
+ module.exports.sign = nativeBinding.sign
595
+ module.exports.verify = nativeBinding.verify
package/dist/index.cjs ADDED
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ generateKeypair: () => generateKeypair,
34
+ generateSigningKeypair: () => generateSigningKeypair,
35
+ open: () => open,
36
+ seal: () => seal,
37
+ sign: () => sign,
38
+ verify: () => verify
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+ var import_binding = __toESM(require("../binding.js"));
42
+ function generateKeypair() {
43
+ return import_binding.default.generateKeypair();
44
+ }
45
+ function seal(data, recipientPublicKey) {
46
+ return import_binding.default.seal(data, recipientPublicKey);
47
+ }
48
+ function open(ciphertext, recipientSecretKey) {
49
+ return import_binding.default.open(ciphertext, recipientSecretKey);
50
+ }
51
+ function generateSigningKeypair() {
52
+ return import_binding.default.generateSigningKeypair();
53
+ }
54
+ function sign(message, secretKey) {
55
+ return import_binding.default.sign(message, secretKey);
56
+ }
57
+ function verify(message, signature, publicKey) {
58
+ return import_binding.default.verify(message, signature, publicKey);
59
+ }
60
+ // Annotate the CommonJS export names for ESM import in node:
61
+ 0 && (module.exports = {
62
+ generateKeypair,
63
+ generateSigningKeypair,
64
+ open,
65
+ seal,
66
+ sign,
67
+ verify
68
+ });
69
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * pqc-vault — hybrid post-quantum encryption and signatures for Node.js.\n *\n * Hybrid (classical + post-quantum) construction using X25519 + ML-KEM-768 for\n * encryption and ML-DSA-65 for signatures, via the formally-verified libcrux\n * backend.\n *\n * All functions accept any `Uint8Array` (a Node `Buffer` is a `Uint8Array`, so\n * Buffers work too) and resolve to a `Buffer` (which is itself a `Uint8Array`).\n */\n\n// The native addon loader is shipped alongside this file and kept external from\n// the bundle, so its relative `require` of the platform `.node` stays correct.\nimport native from \"../binding.js\";\n\n/** A hybrid keypair. Both fields are self-describing binary blobs. */\nexport interface KeyPair {\n /** Recipient public key (X25519 + ML-KEM-768), safe to share. */\n publicKey: Buffer;\n /** Recipient secret key (X25519 + ML-KEM-768). Keep secret. */\n secretKey: Buffer;\n}\n\n/** Accepts `Uint8Array` or Node `Buffer`. */\nexport type BytesInput = Uint8Array;\n\n/**\n * Generate a fresh hybrid encryption keypair (X25519 + ML-KEM-768).\n */\nexport function generateKeypair(): Promise<KeyPair> {\n return native.generateKeypair();\n}\n\n/**\n * Hybrid-encrypt `data` for the holder of `recipientPublicKey`.\n *\n * Internally: ephemeral X25519 + ML-KEM-768 encapsulation, combined via\n * HKDF-SHA256, then AES-256-GCM over the payload. The output is a versioned,\n * self-describing binary message.\n */\nexport function seal(\n data: BytesInput,\n recipientPublicKey: BytesInput,\n): Promise<Buffer> {\n return native.seal(data, recipientPublicKey);\n}\n\n/**\n * Decrypt a message produced by {@link seal} using `recipientSecretKey`.\n *\n * Rejects with a generic error on any failure (wrong key, tampered ciphertext,\n * tampered tag) without distinguishing the cause.\n */\nexport function open(\n ciphertext: BytesInput,\n recipientSecretKey: BytesInput,\n): Promise<Buffer> {\n return native.open(ciphertext, recipientSecretKey);\n}\n\n// ---- Signatures (ML-DSA-65) ----\n\n/**\n * Generate a fresh ML-DSA-65 signing keypair.\n */\nexport function generateSigningKeypair(): Promise<KeyPair> {\n return native.generateSigningKeypair();\n}\n\n/**\n * Sign `message` with an ML-DSA-65 signing secret key. Returns a versioned,\n * self-describing signature blob.\n */\nexport function sign(\n message: BytesInput,\n secretKey: BytesInput,\n): Promise<Buffer> {\n return native.sign(message, secretKey);\n}\n\n/**\n * Verify a `signature` over `message` against a signing public key.\n *\n * Resolves to `true`/`false`. A malformed signature resolves to `false`; a\n * malformed public key rejects with an error.\n */\nexport function verify(\n message: BytesInput,\n signature: BytesInput,\n publicKey: BytesInput,\n): Promise<boolean> {\n return native.verify(message, signature, publicKey);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,qBAAmB;AAgBZ,SAAS,kBAAoC;AAClD,SAAO,eAAAA,QAAO,gBAAgB;AAChC;AASO,SAAS,KACd,MACA,oBACiB;AACjB,SAAO,eAAAA,QAAO,KAAK,MAAM,kBAAkB;AAC7C;AAQO,SAAS,KACd,YACA,oBACiB;AACjB,SAAO,eAAAA,QAAO,KAAK,YAAY,kBAAkB;AACnD;AAOO,SAAS,yBAA2C;AACzD,SAAO,eAAAA,QAAO,uBAAuB;AACvC;AAMO,SAAS,KACd,SACA,WACiB;AACjB,SAAO,eAAAA,QAAO,KAAK,SAAS,SAAS;AACvC;AAQO,SAAS,OACd,SACA,WACA,WACkB;AAClB,SAAO,eAAAA,QAAO,OAAO,SAAS,WAAW,SAAS;AACpD;","names":["native"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * pqc-vault — hybrid post-quantum encryption and signatures for Node.js.
3
+ *
4
+ * Hybrid (classical + post-quantum) construction using X25519 + ML-KEM-768 for
5
+ * encryption and ML-DSA-65 for signatures, via the formally-verified libcrux
6
+ * backend.
7
+ *
8
+ * All functions accept any `Uint8Array` (a Node `Buffer` is a `Uint8Array`, so
9
+ * Buffers work too) and resolve to a `Buffer` (which is itself a `Uint8Array`).
10
+ */
11
+ /** A hybrid keypair. Both fields are self-describing binary blobs. */
12
+ interface KeyPair {
13
+ /** Recipient public key (X25519 + ML-KEM-768), safe to share. */
14
+ publicKey: Buffer;
15
+ /** Recipient secret key (X25519 + ML-KEM-768). Keep secret. */
16
+ secretKey: Buffer;
17
+ }
18
+ /** Accepts `Uint8Array` or Node `Buffer`. */
19
+ type BytesInput = Uint8Array;
20
+ /**
21
+ * Generate a fresh hybrid encryption keypair (X25519 + ML-KEM-768).
22
+ */
23
+ declare function generateKeypair(): Promise<KeyPair>;
24
+ /**
25
+ * Hybrid-encrypt `data` for the holder of `recipientPublicKey`.
26
+ *
27
+ * Internally: ephemeral X25519 + ML-KEM-768 encapsulation, combined via
28
+ * HKDF-SHA256, then AES-256-GCM over the payload. The output is a versioned,
29
+ * self-describing binary message.
30
+ */
31
+ declare function seal(data: BytesInput, recipientPublicKey: BytesInput): Promise<Buffer>;
32
+ /**
33
+ * Decrypt a message produced by {@link seal} using `recipientSecretKey`.
34
+ *
35
+ * Rejects with a generic error on any failure (wrong key, tampered ciphertext,
36
+ * tampered tag) without distinguishing the cause.
37
+ */
38
+ declare function open(ciphertext: BytesInput, recipientSecretKey: BytesInput): Promise<Buffer>;
39
+ /**
40
+ * Generate a fresh ML-DSA-65 signing keypair.
41
+ */
42
+ declare function generateSigningKeypair(): Promise<KeyPair>;
43
+ /**
44
+ * Sign `message` with an ML-DSA-65 signing secret key. Returns a versioned,
45
+ * self-describing signature blob.
46
+ */
47
+ declare function sign(message: BytesInput, secretKey: BytesInput): Promise<Buffer>;
48
+ /**
49
+ * Verify a `signature` over `message` against a signing public key.
50
+ *
51
+ * Resolves to `true`/`false`. A malformed signature resolves to `false`; a
52
+ * malformed public key rejects with an error.
53
+ */
54
+ declare function verify(message: BytesInput, signature: BytesInput, publicKey: BytesInput): Promise<boolean>;
55
+
56
+ export { type BytesInput, type KeyPair, generateKeypair, generateSigningKeypair, open, seal, sign, verify };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * pqc-vault — hybrid post-quantum encryption and signatures for Node.js.
3
+ *
4
+ * Hybrid (classical + post-quantum) construction using X25519 + ML-KEM-768 for
5
+ * encryption and ML-DSA-65 for signatures, via the formally-verified libcrux
6
+ * backend.
7
+ *
8
+ * All functions accept any `Uint8Array` (a Node `Buffer` is a `Uint8Array`, so
9
+ * Buffers work too) and resolve to a `Buffer` (which is itself a `Uint8Array`).
10
+ */
11
+ /** A hybrid keypair. Both fields are self-describing binary blobs. */
12
+ interface KeyPair {
13
+ /** Recipient public key (X25519 + ML-KEM-768), safe to share. */
14
+ publicKey: Buffer;
15
+ /** Recipient secret key (X25519 + ML-KEM-768). Keep secret. */
16
+ secretKey: Buffer;
17
+ }
18
+ /** Accepts `Uint8Array` or Node `Buffer`. */
19
+ type BytesInput = Uint8Array;
20
+ /**
21
+ * Generate a fresh hybrid encryption keypair (X25519 + ML-KEM-768).
22
+ */
23
+ declare function generateKeypair(): Promise<KeyPair>;
24
+ /**
25
+ * Hybrid-encrypt `data` for the holder of `recipientPublicKey`.
26
+ *
27
+ * Internally: ephemeral X25519 + ML-KEM-768 encapsulation, combined via
28
+ * HKDF-SHA256, then AES-256-GCM over the payload. The output is a versioned,
29
+ * self-describing binary message.
30
+ */
31
+ declare function seal(data: BytesInput, recipientPublicKey: BytesInput): Promise<Buffer>;
32
+ /**
33
+ * Decrypt a message produced by {@link seal} using `recipientSecretKey`.
34
+ *
35
+ * Rejects with a generic error on any failure (wrong key, tampered ciphertext,
36
+ * tampered tag) without distinguishing the cause.
37
+ */
38
+ declare function open(ciphertext: BytesInput, recipientSecretKey: BytesInput): Promise<Buffer>;
39
+ /**
40
+ * Generate a fresh ML-DSA-65 signing keypair.
41
+ */
42
+ declare function generateSigningKeypair(): Promise<KeyPair>;
43
+ /**
44
+ * Sign `message` with an ML-DSA-65 signing secret key. Returns a versioned,
45
+ * self-describing signature blob.
46
+ */
47
+ declare function sign(message: BytesInput, secretKey: BytesInput): Promise<Buffer>;
48
+ /**
49
+ * Verify a `signature` over `message` against a signing public key.
50
+ *
51
+ * Resolves to `true`/`false`. A malformed signature resolves to `false`; a
52
+ * malformed public key rejects with an error.
53
+ */
54
+ declare function verify(message: BytesInput, signature: BytesInput, publicKey: BytesInput): Promise<boolean>;
55
+
56
+ export { type BytesInput, type KeyPair, generateKeypair, generateSigningKeypair, open, seal, sign, verify };
package/dist/index.mjs ADDED
@@ -0,0 +1,29 @@
1
+ // src/index.ts
2
+ import native from "../binding.js";
3
+ function generateKeypair() {
4
+ return native.generateKeypair();
5
+ }
6
+ function seal(data, recipientPublicKey) {
7
+ return native.seal(data, recipientPublicKey);
8
+ }
9
+ function open(ciphertext, recipientSecretKey) {
10
+ return native.open(ciphertext, recipientSecretKey);
11
+ }
12
+ function generateSigningKeypair() {
13
+ return native.generateSigningKeypair();
14
+ }
15
+ function sign(message, secretKey) {
16
+ return native.sign(message, secretKey);
17
+ }
18
+ function verify(message, signature, publicKey) {
19
+ return native.verify(message, signature, publicKey);
20
+ }
21
+ export {
22
+ generateKeypair,
23
+ generateSigningKeypair,
24
+ open,
25
+ seal,
26
+ sign,
27
+ verify
28
+ };
29
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * pqc-vault — hybrid post-quantum encryption and signatures for Node.js.\n *\n * Hybrid (classical + post-quantum) construction using X25519 + ML-KEM-768 for\n * encryption and ML-DSA-65 for signatures, via the formally-verified libcrux\n * backend.\n *\n * All functions accept any `Uint8Array` (a Node `Buffer` is a `Uint8Array`, so\n * Buffers work too) and resolve to a `Buffer` (which is itself a `Uint8Array`).\n */\n\n// The native addon loader is shipped alongside this file and kept external from\n// the bundle, so its relative `require` of the platform `.node` stays correct.\nimport native from \"../binding.js\";\n\n/** A hybrid keypair. Both fields are self-describing binary blobs. */\nexport interface KeyPair {\n /** Recipient public key (X25519 + ML-KEM-768), safe to share. */\n publicKey: Buffer;\n /** Recipient secret key (X25519 + ML-KEM-768). Keep secret. */\n secretKey: Buffer;\n}\n\n/** Accepts `Uint8Array` or Node `Buffer`. */\nexport type BytesInput = Uint8Array;\n\n/**\n * Generate a fresh hybrid encryption keypair (X25519 + ML-KEM-768).\n */\nexport function generateKeypair(): Promise<KeyPair> {\n return native.generateKeypair();\n}\n\n/**\n * Hybrid-encrypt `data` for the holder of `recipientPublicKey`.\n *\n * Internally: ephemeral X25519 + ML-KEM-768 encapsulation, combined via\n * HKDF-SHA256, then AES-256-GCM over the payload. The output is a versioned,\n * self-describing binary message.\n */\nexport function seal(\n data: BytesInput,\n recipientPublicKey: BytesInput,\n): Promise<Buffer> {\n return native.seal(data, recipientPublicKey);\n}\n\n/**\n * Decrypt a message produced by {@link seal} using `recipientSecretKey`.\n *\n * Rejects with a generic error on any failure (wrong key, tampered ciphertext,\n * tampered tag) without distinguishing the cause.\n */\nexport function open(\n ciphertext: BytesInput,\n recipientSecretKey: BytesInput,\n): Promise<Buffer> {\n return native.open(ciphertext, recipientSecretKey);\n}\n\n// ---- Signatures (ML-DSA-65) ----\n\n/**\n * Generate a fresh ML-DSA-65 signing keypair.\n */\nexport function generateSigningKeypair(): Promise<KeyPair> {\n return native.generateSigningKeypair();\n}\n\n/**\n * Sign `message` with an ML-DSA-65 signing secret key. Returns a versioned,\n * self-describing signature blob.\n */\nexport function sign(\n message: BytesInput,\n secretKey: BytesInput,\n): Promise<Buffer> {\n return native.sign(message, secretKey);\n}\n\n/**\n * Verify a `signature` over `message` against a signing public key.\n *\n * Resolves to `true`/`false`. A malformed signature resolves to `false`; a\n * malformed public key rejects with an error.\n */\nexport function verify(\n message: BytesInput,\n signature: BytesInput,\n publicKey: BytesInput,\n): Promise<boolean> {\n return native.verify(message, signature, publicKey);\n}\n"],"mappings":";AAaA,OAAO,YAAY;AAgBZ,SAAS,kBAAoC;AAClD,SAAO,OAAO,gBAAgB;AAChC;AASO,SAAS,KACd,MACA,oBACiB;AACjB,SAAO,OAAO,KAAK,MAAM,kBAAkB;AAC7C;AAQO,SAAS,KACd,YACA,oBACiB;AACjB,SAAO,OAAO,KAAK,YAAY,kBAAkB;AACnD;AAOO,SAAS,yBAA2C;AACzD,SAAO,OAAO,uBAAuB;AACvC;AAMO,SAAS,KACd,SACA,WACiB;AACjB,SAAO,OAAO,KAAK,SAAS,SAAS;AACvC;AAQO,SAAS,OACd,SACA,WACA,WACkB;AAClB,SAAO,OAAO,OAAO,SAAS,WAAW,SAAS;AACpD;","names":[]}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "pqc-vault",
3
+ "version": "0.1.0",
4
+ "description": "Hybrid post-quantum encryption (X25519 + ML-KEM-768) and signatures (ML-DSA-65) for Node.js — libsodium-style API over the formally-verified libcrux backend.",
5
+ "author": "carlosmontanor",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cjmont/pqc-vault.git"
10
+ },
11
+ "keywords": [
12
+ "post-quantum",
13
+ "pqc",
14
+ "ml-kem",
15
+ "ml-dsa",
16
+ "kyber",
17
+ "dilithium",
18
+ "x25519",
19
+ "hybrid",
20
+ "encryption",
21
+ "libcrux",
22
+ "napi-rs"
23
+ ],
24
+ "type": "commonjs",
25
+ "main": "./dist/index.cjs",
26
+ "module": "./dist/index.mjs",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.mjs",
32
+ "require": "./dist/index.cjs"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "binding.js",
38
+ "binding.d.ts"
39
+ ],
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "napi": {
44
+ "binaryName": "pqc-vault",
45
+ "packageName": "@post-quantum-cryptography/pqc-vault",
46
+ "targets": [
47
+ "x86_64-pc-windows-msvc",
48
+ "x86_64-unknown-linux-gnu",
49
+ "aarch64-unknown-linux-gnu",
50
+ "aarch64-apple-darwin"
51
+ ]
52
+ },
53
+ "scripts": {
54
+ "build:rust": "napi build --release --platform --js-package-name @post-quantum-cryptography/pqc-vault --manifest-path ../../crates/pqc-vault-napi/Cargo.toml --js binding.js --dts binding.d.ts --output-dir .",
55
+ "build:rust:debug": "napi build --platform --js-package-name @post-quantum-cryptography/pqc-vault --manifest-path ../../crates/pqc-vault-napi/Cargo.toml --js binding.js --dts binding.d.ts --output-dir .",
56
+ "build:ts": "tsup",
57
+ "build": "npm run build:rust && npm run build:ts",
58
+ "test": "node --test \"test/**/*.test.mjs\""
59
+ },
60
+ "devDependencies": {
61
+ "@napi-rs/cli": "^3.0.0",
62
+ "@types/node": "^22.0.0",
63
+ "tsup": "^8.0.0",
64
+ "typescript": "^5.4.0"
65
+ },
66
+ "optionalDependencies": {
67
+ "@post-quantum-cryptography/pqc-vault-darwin-arm64": "0.1.0",
68
+ "@post-quantum-cryptography/pqc-vault-linux-arm64-gnu": "0.1.0",
69
+ "@post-quantum-cryptography/pqc-vault-linux-x64-gnu": "0.1.0",
70
+ "@post-quantum-cryptography/pqc-vault-win32-x64-msvc": "0.1.0"
71
+ }
72
+ }