@pqc-sdk/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/index.cjs +313 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +216 -0
- package/dist/index.d.ts +216 -0
- package/dist/index.js +276 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PQC SDK contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @pqc-sdk/core
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jeloercc/pqc-sdk/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@pqc-sdk/core)
|
|
5
|
+
[](https://bundlephobia.com/package/@pqc-sdk/core)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
Criptografía post-cuántica para JS/TS con defaults seguros y cero configuración.
|
|
9
|
+
**ML-KEM-768** (FIPS 203) + AES-256-GCM para cifrado híbrido, **ML-DSA-65**
|
|
10
|
+
(FIPS 204) para firmas. Validado contra los test vectors oficiales NIST ACVP.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @pqc-sdk/core
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { pqc } from '@pqc-sdk/core';
|
|
18
|
+
|
|
19
|
+
const pair = await pqc.keys.generate();
|
|
20
|
+
const ciphertext = await pqc.encrypt('secreto', pair.publicKey);
|
|
21
|
+
const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);
|
|
22
|
+
|
|
23
|
+
const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
24
|
+
const signature = await pqc.sign('documento', signer.secretKey);
|
|
25
|
+
const valid = await pqc.verify('documento', signature, signer.publicKey);
|
|
26
|
+
|
|
27
|
+
console.log(new TextDecoder().decode(plaintext), valid); // "secreto" true
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Compatibilidad
|
|
31
|
+
|
|
32
|
+
| Runtime | Soporte | Notas |
|
|
33
|
+
| ------------------ | ------- | --------------------------------------------- |
|
|
34
|
+
| Node 20+ | ✅ | ESM y CJS |
|
|
35
|
+
| Cloudflare Workers | ✅ | Sin `nodejs_compat`; ~20 KB gzip en el bundle |
|
|
36
|
+
| Deno 2+ | ✅ | `npm:@pqc-sdk/core` |
|
|
37
|
+
| Bun | ✅ | |
|
|
38
|
+
| React Native | ✅ | Requiere `react-native-get-random-values` |
|
|
39
|
+
| Navegadores | ✅ | Cualquier target ES2022 con WebCrypto |
|
|
40
|
+
|
|
41
|
+
Sin WASM ni addons nativos: TypeScript puro sobre
|
|
42
|
+
[@noble/post-quantum](https://github.com/paulmillr/noble-post-quantum).
|
|
43
|
+
|
|
44
|
+
## Benchmarks
|
|
45
|
+
|
|
46
|
+
Node 24, x86_64 (mensajes de 1 KB):
|
|
47
|
+
|
|
48
|
+
| Operación | Tiempo | Throughput |
|
|
49
|
+
| ----------------- | ---------- | ---------- |
|
|
50
|
+
| keygen ML-KEM-768 | 1,3 ms/op | 768 ops/s |
|
|
51
|
+
| encrypt | 1,7 ms/op | 585 ops/s |
|
|
52
|
+
| decrypt | 2,3 ms/op | 440 ops/s |
|
|
53
|
+
| keygen ML-DSA-65 | 4,8 ms/op | 210 ops/s |
|
|
54
|
+
| sign | 20,2 ms/op | 50 ops/s |
|
|
55
|
+
| verify | 5,1 ms/op | 195 ops/s |
|
|
56
|
+
|
|
57
|
+
## Documentación
|
|
58
|
+
|
|
59
|
+
- [Quickstart de 5 minutos](https://github.com/jeloercc/pqc-sdk/tree/main/apps/docs/guide/quickstart.md)
|
|
60
|
+
- [Cifrado híbrido KEM+AES, explicado](https://github.com/jeloercc/pqc-sdk/tree/main/apps/docs/guide/hybrid-encryption.md)
|
|
61
|
+
- [Compatibilidad detallada](https://github.com/jeloercc/pqc-sdk/tree/main/docs/compatibility.md)
|
|
62
|
+
|
|
63
|
+
## Seguridad
|
|
64
|
+
|
|
65
|
+
- Nunca implementamos primitivas: ML-KEM/ML-DSA vienen de `@noble/post-quantum`
|
|
66
|
+
y AES-GCM de `@noble/ciphers`.
|
|
67
|
+
- `@noble/post-quantum` no tiene aún auditoría independiente (self-audit
|
|
68
|
+
04/2026). Como todo JS, sin garantías constant-time estrictas.
|
|
69
|
+
- Reportes de seguridad: abrí un issue privado o escribí al maintainer.
|
|
70
|
+
|
|
71
|
+
## Licencia
|
|
72
|
+
|
|
73
|
+
[MIT](./LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PqcError: () => PqcError,
|
|
24
|
+
SUPPORTED_ALGORITHMS: () => SUPPORTED_ALGORITHMS,
|
|
25
|
+
decrypt: () => decrypt,
|
|
26
|
+
deserialize: () => deserialize,
|
|
27
|
+
encrypt: () => encrypt,
|
|
28
|
+
generate: () => generate,
|
|
29
|
+
pqc: () => pqc,
|
|
30
|
+
serialize: () => serialize,
|
|
31
|
+
sign: () => sign,
|
|
32
|
+
verify: () => verify,
|
|
33
|
+
version: () => version
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/encrypt.ts
|
|
38
|
+
var import_aes = require("@noble/ciphers/aes.js");
|
|
39
|
+
var import_utils = require("@noble/post-quantum/utils.js");
|
|
40
|
+
|
|
41
|
+
// src/algorithms.ts
|
|
42
|
+
var import_ml_dsa = require("@noble/post-quantum/ml-dsa.js");
|
|
43
|
+
var import_ml_kem = require("@noble/post-quantum/ml-kem.js");
|
|
44
|
+
|
|
45
|
+
// src/errors.ts
|
|
46
|
+
var PqcError = class extends Error {
|
|
47
|
+
code;
|
|
48
|
+
constructor(code, message) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = "PqcError";
|
|
51
|
+
this.code = code;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/algorithms.ts
|
|
56
|
+
var KEM_ALGORITHMS = {
|
|
57
|
+
"ml-kem-768": {
|
|
58
|
+
kind: "kem",
|
|
59
|
+
headerId: 1,
|
|
60
|
+
kem: import_ml_kem.ml_kem768,
|
|
61
|
+
seedLength: 64,
|
|
62
|
+
publicKeyLength: 1184,
|
|
63
|
+
secretKeyLength: 2400,
|
|
64
|
+
ciphertextLength: 1088
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var SIGNATURE_ALGORITHMS = {
|
|
68
|
+
"ml-dsa-65": {
|
|
69
|
+
kind: "signer",
|
|
70
|
+
signer: import_ml_dsa.ml_dsa65,
|
|
71
|
+
seedLength: 32,
|
|
72
|
+
publicKeyLength: 1952,
|
|
73
|
+
secretKeyLength: 4032,
|
|
74
|
+
signatureLength: 3309
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var ALGORITHMS = {
|
|
78
|
+
...KEM_ALGORITHMS,
|
|
79
|
+
...SIGNATURE_ALGORITHMS
|
|
80
|
+
};
|
|
81
|
+
function getAlgorithm(algorithm) {
|
|
82
|
+
const spec = ALGORITHMS[algorithm];
|
|
83
|
+
if (!spec) {
|
|
84
|
+
throw new PqcError("UNSUPPORTED_ALGORITHM", `Algoritmo no soportado: ${algorithm}`);
|
|
85
|
+
}
|
|
86
|
+
return spec;
|
|
87
|
+
}
|
|
88
|
+
function keyLengthFor(spec, use) {
|
|
89
|
+
return use === "public" ? spec.publicKeyLength : spec.secretKeyLength;
|
|
90
|
+
}
|
|
91
|
+
function requireKey(key, kind, use, operation) {
|
|
92
|
+
const spec = getAlgorithm(key.algorithm);
|
|
93
|
+
if (spec.kind !== kind) {
|
|
94
|
+
throw new PqcError(
|
|
95
|
+
"WRONG_ALGORITHM",
|
|
96
|
+
`${operation} requiere una key ${kind === "kem" ? "ML-KEM" : "ML-DSA"}, recibi\xF3 ${key.algorithm}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (key.use !== use) {
|
|
100
|
+
throw new PqcError("WRONG_KEY_USE", `${operation} requiere la key ${use}, recibi\xF3 ${key.use}`);
|
|
101
|
+
}
|
|
102
|
+
if (key.bytes.length !== keyLengthFor(spec, use)) {
|
|
103
|
+
throw new PqcError(
|
|
104
|
+
"INVALID_KEY",
|
|
105
|
+
`Key ${key.algorithm} ${use} con longitud inv\xE1lida: ${key.bytes.length}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return spec;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/encrypt.ts
|
|
112
|
+
var FORMAT_VERSION = 1;
|
|
113
|
+
var NONCE_LENGTH = 12;
|
|
114
|
+
var GCM_TAG_LENGTH = 16;
|
|
115
|
+
var utf8 = new TextEncoder();
|
|
116
|
+
function toBytes(data) {
|
|
117
|
+
return typeof data === "string" ? utf8.encode(data) : data;
|
|
118
|
+
}
|
|
119
|
+
async function encrypt(data, publicKey) {
|
|
120
|
+
const spec = requireKey(publicKey, "kem", "public", "encrypt");
|
|
121
|
+
const plaintext = toBytes(data);
|
|
122
|
+
const { cipherText, sharedSecret } = spec.kem.encapsulate(publicKey.bytes);
|
|
123
|
+
const nonce = (0, import_utils.randomBytes)(NONCE_LENGTH);
|
|
124
|
+
const sealed = (0, import_aes.gcm)(sharedSecret, nonce).encrypt(plaintext);
|
|
125
|
+
const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);
|
|
126
|
+
out[0] = FORMAT_VERSION;
|
|
127
|
+
out[1] = spec.headerId;
|
|
128
|
+
out.set(cipherText, 2);
|
|
129
|
+
out.set(nonce, 2 + cipherText.length);
|
|
130
|
+
out.set(sealed, 2 + cipherText.length + nonce.length);
|
|
131
|
+
return Promise.resolve(out);
|
|
132
|
+
}
|
|
133
|
+
async function decrypt(ciphertext, secretKey) {
|
|
134
|
+
const spec = requireKey(secretKey, "kem", "secret", "decrypt");
|
|
135
|
+
const minLength = 2 + spec.ciphertextLength + NONCE_LENGTH + GCM_TAG_LENGTH;
|
|
136
|
+
if (ciphertext.length < minLength) {
|
|
137
|
+
throw new PqcError("INVALID_CIPHERTEXT", "Ciphertext truncado o no producido por pqc.encrypt");
|
|
138
|
+
}
|
|
139
|
+
if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {
|
|
140
|
+
throw new PqcError(
|
|
141
|
+
"INVALID_CIPHERTEXT",
|
|
142
|
+
"Header desconocido: el ciphertext no corresponde a esta versi\xF3n o algoritmo"
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const kemCiphertext = ciphertext.subarray(2, 2 + spec.ciphertextLength);
|
|
146
|
+
const nonce = ciphertext.subarray(
|
|
147
|
+
2 + spec.ciphertextLength,
|
|
148
|
+
2 + spec.ciphertextLength + NONCE_LENGTH
|
|
149
|
+
);
|
|
150
|
+
const sealed = ciphertext.subarray(2 + spec.ciphertextLength + NONCE_LENGTH);
|
|
151
|
+
const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);
|
|
152
|
+
try {
|
|
153
|
+
return Promise.resolve((0, import_aes.gcm)(sharedSecret, nonce).decrypt(sealed));
|
|
154
|
+
} catch {
|
|
155
|
+
throw new PqcError(
|
|
156
|
+
"DECRYPTION_FAILED",
|
|
157
|
+
"No se pudo descifrar: ciphertext manipulado o secret key incorrecta"
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
var KEM_NAMES = Object.keys(KEM_ALGORITHMS);
|
|
162
|
+
|
|
163
|
+
// src/keys.ts
|
|
164
|
+
var import_utils2 = require("@noble/post-quantum/utils.js");
|
|
165
|
+
|
|
166
|
+
// src/base64url.ts
|
|
167
|
+
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
168
|
+
var CHAR_TO_VALUE = new Map([...ALPHABET].map((c, i) => [c, i]));
|
|
169
|
+
function toBase64Url(bytes) {
|
|
170
|
+
let out = "";
|
|
171
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
172
|
+
const b0 = bytes[i];
|
|
173
|
+
const b1 = bytes[i + 1];
|
|
174
|
+
const b2 = bytes[i + 2];
|
|
175
|
+
out += ALPHABET[b0 >> 2];
|
|
176
|
+
out += ALPHABET[(b0 & 3) << 4 | (b1 ?? 0) >> 4];
|
|
177
|
+
if (b1 !== void 0) out += ALPHABET[(b1 & 15) << 2 | (b2 ?? 0) >> 6];
|
|
178
|
+
if (b2 !== void 0) out += ALPHABET[b2 & 63];
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
function fromBase64Url(encoded) {
|
|
183
|
+
if (encoded.length % 4 === 1) {
|
|
184
|
+
throw new TypeError("base64url inv\xE1lido: longitud imposible");
|
|
185
|
+
}
|
|
186
|
+
const out = new Uint8Array(Math.floor(encoded.length * 3 / 4));
|
|
187
|
+
let outIndex = 0;
|
|
188
|
+
let buffer = 0;
|
|
189
|
+
let bits = 0;
|
|
190
|
+
for (const char of encoded) {
|
|
191
|
+
const value = CHAR_TO_VALUE.get(char);
|
|
192
|
+
if (value === void 0) {
|
|
193
|
+
throw new TypeError(`base64url inv\xE1lido: car\xE1cter ${JSON.stringify(char)}`);
|
|
194
|
+
}
|
|
195
|
+
buffer = buffer << 6 | value;
|
|
196
|
+
bits += 6;
|
|
197
|
+
if (bits >= 8) {
|
|
198
|
+
bits -= 8;
|
|
199
|
+
out[outIndex++] = buffer >> bits & 255;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/keys.ts
|
|
206
|
+
var SERIAL_PREFIX = "pqcv1";
|
|
207
|
+
async function generate(options) {
|
|
208
|
+
const algorithm = options?.algorithm ?? "ml-kem-768";
|
|
209
|
+
const spec = getAlgorithm(algorithm);
|
|
210
|
+
return Promise.resolve(generateKeyPairFromSeed(algorithm, (0, import_utils2.randomBytes)(spec.seedLength)));
|
|
211
|
+
}
|
|
212
|
+
function generateKeyPairFromSeed(algorithm, seed) {
|
|
213
|
+
const spec = getAlgorithm(algorithm);
|
|
214
|
+
if (seed.length !== spec.seedLength) {
|
|
215
|
+
throw new PqcError(
|
|
216
|
+
"INVALID_KEY",
|
|
217
|
+
`Seed de ${algorithm} debe medir ${spec.seedLength} bytes, recibi\xF3 ${seed.length}`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const material = spec.kind === "kem" ? spec.kem.keygen(seed) : spec.signer.keygen(seed);
|
|
221
|
+
return {
|
|
222
|
+
algorithm,
|
|
223
|
+
publicKey: { algorithm, use: "public", bytes: material.publicKey },
|
|
224
|
+
secretKey: { algorithm, use: "secret", bytes: material.secretKey }
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function serialize(key) {
|
|
228
|
+
const spec = getAlgorithm(key.algorithm);
|
|
229
|
+
if (key.bytes.length !== keyLengthFor(spec, key.use)) {
|
|
230
|
+
throw new PqcError("INVALID_KEY", `Key ${key.algorithm} ${key.use} con longitud inv\xE1lida`);
|
|
231
|
+
}
|
|
232
|
+
return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;
|
|
233
|
+
}
|
|
234
|
+
function deserialize(serialized) {
|
|
235
|
+
const parts = serialized.split(".");
|
|
236
|
+
if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {
|
|
237
|
+
throw new PqcError(
|
|
238
|
+
"INVALID_SERIALIZED_KEY",
|
|
239
|
+
"Formato esperado: pqcv1.<algoritmo>.<uso>.<base64url>"
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
const [, algorithm, use, encoded] = parts;
|
|
243
|
+
const spec = getAlgorithm(algorithm);
|
|
244
|
+
if (use !== "public" && use !== "secret") {
|
|
245
|
+
throw new PqcError("INVALID_SERIALIZED_KEY", `Uso de key desconocido: ${use}`);
|
|
246
|
+
}
|
|
247
|
+
let bytes;
|
|
248
|
+
try {
|
|
249
|
+
bytes = fromBase64Url(encoded);
|
|
250
|
+
} catch (cause) {
|
|
251
|
+
throw new PqcError(
|
|
252
|
+
"INVALID_SERIALIZED_KEY",
|
|
253
|
+
cause instanceof Error ? cause.message : "base64url inv\xE1lido"
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
const key = { algorithm, use, bytes };
|
|
257
|
+
if (bytes.length !== keyLengthFor(spec, key.use)) {
|
|
258
|
+
throw new PqcError(
|
|
259
|
+
"INVALID_KEY",
|
|
260
|
+
`Key ${algorithm} ${use} debe medir ${keyLengthFor(spec, key.use)} bytes, midi\xF3 ${bytes.length}`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
return key;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/sign.ts
|
|
267
|
+
var utf82 = new TextEncoder();
|
|
268
|
+
function toBytes2(data) {
|
|
269
|
+
return typeof data === "string" ? utf82.encode(data) : data;
|
|
270
|
+
}
|
|
271
|
+
function toNobleOptions(options) {
|
|
272
|
+
return options?.context ? { context: options.context } : void 0;
|
|
273
|
+
}
|
|
274
|
+
async function sign(data, secretKey, options) {
|
|
275
|
+
const spec = requireKey(secretKey, "signer", "secret", "sign");
|
|
276
|
+
return Promise.resolve(spec.signer.sign(toBytes2(data), secretKey.bytes, toNobleOptions(options)));
|
|
277
|
+
}
|
|
278
|
+
async function verify(data, signature, publicKey, options) {
|
|
279
|
+
const spec = requireKey(publicKey, "signer", "public", "verify");
|
|
280
|
+
try {
|
|
281
|
+
return Promise.resolve(
|
|
282
|
+
spec.signer.verify(signature, toBytes2(data), publicKey.bytes, toNobleOptions(options))
|
|
283
|
+
);
|
|
284
|
+
} catch {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/index.ts
|
|
290
|
+
var version = "0.0.1";
|
|
291
|
+
var SUPPORTED_ALGORITHMS = ["ml-kem-768", "ml-dsa-65"];
|
|
292
|
+
var pqc = {
|
|
293
|
+
keys: { generate, serialize, deserialize },
|
|
294
|
+
encrypt,
|
|
295
|
+
decrypt,
|
|
296
|
+
sign,
|
|
297
|
+
verify
|
|
298
|
+
};
|
|
299
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
300
|
+
0 && (module.exports = {
|
|
301
|
+
PqcError,
|
|
302
|
+
SUPPORTED_ALGORITHMS,
|
|
303
|
+
decrypt,
|
|
304
|
+
deserialize,
|
|
305
|
+
encrypt,
|
|
306
|
+
generate,
|
|
307
|
+
pqc,
|
|
308
|
+
serialize,
|
|
309
|
+
sign,
|
|
310
|
+
verify,
|
|
311
|
+
version
|
|
312
|
+
});
|
|
313
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/encrypt.ts","../src/algorithms.ts","../src/errors.ts","../src/keys.ts","../src/base64url.ts","../src/sign.ts"],"sourcesContent":["import { encrypt, decrypt } from './encrypt.js';\nimport { deserialize, generate, serialize } from './keys.js';\nimport { sign, verify } from './sign.js';\n\nexport { PqcError, type PqcErrorCode } from './errors.js';\nexport type { GenerateOptions } from './keys.js';\nexport type {\n Algorithm,\n KemAlgorithm,\n KeyPair,\n KeyUse,\n PqcKey,\n PublicKey,\n SecretKey,\n SignatureAlgorithm,\n SignatureOptions,\n} from './types.js';\nexport { encrypt, decrypt, sign, verify, generate, serialize, deserialize };\n\n/**\n * Versión del SDK.\n *\n * @example\n * ```ts\n * import { version } from '@pqc-sdk/core';\n *\n * console.log(version); // \"0.0.1\"\n * ```\n */\nexport const version = '0.0.1';\n\n/**\n * Algoritmos PQC implementados (FIPS 203 y FIPS 204).\n *\n * @example\n * ```ts\n * import { SUPPORTED_ALGORITHMS } from '@pqc-sdk/core';\n *\n * SUPPORTED_ALGORITHMS.includes('ml-kem-768'); // true\n * ```\n */\nexport const SUPPORTED_ALGORITHMS = ['ml-kem-768', 'ml-dsa-65'] as const;\n\nexport type SupportedAlgorithm = (typeof SUPPORTED_ALGORITHMS)[number];\n\n/**\n * Punto de entrada del SDK: cifrado híbrido post-cuántico y firmas digitales\n * con defaults seguros, sin configuración.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * // Cifrado (ML-KEM-768 + AES-256-GCM)\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('hola', pair.publicKey);\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n *\n * // Firmas (ML-DSA-65)\n * const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign('documento', signer.secretKey);\n * await pqc.verify('documento', signature, signer.publicKey); // true\n * ```\n */\nexport const pqc = {\n keys: { generate, serialize, deserialize },\n encrypt,\n decrypt,\n sign,\n verify,\n} as const;\n","import { gcm } from '@noble/ciphers/aes.js';\nimport { randomBytes } from '@noble/post-quantum/utils.js';\n\nimport { KEM_ALGORITHMS, requireKey } from './algorithms.js';\nimport { PqcError } from './errors.js';\nimport type { PublicKey, SecretKey } from './types.js';\n\nconst FORMAT_VERSION = 1;\nconst NONCE_LENGTH = 12;\nconst GCM_TAG_LENGTH = 16;\n\nconst utf8 = new TextEncoder();\n\nfunction toBytes(data: Uint8Array | string): Uint8Array {\n return typeof data === 'string' ? utf8.encode(data) : data;\n}\n\n/**\n * Cifrado híbrido: encapsula un secreto con ML-KEM-768 (FIPS 203) y cifra los\n * datos con AES-256-GCM usando ese secreto. El resultado es un único\n * `Uint8Array` autocontenido que solo {@link decrypt} puede abrir.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('dato sensible', pair.publicKey);\n * ```\n */\nexport async function encrypt(\n data: Uint8Array | string,\n publicKey: PublicKey<'ml-kem-768'>,\n): Promise<Uint8Array> {\n const spec = requireKey(publicKey, 'kem', 'public', 'encrypt');\n const plaintext = toBytes(data);\n\n const { cipherText, sharedSecret } = spec.kem.encapsulate(publicKey.bytes);\n const nonce = randomBytes(NONCE_LENGTH);\n const sealed = gcm(sharedSecret, nonce).encrypt(plaintext);\n\n const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);\n out[0] = FORMAT_VERSION;\n out[1] = spec.headerId;\n out.set(cipherText, 2);\n out.set(nonce, 2 + cipherText.length);\n out.set(sealed, 2 + cipherText.length + nonce.length);\n return Promise.resolve(out);\n}\n\n/**\n * Descifra un ciphertext producido por {@link encrypt}. Si el ciphertext fue\n * manipulado o la key no corresponde, lanza {@link PqcError} con código\n * `DECRYPTION_FAILED` — nunca devuelve datos corruptos.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n * new TextDecoder().decode(plaintext);\n * ```\n */\nexport async function decrypt(\n ciphertext: Uint8Array,\n secretKey: SecretKey<'ml-kem-768'>,\n): Promise<Uint8Array> {\n const spec = requireKey(secretKey, 'kem', 'secret', 'decrypt');\n\n const minLength = 2 + spec.ciphertextLength + NONCE_LENGTH + GCM_TAG_LENGTH;\n if (ciphertext.length < minLength) {\n throw new PqcError('INVALID_CIPHERTEXT', 'Ciphertext truncado o no producido por pqc.encrypt');\n }\n if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {\n throw new PqcError(\n 'INVALID_CIPHERTEXT',\n 'Header desconocido: el ciphertext no corresponde a esta versión o algoritmo',\n );\n }\n\n const kemCiphertext = ciphertext.subarray(2, 2 + spec.ciphertextLength);\n const nonce = ciphertext.subarray(\n 2 + spec.ciphertextLength,\n 2 + spec.ciphertextLength + NONCE_LENGTH,\n );\n const sealed = ciphertext.subarray(2 + spec.ciphertextLength + NONCE_LENGTH);\n\n const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);\n try {\n return Promise.resolve(gcm(sharedSecret, nonce).decrypt(sealed));\n } catch {\n throw new PqcError(\n 'DECRYPTION_FAILED',\n 'No se pudo descifrar: ciphertext manipulado o secret key incorrecta',\n );\n }\n}\n\n/** Algoritmos KEM disponibles, exportado para introspección. */\nexport const KEM_NAMES = Object.keys(KEM_ALGORITHMS);\n","import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';\nimport { ml_kem768 } from '@noble/post-quantum/ml-kem.js';\n\nimport { PqcError } from './errors.js';\nimport type { Algorithm, KemAlgorithm, KeyUse, PqcKey, SignatureAlgorithm } from './types.js';\n\ninterface AlgorithmSpec {\n readonly seedLength: number;\n readonly publicKeyLength: number;\n readonly secretKeyLength: number;\n}\n\nexport interface KemSpec extends AlgorithmSpec {\n readonly kind: 'kem';\n readonly headerId: number;\n readonly ciphertextLength: number;\n readonly kem: typeof ml_kem768;\n}\n\nexport interface SignerSpec extends AlgorithmSpec {\n readonly kind: 'signer';\n readonly signatureLength: number;\n readonly signer: typeof ml_dsa65;\n}\n\nexport const KEM_ALGORITHMS: Record<KemAlgorithm, KemSpec> = {\n 'ml-kem-768': {\n kind: 'kem',\n headerId: 1,\n kem: ml_kem768,\n seedLength: 64,\n publicKeyLength: 1184,\n secretKeyLength: 2400,\n ciphertextLength: 1088,\n },\n};\n\nexport const SIGNATURE_ALGORITHMS: Record<SignatureAlgorithm, SignerSpec> = {\n 'ml-dsa-65': {\n kind: 'signer',\n signer: ml_dsa65,\n seedLength: 32,\n publicKeyLength: 1952,\n secretKeyLength: 4032,\n signatureLength: 3309,\n },\n};\n\nexport const ALGORITHMS: Record<Algorithm, KemSpec | SignerSpec> = {\n ...KEM_ALGORITHMS,\n ...SIGNATURE_ALGORITHMS,\n};\n\nexport function getAlgorithm(algorithm: string): KemSpec | SignerSpec {\n const spec = (ALGORITHMS as Record<string, KemSpec | SignerSpec>)[algorithm];\n if (!spec) {\n throw new PqcError('UNSUPPORTED_ALGORITHM', `Algoritmo no soportado: ${algorithm}`);\n }\n return spec;\n}\n\nexport function keyLengthFor(spec: KemSpec | SignerSpec, use: KeyUse): number {\n return use === 'public' ? spec.publicKeyLength : spec.secretKeyLength;\n}\n\n/** Valida algoritmo, uso y longitud de una key antes de operar con ella. */\nexport function requireKey<K extends 'kem' | 'signer'>(\n key: PqcKey,\n kind: K,\n use: KeyUse,\n operation: string,\n): K extends 'kem' ? KemSpec : SignerSpec {\n const spec = getAlgorithm(key.algorithm);\n if (spec.kind !== kind) {\n throw new PqcError(\n 'WRONG_ALGORITHM',\n `${operation} requiere una key ${kind === 'kem' ? 'ML-KEM' : 'ML-DSA'}, recibió ${key.algorithm}`,\n );\n }\n if (key.use !== use) {\n throw new PqcError('WRONG_KEY_USE', `${operation} requiere la key ${use}, recibió ${key.use}`);\n }\n if (key.bytes.length !== keyLengthFor(spec, use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `Key ${key.algorithm} ${use} con longitud inválida: ${key.bytes.length}`,\n );\n }\n return spec as K extends 'kem' ? KemSpec : SignerSpec;\n}\n","/** Códigos de error que puede emitir el SDK. */\nexport type PqcErrorCode =\n | 'UNSUPPORTED_ALGORITHM'\n | 'WRONG_ALGORITHM'\n | 'WRONG_KEY_USE'\n | 'INVALID_KEY'\n | 'INVALID_SERIALIZED_KEY'\n | 'INVALID_CIPHERTEXT'\n | 'DECRYPTION_FAILED';\n\n/**\n * Error tipado del SDK. Toda falla esperable expone un `code` estable para\n * manejarla programáticamente sin parsear mensajes.\n *\n * @example\n * ```ts\n * import { PqcError, pqc } from '@pqc-sdk/core';\n *\n * try {\n * await pqc.decrypt(ciphertext, secretKey);\n * } catch (error) {\n * if (error instanceof PqcError && error.code === 'DECRYPTION_FAILED') {\n * // ciphertext manipulado o key incorrecta\n * }\n * }\n * ```\n */\nexport class PqcError extends Error {\n readonly code: PqcErrorCode;\n\n constructor(code: PqcErrorCode, message: string) {\n super(message);\n this.name = 'PqcError';\n this.code = code;\n }\n}\n","import { randomBytes } from '@noble/post-quantum/utils.js';\n\nimport { getAlgorithm, keyLengthFor } from './algorithms.js';\nimport { fromBase64Url, toBase64Url } from './base64url.js';\nimport { PqcError } from './errors.js';\nimport type { Algorithm, KeyPair, PqcKey } from './types.js';\n\nconst SERIAL_PREFIX = 'pqcv1';\n\n/** Opciones de {@link generate}. */\nexport interface GenerateOptions<A extends Algorithm = Algorithm> {\n /** Algoritmo del par. Default: `'ml-kem-768'` (cifrado). */\n readonly algorithm?: A;\n}\n\n/**\n * Genera un par de keys post-cuánticas. Sin opciones genera ML-KEM-768,\n * listo para `pqc.encrypt`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const encryption = await pqc.keys.generate();\n * const signing = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * ```\n */\nexport async function generate(): Promise<KeyPair<'ml-kem-768'>>;\nexport async function generate<A extends Algorithm>(\n options: GenerateOptions<A> & { algorithm: A },\n): Promise<KeyPair<A>>;\nexport async function generate(options?: GenerateOptions): Promise<KeyPair>;\nexport async function generate(options?: GenerateOptions): Promise<KeyPair> {\n const algorithm = options?.algorithm ?? 'ml-kem-768';\n const spec = getAlgorithm(algorithm);\n return Promise.resolve(generateKeyPairFromSeed(algorithm, randomBytes(spec.seedLength)));\n}\n\n/**\n * Generación determinística a partir de una seed. Uso interno y de tests\n * (vectores NIST). Para uso normal preferir {@link generate}.\n */\nexport function generateKeyPairFromSeed(algorithm: Algorithm, seed: Uint8Array): KeyPair {\n const spec = getAlgorithm(algorithm);\n if (seed.length !== spec.seedLength) {\n throw new PqcError(\n 'INVALID_KEY',\n `Seed de ${algorithm} debe medir ${spec.seedLength} bytes, recibió ${seed.length}`,\n );\n }\n const material = spec.kind === 'kem' ? spec.kem.keygen(seed) : spec.signer.keygen(seed);\n return {\n algorithm,\n publicKey: { algorithm, use: 'public', bytes: material.publicKey },\n secretKey: { algorithm, use: 'secret', bytes: material.secretKey },\n };\n}\n\n/**\n * Serializa una key a un string portable: `pqcv1.<algoritmo>.<uso>.<base64url>`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const pair = await pqc.keys.generate();\n * const token = pqc.keys.serialize(pair.publicKey);\n * // \"pqcv1.ml-kem-768.public.h1q3…\"\n * ```\n */\nexport function serialize(key: PqcKey): string {\n const spec = getAlgorithm(key.algorithm);\n if (key.bytes.length !== keyLengthFor(spec, key.use)) {\n throw new PqcError('INVALID_KEY', `Key ${key.algorithm} ${key.use} con longitud inválida`);\n }\n return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;\n}\n\n/**\n * Reconstruye una key desde el formato de {@link serialize}. Valida versión,\n * algoritmo, uso y longitud; ante cualquier problema lanza {@link PqcError}\n * con código `INVALID_SERIALIZED_KEY` o `INVALID_KEY`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const publicKey = pqc.keys.deserialize(tokenRecibidoDelCliente);\n * const ciphertext = await pqc.encrypt(payload, publicKey);\n * ```\n */\nexport function deserialize(serialized: string): PqcKey {\n const parts = serialized.split('.');\n if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {\n throw new PqcError(\n 'INVALID_SERIALIZED_KEY',\n 'Formato esperado: pqcv1.<algoritmo>.<uso>.<base64url>',\n );\n }\n const [, algorithm, use, encoded] = parts as [string, string, string, string];\n const spec = getAlgorithm(algorithm);\n if (use !== 'public' && use !== 'secret') {\n throw new PqcError('INVALID_SERIALIZED_KEY', `Uso de key desconocido: ${use}`);\n }\n let bytes: Uint8Array;\n try {\n bytes = fromBase64Url(encoded);\n } catch (cause) {\n throw new PqcError(\n 'INVALID_SERIALIZED_KEY',\n cause instanceof Error ? cause.message : 'base64url inválido',\n );\n }\n const key: PqcKey = { algorithm: algorithm as Algorithm, use, bytes };\n if (bytes.length !== keyLengthFor(spec, key.use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `Key ${algorithm} ${use} debe medir ${keyLengthFor(spec, key.use)} bytes, midió ${bytes.length}`,\n );\n }\n return key;\n}\n","const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n\nconst CHAR_TO_VALUE = new Map<string, number>([...ALPHABET].map((c, i) => [c, i]));\n\n/** Codifica bytes a base64url sin padding. Implementación pura, sin Buffer/btoa. */\nexport function toBase64Url(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i]!;\n const b1 = bytes[i + 1];\n const b2 = bytes[i + 2];\n out += ALPHABET[b0 >> 2]!;\n out += ALPHABET[((b0 & 0x03) << 4) | ((b1 ?? 0) >> 4)]!;\n if (b1 !== undefined) out += ALPHABET[((b1 & 0x0f) << 2) | ((b2 ?? 0) >> 6)]!;\n if (b2 !== undefined) out += ALPHABET[b2 & 0x3f]!;\n }\n return out;\n}\n\n/** Decodifica base64url sin padding. Lanza TypeError ante caracteres inválidos. */\nexport function fromBase64Url(encoded: string): Uint8Array {\n if (encoded.length % 4 === 1) {\n throw new TypeError('base64url inválido: longitud imposible');\n }\n const out = new Uint8Array(Math.floor((encoded.length * 3) / 4));\n let outIndex = 0;\n let buffer = 0;\n let bits = 0;\n for (const char of encoded) {\n const value = CHAR_TO_VALUE.get(char);\n if (value === undefined) {\n throw new TypeError(`base64url inválido: carácter ${JSON.stringify(char)}`);\n }\n buffer = (buffer << 6) | value;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[outIndex++] = (buffer >> bits) & 0xff;\n }\n }\n return out;\n}\n","import { requireKey } from './algorithms.js';\nimport type { PublicKey, SecretKey, SignatureOptions } from './types.js';\n\nconst utf8 = new TextEncoder();\n\nfunction toBytes(data: Uint8Array | string): Uint8Array {\n return typeof data === 'string' ? utf8.encode(data) : data;\n}\n\nfunction toNobleOptions(options?: SignatureOptions): { context: Uint8Array } | undefined {\n return options?.context ? { context: options.context } : undefined;\n}\n\n/**\n * Firma datos con ML-DSA-65 (FIPS 204), modo hedged (firma aleatorizada,\n * el default del estándar). Devuelve la firma de 3309 bytes.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const pair = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign(documento, pair.secretKey);\n * ```\n */\nexport async function sign(\n data: Uint8Array | string,\n secretKey: SecretKey<'ml-dsa-65'>,\n options?: SignatureOptions,\n): Promise<Uint8Array> {\n const spec = requireKey(secretKey, 'signer', 'secret', 'sign');\n return Promise.resolve(spec.signer.sign(toBytes(data), secretKey.bytes, toNobleOptions(options)));\n}\n\n/**\n * Verifica una firma ML-DSA-65. Devuelve `false` ante firmas inválidas o\n * malformadas (nunca lanza por una firma corrupta); solo lanza si la key\n * no es ML-DSA.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const valid = await pqc.verify(documento, signature, pair.publicKey);\n * if (!valid) throw new Error('firma inválida');\n * ```\n */\nexport async function verify(\n data: Uint8Array | string,\n signature: Uint8Array,\n publicKey: PublicKey<'ml-dsa-65'>,\n options?: SignatureOptions,\n): Promise<boolean> {\n const spec = requireKey(publicKey, 'signer', 'public', 'verify');\n try {\n return Promise.resolve(\n spec.signer.verify(signature, toBytes(data), publicKey.bytes, toNobleOptions(options)),\n );\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["import_utils","utf8","toBytes"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/** Algoritmo de encapsulamiento de claves (cifrado híbrido). */
|
|
2
|
+
type KemAlgorithm = 'ml-kem-768';
|
|
3
|
+
/** Algoritmo de firma digital. */
|
|
4
|
+
type SignatureAlgorithm = 'ml-dsa-65';
|
|
5
|
+
/** Algoritmos soportados por el SDK. */
|
|
6
|
+
type Algorithm = KemAlgorithm | SignatureAlgorithm;
|
|
7
|
+
/** Rol de una key dentro de su par. */
|
|
8
|
+
type KeyUse = 'public' | 'secret';
|
|
9
|
+
/** Key del SDK: bytes crudos más metadata de algoritmo y uso. */
|
|
10
|
+
interface PqcKey<A extends Algorithm = Algorithm, U extends KeyUse = KeyUse> {
|
|
11
|
+
readonly algorithm: A;
|
|
12
|
+
readonly use: U;
|
|
13
|
+
readonly bytes: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
/** Key pública, segura de compartir. */
|
|
16
|
+
type PublicKey<A extends Algorithm = Algorithm> = PqcKey<A, 'public'>;
|
|
17
|
+
/** Key secreta. Nunca debe salir del entorno del dueño. */
|
|
18
|
+
type SecretKey<A extends Algorithm = Algorithm> = PqcKey<A, 'secret'>;
|
|
19
|
+
/** Par de keys generado por `pqc.keys.generate`. */
|
|
20
|
+
interface KeyPair<A extends Algorithm = Algorithm> {
|
|
21
|
+
readonly algorithm: A;
|
|
22
|
+
readonly publicKey: PublicKey<A>;
|
|
23
|
+
readonly secretKey: SecretKey<A>;
|
|
24
|
+
}
|
|
25
|
+
/** Opciones de firma/verificación (FIPS 204 §5.2, context string opcional). */
|
|
26
|
+
interface SignatureOptions {
|
|
27
|
+
/** Context string de hasta 255 bytes. Default: vacío. */
|
|
28
|
+
readonly context?: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Cifrado híbrido: encapsula un secreto con ML-KEM-768 (FIPS 203) y cifra los
|
|
33
|
+
* datos con AES-256-GCM usando ese secreto. El resultado es un único
|
|
34
|
+
* `Uint8Array` autocontenido que solo {@link decrypt} puede abrir.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
39
|
+
*
|
|
40
|
+
* const pair = await pqc.keys.generate();
|
|
41
|
+
* const ciphertext = await pqc.encrypt('dato sensible', pair.publicKey);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function encrypt(data: Uint8Array | string, publicKey: PublicKey<'ml-kem-768'>): Promise<Uint8Array>;
|
|
45
|
+
/**
|
|
46
|
+
* Descifra un ciphertext producido por {@link encrypt}. Si el ciphertext fue
|
|
47
|
+
* manipulado o la key no corresponde, lanza {@link PqcError} con código
|
|
48
|
+
* `DECRYPTION_FAILED` — nunca devuelve datos corruptos.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
53
|
+
*
|
|
54
|
+
* const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);
|
|
55
|
+
* new TextDecoder().decode(plaintext);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function decrypt(ciphertext: Uint8Array, secretKey: SecretKey<'ml-kem-768'>): Promise<Uint8Array>;
|
|
59
|
+
|
|
60
|
+
/** Opciones de {@link generate}. */
|
|
61
|
+
interface GenerateOptions<A extends Algorithm = Algorithm> {
|
|
62
|
+
/** Algoritmo del par. Default: `'ml-kem-768'` (cifrado). */
|
|
63
|
+
readonly algorithm?: A;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Genera un par de keys post-cuánticas. Sin opciones genera ML-KEM-768,
|
|
67
|
+
* listo para `pqc.encrypt`.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
72
|
+
*
|
|
73
|
+
* const encryption = await pqc.keys.generate();
|
|
74
|
+
* const signing = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function generate(): Promise<KeyPair<'ml-kem-768'>>;
|
|
78
|
+
declare function generate<A extends Algorithm>(options: GenerateOptions<A> & {
|
|
79
|
+
algorithm: A;
|
|
80
|
+
}): Promise<KeyPair<A>>;
|
|
81
|
+
declare function generate(options?: GenerateOptions): Promise<KeyPair>;
|
|
82
|
+
/**
|
|
83
|
+
* Serializa una key a un string portable: `pqcv1.<algoritmo>.<uso>.<base64url>`.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
88
|
+
*
|
|
89
|
+
* const pair = await pqc.keys.generate();
|
|
90
|
+
* const token = pqc.keys.serialize(pair.publicKey);
|
|
91
|
+
* // "pqcv1.ml-kem-768.public.h1q3…"
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
declare function serialize(key: PqcKey): string;
|
|
95
|
+
/**
|
|
96
|
+
* Reconstruye una key desde el formato de {@link serialize}. Valida versión,
|
|
97
|
+
* algoritmo, uso y longitud; ante cualquier problema lanza {@link PqcError}
|
|
98
|
+
* con código `INVALID_SERIALIZED_KEY` o `INVALID_KEY`.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
103
|
+
*
|
|
104
|
+
* const publicKey = pqc.keys.deserialize(tokenRecibidoDelCliente);
|
|
105
|
+
* const ciphertext = await pqc.encrypt(payload, publicKey);
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function deserialize(serialized: string): PqcKey;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Firma datos con ML-DSA-65 (FIPS 204), modo hedged (firma aleatorizada,
|
|
112
|
+
* el default del estándar). Devuelve la firma de 3309 bytes.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
117
|
+
*
|
|
118
|
+
* const pair = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
119
|
+
* const signature = await pqc.sign(documento, pair.secretKey);
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare function sign(data: Uint8Array | string, secretKey: SecretKey<'ml-dsa-65'>, options?: SignatureOptions): Promise<Uint8Array>;
|
|
123
|
+
/**
|
|
124
|
+
* Verifica una firma ML-DSA-65. Devuelve `false` ante firmas inválidas o
|
|
125
|
+
* malformadas (nunca lanza por una firma corrupta); solo lanza si la key
|
|
126
|
+
* no es ML-DSA.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
131
|
+
*
|
|
132
|
+
* const valid = await pqc.verify(documento, signature, pair.publicKey);
|
|
133
|
+
* if (!valid) throw new Error('firma inválida');
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
declare function verify(data: Uint8Array | string, signature: Uint8Array, publicKey: PublicKey<'ml-dsa-65'>, options?: SignatureOptions): Promise<boolean>;
|
|
137
|
+
|
|
138
|
+
/** Códigos de error que puede emitir el SDK. */
|
|
139
|
+
type PqcErrorCode = 'UNSUPPORTED_ALGORITHM' | 'WRONG_ALGORITHM' | 'WRONG_KEY_USE' | 'INVALID_KEY' | 'INVALID_SERIALIZED_KEY' | 'INVALID_CIPHERTEXT' | 'DECRYPTION_FAILED';
|
|
140
|
+
/**
|
|
141
|
+
* Error tipado del SDK. Toda falla esperable expone un `code` estable para
|
|
142
|
+
* manejarla programáticamente sin parsear mensajes.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* import { PqcError, pqc } from '@pqc-sdk/core';
|
|
147
|
+
*
|
|
148
|
+
* try {
|
|
149
|
+
* await pqc.decrypt(ciphertext, secretKey);
|
|
150
|
+
* } catch (error) {
|
|
151
|
+
* if (error instanceof PqcError && error.code === 'DECRYPTION_FAILED') {
|
|
152
|
+
* // ciphertext manipulado o key incorrecta
|
|
153
|
+
* }
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare class PqcError extends Error {
|
|
158
|
+
readonly code: PqcErrorCode;
|
|
159
|
+
constructor(code: PqcErrorCode, message: string);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Versión del SDK.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* import { version } from '@pqc-sdk/core';
|
|
168
|
+
*
|
|
169
|
+
* console.log(version); // "0.0.1"
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare const version = "0.0.1";
|
|
173
|
+
/**
|
|
174
|
+
* Algoritmos PQC implementados (FIPS 203 y FIPS 204).
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* import { SUPPORTED_ALGORITHMS } from '@pqc-sdk/core';
|
|
179
|
+
*
|
|
180
|
+
* SUPPORTED_ALGORITHMS.includes('ml-kem-768'); // true
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
declare const SUPPORTED_ALGORITHMS: readonly ["ml-kem-768", "ml-dsa-65"];
|
|
184
|
+
type SupportedAlgorithm = (typeof SUPPORTED_ALGORITHMS)[number];
|
|
185
|
+
/**
|
|
186
|
+
* Punto de entrada del SDK: cifrado híbrido post-cuántico y firmas digitales
|
|
187
|
+
* con defaults seguros, sin configuración.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
192
|
+
*
|
|
193
|
+
* // Cifrado (ML-KEM-768 + AES-256-GCM)
|
|
194
|
+
* const pair = await pqc.keys.generate();
|
|
195
|
+
* const ciphertext = await pqc.encrypt('hola', pair.publicKey);
|
|
196
|
+
* const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);
|
|
197
|
+
*
|
|
198
|
+
* // Firmas (ML-DSA-65)
|
|
199
|
+
* const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
200
|
+
* const signature = await pqc.sign('documento', signer.secretKey);
|
|
201
|
+
* await pqc.verify('documento', signature, signer.publicKey); // true
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
declare const pqc: {
|
|
205
|
+
readonly keys: {
|
|
206
|
+
readonly generate: typeof generate;
|
|
207
|
+
readonly serialize: typeof serialize;
|
|
208
|
+
readonly deserialize: typeof deserialize;
|
|
209
|
+
};
|
|
210
|
+
readonly encrypt: typeof encrypt;
|
|
211
|
+
readonly decrypt: typeof decrypt;
|
|
212
|
+
readonly sign: typeof sign;
|
|
213
|
+
readonly verify: typeof verify;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export { type Algorithm, type GenerateOptions, type KemAlgorithm, type KeyPair, type KeyUse, PqcError, type PqcErrorCode, type PqcKey, type PublicKey, SUPPORTED_ALGORITHMS, type SecretKey, type SignatureAlgorithm, type SignatureOptions, type SupportedAlgorithm, decrypt, deserialize, encrypt, generate, pqc, serialize, sign, verify, version };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/** Algoritmo de encapsulamiento de claves (cifrado híbrido). */
|
|
2
|
+
type KemAlgorithm = 'ml-kem-768';
|
|
3
|
+
/** Algoritmo de firma digital. */
|
|
4
|
+
type SignatureAlgorithm = 'ml-dsa-65';
|
|
5
|
+
/** Algoritmos soportados por el SDK. */
|
|
6
|
+
type Algorithm = KemAlgorithm | SignatureAlgorithm;
|
|
7
|
+
/** Rol de una key dentro de su par. */
|
|
8
|
+
type KeyUse = 'public' | 'secret';
|
|
9
|
+
/** Key del SDK: bytes crudos más metadata de algoritmo y uso. */
|
|
10
|
+
interface PqcKey<A extends Algorithm = Algorithm, U extends KeyUse = KeyUse> {
|
|
11
|
+
readonly algorithm: A;
|
|
12
|
+
readonly use: U;
|
|
13
|
+
readonly bytes: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
/** Key pública, segura de compartir. */
|
|
16
|
+
type PublicKey<A extends Algorithm = Algorithm> = PqcKey<A, 'public'>;
|
|
17
|
+
/** Key secreta. Nunca debe salir del entorno del dueño. */
|
|
18
|
+
type SecretKey<A extends Algorithm = Algorithm> = PqcKey<A, 'secret'>;
|
|
19
|
+
/** Par de keys generado por `pqc.keys.generate`. */
|
|
20
|
+
interface KeyPair<A extends Algorithm = Algorithm> {
|
|
21
|
+
readonly algorithm: A;
|
|
22
|
+
readonly publicKey: PublicKey<A>;
|
|
23
|
+
readonly secretKey: SecretKey<A>;
|
|
24
|
+
}
|
|
25
|
+
/** Opciones de firma/verificación (FIPS 204 §5.2, context string opcional). */
|
|
26
|
+
interface SignatureOptions {
|
|
27
|
+
/** Context string de hasta 255 bytes. Default: vacío. */
|
|
28
|
+
readonly context?: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Cifrado híbrido: encapsula un secreto con ML-KEM-768 (FIPS 203) y cifra los
|
|
33
|
+
* datos con AES-256-GCM usando ese secreto. El resultado es un único
|
|
34
|
+
* `Uint8Array` autocontenido que solo {@link decrypt} puede abrir.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
39
|
+
*
|
|
40
|
+
* const pair = await pqc.keys.generate();
|
|
41
|
+
* const ciphertext = await pqc.encrypt('dato sensible', pair.publicKey);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare function encrypt(data: Uint8Array | string, publicKey: PublicKey<'ml-kem-768'>): Promise<Uint8Array>;
|
|
45
|
+
/**
|
|
46
|
+
* Descifra un ciphertext producido por {@link encrypt}. Si el ciphertext fue
|
|
47
|
+
* manipulado o la key no corresponde, lanza {@link PqcError} con código
|
|
48
|
+
* `DECRYPTION_FAILED` — nunca devuelve datos corruptos.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
53
|
+
*
|
|
54
|
+
* const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);
|
|
55
|
+
* new TextDecoder().decode(plaintext);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function decrypt(ciphertext: Uint8Array, secretKey: SecretKey<'ml-kem-768'>): Promise<Uint8Array>;
|
|
59
|
+
|
|
60
|
+
/** Opciones de {@link generate}. */
|
|
61
|
+
interface GenerateOptions<A extends Algorithm = Algorithm> {
|
|
62
|
+
/** Algoritmo del par. Default: `'ml-kem-768'` (cifrado). */
|
|
63
|
+
readonly algorithm?: A;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Genera un par de keys post-cuánticas. Sin opciones genera ML-KEM-768,
|
|
67
|
+
* listo para `pqc.encrypt`.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
72
|
+
*
|
|
73
|
+
* const encryption = await pqc.keys.generate();
|
|
74
|
+
* const signing = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function generate(): Promise<KeyPair<'ml-kem-768'>>;
|
|
78
|
+
declare function generate<A extends Algorithm>(options: GenerateOptions<A> & {
|
|
79
|
+
algorithm: A;
|
|
80
|
+
}): Promise<KeyPair<A>>;
|
|
81
|
+
declare function generate(options?: GenerateOptions): Promise<KeyPair>;
|
|
82
|
+
/**
|
|
83
|
+
* Serializa una key a un string portable: `pqcv1.<algoritmo>.<uso>.<base64url>`.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
88
|
+
*
|
|
89
|
+
* const pair = await pqc.keys.generate();
|
|
90
|
+
* const token = pqc.keys.serialize(pair.publicKey);
|
|
91
|
+
* // "pqcv1.ml-kem-768.public.h1q3…"
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
declare function serialize(key: PqcKey): string;
|
|
95
|
+
/**
|
|
96
|
+
* Reconstruye una key desde el formato de {@link serialize}. Valida versión,
|
|
97
|
+
* algoritmo, uso y longitud; ante cualquier problema lanza {@link PqcError}
|
|
98
|
+
* con código `INVALID_SERIALIZED_KEY` o `INVALID_KEY`.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
103
|
+
*
|
|
104
|
+
* const publicKey = pqc.keys.deserialize(tokenRecibidoDelCliente);
|
|
105
|
+
* const ciphertext = await pqc.encrypt(payload, publicKey);
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare function deserialize(serialized: string): PqcKey;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Firma datos con ML-DSA-65 (FIPS 204), modo hedged (firma aleatorizada,
|
|
112
|
+
* el default del estándar). Devuelve la firma de 3309 bytes.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
117
|
+
*
|
|
118
|
+
* const pair = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
119
|
+
* const signature = await pqc.sign(documento, pair.secretKey);
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare function sign(data: Uint8Array | string, secretKey: SecretKey<'ml-dsa-65'>, options?: SignatureOptions): Promise<Uint8Array>;
|
|
123
|
+
/**
|
|
124
|
+
* Verifica una firma ML-DSA-65. Devuelve `false` ante firmas inválidas o
|
|
125
|
+
* malformadas (nunca lanza por una firma corrupta); solo lanza si la key
|
|
126
|
+
* no es ML-DSA.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
131
|
+
*
|
|
132
|
+
* const valid = await pqc.verify(documento, signature, pair.publicKey);
|
|
133
|
+
* if (!valid) throw new Error('firma inválida');
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
declare function verify(data: Uint8Array | string, signature: Uint8Array, publicKey: PublicKey<'ml-dsa-65'>, options?: SignatureOptions): Promise<boolean>;
|
|
137
|
+
|
|
138
|
+
/** Códigos de error que puede emitir el SDK. */
|
|
139
|
+
type PqcErrorCode = 'UNSUPPORTED_ALGORITHM' | 'WRONG_ALGORITHM' | 'WRONG_KEY_USE' | 'INVALID_KEY' | 'INVALID_SERIALIZED_KEY' | 'INVALID_CIPHERTEXT' | 'DECRYPTION_FAILED';
|
|
140
|
+
/**
|
|
141
|
+
* Error tipado del SDK. Toda falla esperable expone un `code` estable para
|
|
142
|
+
* manejarla programáticamente sin parsear mensajes.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* import { PqcError, pqc } from '@pqc-sdk/core';
|
|
147
|
+
*
|
|
148
|
+
* try {
|
|
149
|
+
* await pqc.decrypt(ciphertext, secretKey);
|
|
150
|
+
* } catch (error) {
|
|
151
|
+
* if (error instanceof PqcError && error.code === 'DECRYPTION_FAILED') {
|
|
152
|
+
* // ciphertext manipulado o key incorrecta
|
|
153
|
+
* }
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare class PqcError extends Error {
|
|
158
|
+
readonly code: PqcErrorCode;
|
|
159
|
+
constructor(code: PqcErrorCode, message: string);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Versión del SDK.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* import { version } from '@pqc-sdk/core';
|
|
168
|
+
*
|
|
169
|
+
* console.log(version); // "0.0.1"
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare const version = "0.0.1";
|
|
173
|
+
/**
|
|
174
|
+
* Algoritmos PQC implementados (FIPS 203 y FIPS 204).
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* import { SUPPORTED_ALGORITHMS } from '@pqc-sdk/core';
|
|
179
|
+
*
|
|
180
|
+
* SUPPORTED_ALGORITHMS.includes('ml-kem-768'); // true
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
declare const SUPPORTED_ALGORITHMS: readonly ["ml-kem-768", "ml-dsa-65"];
|
|
184
|
+
type SupportedAlgorithm = (typeof SUPPORTED_ALGORITHMS)[number];
|
|
185
|
+
/**
|
|
186
|
+
* Punto de entrada del SDK: cifrado híbrido post-cuántico y firmas digitales
|
|
187
|
+
* con defaults seguros, sin configuración.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* import { pqc } from '@pqc-sdk/core';
|
|
192
|
+
*
|
|
193
|
+
* // Cifrado (ML-KEM-768 + AES-256-GCM)
|
|
194
|
+
* const pair = await pqc.keys.generate();
|
|
195
|
+
* const ciphertext = await pqc.encrypt('hola', pair.publicKey);
|
|
196
|
+
* const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);
|
|
197
|
+
*
|
|
198
|
+
* // Firmas (ML-DSA-65)
|
|
199
|
+
* const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });
|
|
200
|
+
* const signature = await pqc.sign('documento', signer.secretKey);
|
|
201
|
+
* await pqc.verify('documento', signature, signer.publicKey); // true
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
declare const pqc: {
|
|
205
|
+
readonly keys: {
|
|
206
|
+
readonly generate: typeof generate;
|
|
207
|
+
readonly serialize: typeof serialize;
|
|
208
|
+
readonly deserialize: typeof deserialize;
|
|
209
|
+
};
|
|
210
|
+
readonly encrypt: typeof encrypt;
|
|
211
|
+
readonly decrypt: typeof decrypt;
|
|
212
|
+
readonly sign: typeof sign;
|
|
213
|
+
readonly verify: typeof verify;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export { type Algorithm, type GenerateOptions, type KemAlgorithm, type KeyPair, type KeyUse, PqcError, type PqcErrorCode, type PqcKey, type PublicKey, SUPPORTED_ALGORITHMS, type SecretKey, type SignatureAlgorithm, type SignatureOptions, type SupportedAlgorithm, decrypt, deserialize, encrypt, generate, pqc, serialize, sign, verify, version };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// src/encrypt.ts
|
|
2
|
+
import { gcm } from "@noble/ciphers/aes.js";
|
|
3
|
+
import { randomBytes } from "@noble/post-quantum/utils.js";
|
|
4
|
+
|
|
5
|
+
// src/algorithms.ts
|
|
6
|
+
import { ml_dsa65 } from "@noble/post-quantum/ml-dsa.js";
|
|
7
|
+
import { ml_kem768 } from "@noble/post-quantum/ml-kem.js";
|
|
8
|
+
|
|
9
|
+
// src/errors.ts
|
|
10
|
+
var PqcError = class extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "PqcError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/algorithms.ts
|
|
20
|
+
var KEM_ALGORITHMS = {
|
|
21
|
+
"ml-kem-768": {
|
|
22
|
+
kind: "kem",
|
|
23
|
+
headerId: 1,
|
|
24
|
+
kem: ml_kem768,
|
|
25
|
+
seedLength: 64,
|
|
26
|
+
publicKeyLength: 1184,
|
|
27
|
+
secretKeyLength: 2400,
|
|
28
|
+
ciphertextLength: 1088
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var SIGNATURE_ALGORITHMS = {
|
|
32
|
+
"ml-dsa-65": {
|
|
33
|
+
kind: "signer",
|
|
34
|
+
signer: ml_dsa65,
|
|
35
|
+
seedLength: 32,
|
|
36
|
+
publicKeyLength: 1952,
|
|
37
|
+
secretKeyLength: 4032,
|
|
38
|
+
signatureLength: 3309
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var ALGORITHMS = {
|
|
42
|
+
...KEM_ALGORITHMS,
|
|
43
|
+
...SIGNATURE_ALGORITHMS
|
|
44
|
+
};
|
|
45
|
+
function getAlgorithm(algorithm) {
|
|
46
|
+
const spec = ALGORITHMS[algorithm];
|
|
47
|
+
if (!spec) {
|
|
48
|
+
throw new PqcError("UNSUPPORTED_ALGORITHM", `Algoritmo no soportado: ${algorithm}`);
|
|
49
|
+
}
|
|
50
|
+
return spec;
|
|
51
|
+
}
|
|
52
|
+
function keyLengthFor(spec, use) {
|
|
53
|
+
return use === "public" ? spec.publicKeyLength : spec.secretKeyLength;
|
|
54
|
+
}
|
|
55
|
+
function requireKey(key, kind, use, operation) {
|
|
56
|
+
const spec = getAlgorithm(key.algorithm);
|
|
57
|
+
if (spec.kind !== kind) {
|
|
58
|
+
throw new PqcError(
|
|
59
|
+
"WRONG_ALGORITHM",
|
|
60
|
+
`${operation} requiere una key ${kind === "kem" ? "ML-KEM" : "ML-DSA"}, recibi\xF3 ${key.algorithm}`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (key.use !== use) {
|
|
64
|
+
throw new PqcError("WRONG_KEY_USE", `${operation} requiere la key ${use}, recibi\xF3 ${key.use}`);
|
|
65
|
+
}
|
|
66
|
+
if (key.bytes.length !== keyLengthFor(spec, use)) {
|
|
67
|
+
throw new PqcError(
|
|
68
|
+
"INVALID_KEY",
|
|
69
|
+
`Key ${key.algorithm} ${use} con longitud inv\xE1lida: ${key.bytes.length}`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return spec;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/encrypt.ts
|
|
76
|
+
var FORMAT_VERSION = 1;
|
|
77
|
+
var NONCE_LENGTH = 12;
|
|
78
|
+
var GCM_TAG_LENGTH = 16;
|
|
79
|
+
var utf8 = new TextEncoder();
|
|
80
|
+
function toBytes(data) {
|
|
81
|
+
return typeof data === "string" ? utf8.encode(data) : data;
|
|
82
|
+
}
|
|
83
|
+
async function encrypt(data, publicKey) {
|
|
84
|
+
const spec = requireKey(publicKey, "kem", "public", "encrypt");
|
|
85
|
+
const plaintext = toBytes(data);
|
|
86
|
+
const { cipherText, sharedSecret } = spec.kem.encapsulate(publicKey.bytes);
|
|
87
|
+
const nonce = randomBytes(NONCE_LENGTH);
|
|
88
|
+
const sealed = gcm(sharedSecret, nonce).encrypt(plaintext);
|
|
89
|
+
const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);
|
|
90
|
+
out[0] = FORMAT_VERSION;
|
|
91
|
+
out[1] = spec.headerId;
|
|
92
|
+
out.set(cipherText, 2);
|
|
93
|
+
out.set(nonce, 2 + cipherText.length);
|
|
94
|
+
out.set(sealed, 2 + cipherText.length + nonce.length);
|
|
95
|
+
return Promise.resolve(out);
|
|
96
|
+
}
|
|
97
|
+
async function decrypt(ciphertext, secretKey) {
|
|
98
|
+
const spec = requireKey(secretKey, "kem", "secret", "decrypt");
|
|
99
|
+
const minLength = 2 + spec.ciphertextLength + NONCE_LENGTH + GCM_TAG_LENGTH;
|
|
100
|
+
if (ciphertext.length < minLength) {
|
|
101
|
+
throw new PqcError("INVALID_CIPHERTEXT", "Ciphertext truncado o no producido por pqc.encrypt");
|
|
102
|
+
}
|
|
103
|
+
if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {
|
|
104
|
+
throw new PqcError(
|
|
105
|
+
"INVALID_CIPHERTEXT",
|
|
106
|
+
"Header desconocido: el ciphertext no corresponde a esta versi\xF3n o algoritmo"
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const kemCiphertext = ciphertext.subarray(2, 2 + spec.ciphertextLength);
|
|
110
|
+
const nonce = ciphertext.subarray(
|
|
111
|
+
2 + spec.ciphertextLength,
|
|
112
|
+
2 + spec.ciphertextLength + NONCE_LENGTH
|
|
113
|
+
);
|
|
114
|
+
const sealed = ciphertext.subarray(2 + spec.ciphertextLength + NONCE_LENGTH);
|
|
115
|
+
const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);
|
|
116
|
+
try {
|
|
117
|
+
return Promise.resolve(gcm(sharedSecret, nonce).decrypt(sealed));
|
|
118
|
+
} catch {
|
|
119
|
+
throw new PqcError(
|
|
120
|
+
"DECRYPTION_FAILED",
|
|
121
|
+
"No se pudo descifrar: ciphertext manipulado o secret key incorrecta"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
var KEM_NAMES = Object.keys(KEM_ALGORITHMS);
|
|
126
|
+
|
|
127
|
+
// src/keys.ts
|
|
128
|
+
import { randomBytes as randomBytes2 } from "@noble/post-quantum/utils.js";
|
|
129
|
+
|
|
130
|
+
// src/base64url.ts
|
|
131
|
+
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
132
|
+
var CHAR_TO_VALUE = new Map([...ALPHABET].map((c, i) => [c, i]));
|
|
133
|
+
function toBase64Url(bytes) {
|
|
134
|
+
let out = "";
|
|
135
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
136
|
+
const b0 = bytes[i];
|
|
137
|
+
const b1 = bytes[i + 1];
|
|
138
|
+
const b2 = bytes[i + 2];
|
|
139
|
+
out += ALPHABET[b0 >> 2];
|
|
140
|
+
out += ALPHABET[(b0 & 3) << 4 | (b1 ?? 0) >> 4];
|
|
141
|
+
if (b1 !== void 0) out += ALPHABET[(b1 & 15) << 2 | (b2 ?? 0) >> 6];
|
|
142
|
+
if (b2 !== void 0) out += ALPHABET[b2 & 63];
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
function fromBase64Url(encoded) {
|
|
147
|
+
if (encoded.length % 4 === 1) {
|
|
148
|
+
throw new TypeError("base64url inv\xE1lido: longitud imposible");
|
|
149
|
+
}
|
|
150
|
+
const out = new Uint8Array(Math.floor(encoded.length * 3 / 4));
|
|
151
|
+
let outIndex = 0;
|
|
152
|
+
let buffer = 0;
|
|
153
|
+
let bits = 0;
|
|
154
|
+
for (const char of encoded) {
|
|
155
|
+
const value = CHAR_TO_VALUE.get(char);
|
|
156
|
+
if (value === void 0) {
|
|
157
|
+
throw new TypeError(`base64url inv\xE1lido: car\xE1cter ${JSON.stringify(char)}`);
|
|
158
|
+
}
|
|
159
|
+
buffer = buffer << 6 | value;
|
|
160
|
+
bits += 6;
|
|
161
|
+
if (bits >= 8) {
|
|
162
|
+
bits -= 8;
|
|
163
|
+
out[outIndex++] = buffer >> bits & 255;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/keys.ts
|
|
170
|
+
var SERIAL_PREFIX = "pqcv1";
|
|
171
|
+
async function generate(options) {
|
|
172
|
+
const algorithm = options?.algorithm ?? "ml-kem-768";
|
|
173
|
+
const spec = getAlgorithm(algorithm);
|
|
174
|
+
return Promise.resolve(generateKeyPairFromSeed(algorithm, randomBytes2(spec.seedLength)));
|
|
175
|
+
}
|
|
176
|
+
function generateKeyPairFromSeed(algorithm, seed) {
|
|
177
|
+
const spec = getAlgorithm(algorithm);
|
|
178
|
+
if (seed.length !== spec.seedLength) {
|
|
179
|
+
throw new PqcError(
|
|
180
|
+
"INVALID_KEY",
|
|
181
|
+
`Seed de ${algorithm} debe medir ${spec.seedLength} bytes, recibi\xF3 ${seed.length}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const material = spec.kind === "kem" ? spec.kem.keygen(seed) : spec.signer.keygen(seed);
|
|
185
|
+
return {
|
|
186
|
+
algorithm,
|
|
187
|
+
publicKey: { algorithm, use: "public", bytes: material.publicKey },
|
|
188
|
+
secretKey: { algorithm, use: "secret", bytes: material.secretKey }
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function serialize(key) {
|
|
192
|
+
const spec = getAlgorithm(key.algorithm);
|
|
193
|
+
if (key.bytes.length !== keyLengthFor(spec, key.use)) {
|
|
194
|
+
throw new PqcError("INVALID_KEY", `Key ${key.algorithm} ${key.use} con longitud inv\xE1lida`);
|
|
195
|
+
}
|
|
196
|
+
return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;
|
|
197
|
+
}
|
|
198
|
+
function deserialize(serialized) {
|
|
199
|
+
const parts = serialized.split(".");
|
|
200
|
+
if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {
|
|
201
|
+
throw new PqcError(
|
|
202
|
+
"INVALID_SERIALIZED_KEY",
|
|
203
|
+
"Formato esperado: pqcv1.<algoritmo>.<uso>.<base64url>"
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const [, algorithm, use, encoded] = parts;
|
|
207
|
+
const spec = getAlgorithm(algorithm);
|
|
208
|
+
if (use !== "public" && use !== "secret") {
|
|
209
|
+
throw new PqcError("INVALID_SERIALIZED_KEY", `Uso de key desconocido: ${use}`);
|
|
210
|
+
}
|
|
211
|
+
let bytes;
|
|
212
|
+
try {
|
|
213
|
+
bytes = fromBase64Url(encoded);
|
|
214
|
+
} catch (cause) {
|
|
215
|
+
throw new PqcError(
|
|
216
|
+
"INVALID_SERIALIZED_KEY",
|
|
217
|
+
cause instanceof Error ? cause.message : "base64url inv\xE1lido"
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const key = { algorithm, use, bytes };
|
|
221
|
+
if (bytes.length !== keyLengthFor(spec, key.use)) {
|
|
222
|
+
throw new PqcError(
|
|
223
|
+
"INVALID_KEY",
|
|
224
|
+
`Key ${algorithm} ${use} debe medir ${keyLengthFor(spec, key.use)} bytes, midi\xF3 ${bytes.length}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return key;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/sign.ts
|
|
231
|
+
var utf82 = new TextEncoder();
|
|
232
|
+
function toBytes2(data) {
|
|
233
|
+
return typeof data === "string" ? utf82.encode(data) : data;
|
|
234
|
+
}
|
|
235
|
+
function toNobleOptions(options) {
|
|
236
|
+
return options?.context ? { context: options.context } : void 0;
|
|
237
|
+
}
|
|
238
|
+
async function sign(data, secretKey, options) {
|
|
239
|
+
const spec = requireKey(secretKey, "signer", "secret", "sign");
|
|
240
|
+
return Promise.resolve(spec.signer.sign(toBytes2(data), secretKey.bytes, toNobleOptions(options)));
|
|
241
|
+
}
|
|
242
|
+
async function verify(data, signature, publicKey, options) {
|
|
243
|
+
const spec = requireKey(publicKey, "signer", "public", "verify");
|
|
244
|
+
try {
|
|
245
|
+
return Promise.resolve(
|
|
246
|
+
spec.signer.verify(signature, toBytes2(data), publicKey.bytes, toNobleOptions(options))
|
|
247
|
+
);
|
|
248
|
+
} catch {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/index.ts
|
|
254
|
+
var version = "0.0.1";
|
|
255
|
+
var SUPPORTED_ALGORITHMS = ["ml-kem-768", "ml-dsa-65"];
|
|
256
|
+
var pqc = {
|
|
257
|
+
keys: { generate, serialize, deserialize },
|
|
258
|
+
encrypt,
|
|
259
|
+
decrypt,
|
|
260
|
+
sign,
|
|
261
|
+
verify
|
|
262
|
+
};
|
|
263
|
+
export {
|
|
264
|
+
PqcError,
|
|
265
|
+
SUPPORTED_ALGORITHMS,
|
|
266
|
+
decrypt,
|
|
267
|
+
deserialize,
|
|
268
|
+
encrypt,
|
|
269
|
+
generate,
|
|
270
|
+
pqc,
|
|
271
|
+
serialize,
|
|
272
|
+
sign,
|
|
273
|
+
verify,
|
|
274
|
+
version
|
|
275
|
+
};
|
|
276
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/encrypt.ts","../src/algorithms.ts","../src/errors.ts","../src/keys.ts","../src/base64url.ts","../src/sign.ts","../src/index.ts"],"sourcesContent":["import { gcm } from '@noble/ciphers/aes.js';\nimport { randomBytes } from '@noble/post-quantum/utils.js';\n\nimport { KEM_ALGORITHMS, requireKey } from './algorithms.js';\nimport { PqcError } from './errors.js';\nimport type { PublicKey, SecretKey } from './types.js';\n\nconst FORMAT_VERSION = 1;\nconst NONCE_LENGTH = 12;\nconst GCM_TAG_LENGTH = 16;\n\nconst utf8 = new TextEncoder();\n\nfunction toBytes(data: Uint8Array | string): Uint8Array {\n return typeof data === 'string' ? utf8.encode(data) : data;\n}\n\n/**\n * Cifrado híbrido: encapsula un secreto con ML-KEM-768 (FIPS 203) y cifra los\n * datos con AES-256-GCM usando ese secreto. El resultado es un único\n * `Uint8Array` autocontenido que solo {@link decrypt} puede abrir.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('dato sensible', pair.publicKey);\n * ```\n */\nexport async function encrypt(\n data: Uint8Array | string,\n publicKey: PublicKey<'ml-kem-768'>,\n): Promise<Uint8Array> {\n const spec = requireKey(publicKey, 'kem', 'public', 'encrypt');\n const plaintext = toBytes(data);\n\n const { cipherText, sharedSecret } = spec.kem.encapsulate(publicKey.bytes);\n const nonce = randomBytes(NONCE_LENGTH);\n const sealed = gcm(sharedSecret, nonce).encrypt(plaintext);\n\n const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);\n out[0] = FORMAT_VERSION;\n out[1] = spec.headerId;\n out.set(cipherText, 2);\n out.set(nonce, 2 + cipherText.length);\n out.set(sealed, 2 + cipherText.length + nonce.length);\n return Promise.resolve(out);\n}\n\n/**\n * Descifra un ciphertext producido por {@link encrypt}. Si el ciphertext fue\n * manipulado o la key no corresponde, lanza {@link PqcError} con código\n * `DECRYPTION_FAILED` — nunca devuelve datos corruptos.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n * new TextDecoder().decode(plaintext);\n * ```\n */\nexport async function decrypt(\n ciphertext: Uint8Array,\n secretKey: SecretKey<'ml-kem-768'>,\n): Promise<Uint8Array> {\n const spec = requireKey(secretKey, 'kem', 'secret', 'decrypt');\n\n const minLength = 2 + spec.ciphertextLength + NONCE_LENGTH + GCM_TAG_LENGTH;\n if (ciphertext.length < minLength) {\n throw new PqcError('INVALID_CIPHERTEXT', 'Ciphertext truncado o no producido por pqc.encrypt');\n }\n if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {\n throw new PqcError(\n 'INVALID_CIPHERTEXT',\n 'Header desconocido: el ciphertext no corresponde a esta versión o algoritmo',\n );\n }\n\n const kemCiphertext = ciphertext.subarray(2, 2 + spec.ciphertextLength);\n const nonce = ciphertext.subarray(\n 2 + spec.ciphertextLength,\n 2 + spec.ciphertextLength + NONCE_LENGTH,\n );\n const sealed = ciphertext.subarray(2 + spec.ciphertextLength + NONCE_LENGTH);\n\n const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);\n try {\n return Promise.resolve(gcm(sharedSecret, nonce).decrypt(sealed));\n } catch {\n throw new PqcError(\n 'DECRYPTION_FAILED',\n 'No se pudo descifrar: ciphertext manipulado o secret key incorrecta',\n );\n }\n}\n\n/** Algoritmos KEM disponibles, exportado para introspección. */\nexport const KEM_NAMES = Object.keys(KEM_ALGORITHMS);\n","import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';\nimport { ml_kem768 } from '@noble/post-quantum/ml-kem.js';\n\nimport { PqcError } from './errors.js';\nimport type { Algorithm, KemAlgorithm, KeyUse, PqcKey, SignatureAlgorithm } from './types.js';\n\ninterface AlgorithmSpec {\n readonly seedLength: number;\n readonly publicKeyLength: number;\n readonly secretKeyLength: number;\n}\n\nexport interface KemSpec extends AlgorithmSpec {\n readonly kind: 'kem';\n readonly headerId: number;\n readonly ciphertextLength: number;\n readonly kem: typeof ml_kem768;\n}\n\nexport interface SignerSpec extends AlgorithmSpec {\n readonly kind: 'signer';\n readonly signatureLength: number;\n readonly signer: typeof ml_dsa65;\n}\n\nexport const KEM_ALGORITHMS: Record<KemAlgorithm, KemSpec> = {\n 'ml-kem-768': {\n kind: 'kem',\n headerId: 1,\n kem: ml_kem768,\n seedLength: 64,\n publicKeyLength: 1184,\n secretKeyLength: 2400,\n ciphertextLength: 1088,\n },\n};\n\nexport const SIGNATURE_ALGORITHMS: Record<SignatureAlgorithm, SignerSpec> = {\n 'ml-dsa-65': {\n kind: 'signer',\n signer: ml_dsa65,\n seedLength: 32,\n publicKeyLength: 1952,\n secretKeyLength: 4032,\n signatureLength: 3309,\n },\n};\n\nexport const ALGORITHMS: Record<Algorithm, KemSpec | SignerSpec> = {\n ...KEM_ALGORITHMS,\n ...SIGNATURE_ALGORITHMS,\n};\n\nexport function getAlgorithm(algorithm: string): KemSpec | SignerSpec {\n const spec = (ALGORITHMS as Record<string, KemSpec | SignerSpec>)[algorithm];\n if (!spec) {\n throw new PqcError('UNSUPPORTED_ALGORITHM', `Algoritmo no soportado: ${algorithm}`);\n }\n return spec;\n}\n\nexport function keyLengthFor(spec: KemSpec | SignerSpec, use: KeyUse): number {\n return use === 'public' ? spec.publicKeyLength : spec.secretKeyLength;\n}\n\n/** Valida algoritmo, uso y longitud de una key antes de operar con ella. */\nexport function requireKey<K extends 'kem' | 'signer'>(\n key: PqcKey,\n kind: K,\n use: KeyUse,\n operation: string,\n): K extends 'kem' ? KemSpec : SignerSpec {\n const spec = getAlgorithm(key.algorithm);\n if (spec.kind !== kind) {\n throw new PqcError(\n 'WRONG_ALGORITHM',\n `${operation} requiere una key ${kind === 'kem' ? 'ML-KEM' : 'ML-DSA'}, recibió ${key.algorithm}`,\n );\n }\n if (key.use !== use) {\n throw new PqcError('WRONG_KEY_USE', `${operation} requiere la key ${use}, recibió ${key.use}`);\n }\n if (key.bytes.length !== keyLengthFor(spec, use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `Key ${key.algorithm} ${use} con longitud inválida: ${key.bytes.length}`,\n );\n }\n return spec as K extends 'kem' ? KemSpec : SignerSpec;\n}\n","/** Códigos de error que puede emitir el SDK. */\nexport type PqcErrorCode =\n | 'UNSUPPORTED_ALGORITHM'\n | 'WRONG_ALGORITHM'\n | 'WRONG_KEY_USE'\n | 'INVALID_KEY'\n | 'INVALID_SERIALIZED_KEY'\n | 'INVALID_CIPHERTEXT'\n | 'DECRYPTION_FAILED';\n\n/**\n * Error tipado del SDK. Toda falla esperable expone un `code` estable para\n * manejarla programáticamente sin parsear mensajes.\n *\n * @example\n * ```ts\n * import { PqcError, pqc } from '@pqc-sdk/core';\n *\n * try {\n * await pqc.decrypt(ciphertext, secretKey);\n * } catch (error) {\n * if (error instanceof PqcError && error.code === 'DECRYPTION_FAILED') {\n * // ciphertext manipulado o key incorrecta\n * }\n * }\n * ```\n */\nexport class PqcError extends Error {\n readonly code: PqcErrorCode;\n\n constructor(code: PqcErrorCode, message: string) {\n super(message);\n this.name = 'PqcError';\n this.code = code;\n }\n}\n","import { randomBytes } from '@noble/post-quantum/utils.js';\n\nimport { getAlgorithm, keyLengthFor } from './algorithms.js';\nimport { fromBase64Url, toBase64Url } from './base64url.js';\nimport { PqcError } from './errors.js';\nimport type { Algorithm, KeyPair, PqcKey } from './types.js';\n\nconst SERIAL_PREFIX = 'pqcv1';\n\n/** Opciones de {@link generate}. */\nexport interface GenerateOptions<A extends Algorithm = Algorithm> {\n /** Algoritmo del par. Default: `'ml-kem-768'` (cifrado). */\n readonly algorithm?: A;\n}\n\n/**\n * Genera un par de keys post-cuánticas. Sin opciones genera ML-KEM-768,\n * listo para `pqc.encrypt`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const encryption = await pqc.keys.generate();\n * const signing = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * ```\n */\nexport async function generate(): Promise<KeyPair<'ml-kem-768'>>;\nexport async function generate<A extends Algorithm>(\n options: GenerateOptions<A> & { algorithm: A },\n): Promise<KeyPair<A>>;\nexport async function generate(options?: GenerateOptions): Promise<KeyPair>;\nexport async function generate(options?: GenerateOptions): Promise<KeyPair> {\n const algorithm = options?.algorithm ?? 'ml-kem-768';\n const spec = getAlgorithm(algorithm);\n return Promise.resolve(generateKeyPairFromSeed(algorithm, randomBytes(spec.seedLength)));\n}\n\n/**\n * Generación determinística a partir de una seed. Uso interno y de tests\n * (vectores NIST). Para uso normal preferir {@link generate}.\n */\nexport function generateKeyPairFromSeed(algorithm: Algorithm, seed: Uint8Array): KeyPair {\n const spec = getAlgorithm(algorithm);\n if (seed.length !== spec.seedLength) {\n throw new PqcError(\n 'INVALID_KEY',\n `Seed de ${algorithm} debe medir ${spec.seedLength} bytes, recibió ${seed.length}`,\n );\n }\n const material = spec.kind === 'kem' ? spec.kem.keygen(seed) : spec.signer.keygen(seed);\n return {\n algorithm,\n publicKey: { algorithm, use: 'public', bytes: material.publicKey },\n secretKey: { algorithm, use: 'secret', bytes: material.secretKey },\n };\n}\n\n/**\n * Serializa una key a un string portable: `pqcv1.<algoritmo>.<uso>.<base64url>`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const pair = await pqc.keys.generate();\n * const token = pqc.keys.serialize(pair.publicKey);\n * // \"pqcv1.ml-kem-768.public.h1q3…\"\n * ```\n */\nexport function serialize(key: PqcKey): string {\n const spec = getAlgorithm(key.algorithm);\n if (key.bytes.length !== keyLengthFor(spec, key.use)) {\n throw new PqcError('INVALID_KEY', `Key ${key.algorithm} ${key.use} con longitud inválida`);\n }\n return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;\n}\n\n/**\n * Reconstruye una key desde el formato de {@link serialize}. Valida versión,\n * algoritmo, uso y longitud; ante cualquier problema lanza {@link PqcError}\n * con código `INVALID_SERIALIZED_KEY` o `INVALID_KEY`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const publicKey = pqc.keys.deserialize(tokenRecibidoDelCliente);\n * const ciphertext = await pqc.encrypt(payload, publicKey);\n * ```\n */\nexport function deserialize(serialized: string): PqcKey {\n const parts = serialized.split('.');\n if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {\n throw new PqcError(\n 'INVALID_SERIALIZED_KEY',\n 'Formato esperado: pqcv1.<algoritmo>.<uso>.<base64url>',\n );\n }\n const [, algorithm, use, encoded] = parts as [string, string, string, string];\n const spec = getAlgorithm(algorithm);\n if (use !== 'public' && use !== 'secret') {\n throw new PqcError('INVALID_SERIALIZED_KEY', `Uso de key desconocido: ${use}`);\n }\n let bytes: Uint8Array;\n try {\n bytes = fromBase64Url(encoded);\n } catch (cause) {\n throw new PqcError(\n 'INVALID_SERIALIZED_KEY',\n cause instanceof Error ? cause.message : 'base64url inválido',\n );\n }\n const key: PqcKey = { algorithm: algorithm as Algorithm, use, bytes };\n if (bytes.length !== keyLengthFor(spec, key.use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `Key ${algorithm} ${use} debe medir ${keyLengthFor(spec, key.use)} bytes, midió ${bytes.length}`,\n );\n }\n return key;\n}\n","const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n\nconst CHAR_TO_VALUE = new Map<string, number>([...ALPHABET].map((c, i) => [c, i]));\n\n/** Codifica bytes a base64url sin padding. Implementación pura, sin Buffer/btoa. */\nexport function toBase64Url(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i]!;\n const b1 = bytes[i + 1];\n const b2 = bytes[i + 2];\n out += ALPHABET[b0 >> 2]!;\n out += ALPHABET[((b0 & 0x03) << 4) | ((b1 ?? 0) >> 4)]!;\n if (b1 !== undefined) out += ALPHABET[((b1 & 0x0f) << 2) | ((b2 ?? 0) >> 6)]!;\n if (b2 !== undefined) out += ALPHABET[b2 & 0x3f]!;\n }\n return out;\n}\n\n/** Decodifica base64url sin padding. Lanza TypeError ante caracteres inválidos. */\nexport function fromBase64Url(encoded: string): Uint8Array {\n if (encoded.length % 4 === 1) {\n throw new TypeError('base64url inválido: longitud imposible');\n }\n const out = new Uint8Array(Math.floor((encoded.length * 3) / 4));\n let outIndex = 0;\n let buffer = 0;\n let bits = 0;\n for (const char of encoded) {\n const value = CHAR_TO_VALUE.get(char);\n if (value === undefined) {\n throw new TypeError(`base64url inválido: carácter ${JSON.stringify(char)}`);\n }\n buffer = (buffer << 6) | value;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[outIndex++] = (buffer >> bits) & 0xff;\n }\n }\n return out;\n}\n","import { requireKey } from './algorithms.js';\nimport type { PublicKey, SecretKey, SignatureOptions } from './types.js';\n\nconst utf8 = new TextEncoder();\n\nfunction toBytes(data: Uint8Array | string): Uint8Array {\n return typeof data === 'string' ? utf8.encode(data) : data;\n}\n\nfunction toNobleOptions(options?: SignatureOptions): { context: Uint8Array } | undefined {\n return options?.context ? { context: options.context } : undefined;\n}\n\n/**\n * Firma datos con ML-DSA-65 (FIPS 204), modo hedged (firma aleatorizada,\n * el default del estándar). Devuelve la firma de 3309 bytes.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const pair = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign(documento, pair.secretKey);\n * ```\n */\nexport async function sign(\n data: Uint8Array | string,\n secretKey: SecretKey<'ml-dsa-65'>,\n options?: SignatureOptions,\n): Promise<Uint8Array> {\n const spec = requireKey(secretKey, 'signer', 'secret', 'sign');\n return Promise.resolve(spec.signer.sign(toBytes(data), secretKey.bytes, toNobleOptions(options)));\n}\n\n/**\n * Verifica una firma ML-DSA-65. Devuelve `false` ante firmas inválidas o\n * malformadas (nunca lanza por una firma corrupta); solo lanza si la key\n * no es ML-DSA.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const valid = await pqc.verify(documento, signature, pair.publicKey);\n * if (!valid) throw new Error('firma inválida');\n * ```\n */\nexport async function verify(\n data: Uint8Array | string,\n signature: Uint8Array,\n publicKey: PublicKey<'ml-dsa-65'>,\n options?: SignatureOptions,\n): Promise<boolean> {\n const spec = requireKey(publicKey, 'signer', 'public', 'verify');\n try {\n return Promise.resolve(\n spec.signer.verify(signature, toBytes(data), publicKey.bytes, toNobleOptions(options)),\n );\n } catch {\n return false;\n }\n}\n","import { encrypt, decrypt } from './encrypt.js';\nimport { deserialize, generate, serialize } from './keys.js';\nimport { sign, verify } from './sign.js';\n\nexport { PqcError, type PqcErrorCode } from './errors.js';\nexport type { GenerateOptions } from './keys.js';\nexport type {\n Algorithm,\n KemAlgorithm,\n KeyPair,\n KeyUse,\n PqcKey,\n PublicKey,\n SecretKey,\n SignatureAlgorithm,\n SignatureOptions,\n} from './types.js';\nexport { encrypt, decrypt, sign, verify, generate, serialize, deserialize };\n\n/**\n * Versión del SDK.\n *\n * @example\n * ```ts\n * import { version } from '@pqc-sdk/core';\n *\n * console.log(version); // \"0.0.1\"\n * ```\n */\nexport const version = '0.0.1';\n\n/**\n * Algoritmos PQC implementados (FIPS 203 y FIPS 204).\n *\n * @example\n * ```ts\n * import { SUPPORTED_ALGORITHMS } from '@pqc-sdk/core';\n *\n * SUPPORTED_ALGORITHMS.includes('ml-kem-768'); // true\n * ```\n */\nexport const SUPPORTED_ALGORITHMS = ['ml-kem-768', 'ml-dsa-65'] as const;\n\nexport type SupportedAlgorithm = (typeof SUPPORTED_ALGORITHMS)[number];\n\n/**\n * Punto de entrada del SDK: cifrado híbrido post-cuántico y firmas digitales\n * con defaults seguros, sin configuración.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * // Cifrado (ML-KEM-768 + AES-256-GCM)\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('hola', pair.publicKey);\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n *\n * // Firmas (ML-DSA-65)\n * const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign('documento', signer.secretKey);\n * await pqc.verify('documento', signature, signer.publicKey); // true\n * ```\n */\nexport const pqc = {\n keys: { generate, serialize, deserialize },\n encrypt,\n decrypt,\n sign,\n verify,\n} as const;\n"],"mappings":"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","names":["randomBytes","randomBytes","utf8","toBytes"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pqc-sdk/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SDK de criptografía post-cuántica para JS/TS (ML-KEM, ML-DSA, SLH-DSA)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@noble/hashes": "^2.2.0",
|
|
25
|
+
"@vitest/coverage-v8": "^3.2.2",
|
|
26
|
+
"tsup": "^8.5.0",
|
|
27
|
+
"vitest": "^3.2.2"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@noble/ciphers": "^2.2.0",
|
|
31
|
+
"@noble/post-quantum": "^0.6.1"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"homepage": "https://github.com/jeloercc/pqc-sdk#readme",
|
|
35
|
+
"bugs": "https://github.com/jeloercc/pqc-sdk/issues",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"post-quantum",
|
|
41
|
+
"pqc",
|
|
42
|
+
"ml-kem",
|
|
43
|
+
"ml-dsa",
|
|
44
|
+
"fips-203",
|
|
45
|
+
"fips-204",
|
|
46
|
+
"kyber",
|
|
47
|
+
"dilithium",
|
|
48
|
+
"cryptography",
|
|
49
|
+
"encryption"
|
|
50
|
+
],
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/jeloercc/pqc-sdk.git",
|
|
54
|
+
"directory": "packages/core"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"dev": "tsup --watch",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest",
|
|
61
|
+
"lint": "eslint . && tsc --noEmit"
|
|
62
|
+
}
|
|
63
|
+
}
|