@pqc-sdk/core 0.1.2 → 0.3.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/dist/index.cjs CHANGED
@@ -121,10 +121,10 @@ async function encrypt(data, publicKey) {
121
121
  const plaintext = toBytes(data);
122
122
  const { cipherText, sharedSecret } = spec.kem.encapsulate(publicKey.bytes);
123
123
  const nonce = (0, import_utils.randomBytes)(NONCE_LENGTH);
124
- const sealed = (0, import_aes.gcm)(sharedSecret, nonce).encrypt(plaintext);
124
+ const header = new Uint8Array([FORMAT_VERSION, spec.headerId]);
125
+ const sealed = (0, import_aes.gcm)(sharedSecret, nonce, header).encrypt(plaintext);
125
126
  const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);
126
- out[0] = FORMAT_VERSION;
127
- out[1] = spec.headerId;
127
+ out.set(header, 0);
128
128
  out.set(cipherText, 2);
129
129
  out.set(nonce, 2 + cipherText.length);
130
130
  out.set(sealed, 2 + cipherText.length + nonce.length);
@@ -145,16 +145,20 @@ async function decrypt(ciphertext, secretKey) {
145
145
  "Unknown header: the ciphertext does not match this version or algorithm"
146
146
  );
147
147
  }
148
+ const header = ciphertext.subarray(0, 2);
148
149
  const kemCiphertext = ciphertext.subarray(2, 2 + spec.ciphertextLength);
149
150
  const nonce = ciphertext.subarray(
150
151
  2 + spec.ciphertextLength,
151
152
  2 + spec.ciphertextLength + NONCE_LENGTH
152
153
  );
153
154
  const sealed = ciphertext.subarray(2 + spec.ciphertextLength + NONCE_LENGTH);
154
- const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);
155
155
  try {
156
- return Promise.resolve((0, import_aes.gcm)(sharedSecret, nonce).decrypt(sealed));
157
- } catch {
156
+ const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);
157
+ return Promise.resolve((0, import_aes.gcm)(sharedSecret, nonce, header).decrypt(sealed));
158
+ } catch (cause) {
159
+ if (cause instanceof PqcError) {
160
+ throw cause;
161
+ }
158
162
  throw new PqcError(
159
163
  "DECRYPTION_FAILED",
160
164
  "Decryption failed: tampered ciphertext or wrong secret key"
@@ -202,6 +206,9 @@ function fromBase64Url(encoded) {
202
206
  out[outIndex++] = buffer >> bits & 255;
203
207
  }
204
208
  }
209
+ if ((buffer & (1 << bits) - 1) !== 0) {
210
+ throw new TypeError("Invalid base64url: non-canonical trailing bits");
211
+ }
205
212
  return out;
206
213
  }
207
214
 
@@ -234,7 +241,7 @@ function serialize(key) {
234
241
  }
235
242
  return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;
236
243
  }
237
- function deserialize(serialized) {
244
+ function deserialize(serialized, expected) {
238
245
  const parts = serialized.split(".");
239
246
  if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {
240
247
  throw new PqcError(
@@ -263,16 +270,37 @@ function deserialize(serialized) {
263
270
  `${algorithm} ${use} key must be ${keyLengthFor(spec, key.use)} bytes, got ${bytes.length}`
264
271
  );
265
272
  }
273
+ if (expected !== void 0) {
274
+ if (key.algorithm !== expected.algorithm) {
275
+ throw new PqcError(
276
+ "WRONG_ALGORITHM",
277
+ `Expected an ${expected.algorithm} key, got ${key.algorithm}`
278
+ );
279
+ }
280
+ if (key.use !== expected.use) {
281
+ throw new PqcError("WRONG_KEY_USE", `Expected the ${expected.use} key, got ${key.use}`);
282
+ }
283
+ }
266
284
  return key;
267
285
  }
268
286
 
269
287
  // src/sign.ts
288
+ var MAX_CONTEXT_LENGTH = 255;
270
289
  var utf82 = new TextEncoder();
271
290
  function toBytes2(data) {
272
291
  return typeof data === "string" ? utf82.encode(data) : data;
273
292
  }
274
293
  function toNobleOptions(options) {
275
- return options?.context ? { context: options.context } : void 0;
294
+ if (!options?.context) {
295
+ return void 0;
296
+ }
297
+ if (options.context.length > MAX_CONTEXT_LENGTH) {
298
+ throw new PqcError(
299
+ "INVALID_CONTEXT",
300
+ `Signature context must be at most ${MAX_CONTEXT_LENGTH} bytes, got ${options.context.length}`
301
+ );
302
+ }
303
+ return { context: options.context };
276
304
  }
277
305
  async function sign(data, secretKey, options) {
278
306
  const spec = requireKey(secretKey, "signer", "secret", "sign");
@@ -280,9 +308,10 @@ async function sign(data, secretKey, options) {
280
308
  }
281
309
  async function verify(data, signature, publicKey, options) {
282
310
  const spec = requireKey(publicKey, "signer", "public", "verify");
311
+ const nobleOptions = toNobleOptions(options);
283
312
  try {
284
313
  return Promise.resolve(
285
- spec.signer.verify(signature, toBytes2(data), publicKey.bytes, toNobleOptions(options))
314
+ spec.signer.verify(signature, toBytes2(data), publicKey.bytes, nobleOptions)
286
315
  );
287
316
  } catch {
288
317
  return false;
@@ -290,7 +319,7 @@ async function verify(data, signature, publicKey, options) {
290
319
  }
291
320
 
292
321
  // src/index.ts
293
- var version = "0.1.2";
322
+ var version = "0.3.0";
294
323
  var SUPPORTED_ALGORITHMS = ["ml-kem-768", "ml-dsa-65"];
295
324
  var pqc = {
296
325
  keys: { generate, serialize, deserialize },
@@ -1 +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// Injected at build time from package.json (`define` in tsup.config.ts and vitest.config.ts).\ndeclare const __PQC_CORE_VERSION__: string;\n\n/**\n * SDK version.\n *\n * @example\n * ```ts\n * import { version } from '@pqc-sdk/core';\n *\n * console.log(version); // e.g. \"0.1.0\"\n * ```\n */\nexport const version = __PQC_CORE_VERSION__;\n\n/**\n * Implemented PQC algorithms (FIPS 203 and 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 * SDK entry point: post-quantum hybrid encryption and digital signatures\n * with safe defaults, zero configuration.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * // Encryption (ML-KEM-768 + AES-256-GCM)\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('hello', pair.publicKey);\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n *\n * // Signatures (ML-DSA-65)\n * const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign('document', signer.secretKey);\n * await pqc.verify('document', 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 * Hybrid encryption: encapsulates a secret with ML-KEM-768 (FIPS 203) and\n * encrypts the data with AES-256-GCM using that secret. The result is a\n * single self-contained `Uint8Array` that only {@link decrypt} can open.\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('sensitive data', 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 * Decrypts a ciphertext produced by {@link encrypt}. If the ciphertext was\n * tampered with or the key does not match, it throws {@link PqcError} with\n * code `DECRYPTION_FAILED` — it never returns corrupted data.\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(\n 'INVALID_CIPHERTEXT',\n 'Ciphertext is truncated or was not produced by pqc.encrypt',\n );\n }\n if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {\n throw new PqcError(\n 'INVALID_CIPHERTEXT',\n 'Unknown header: the ciphertext does not match this version or algorithm',\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 'Decryption failed: tampered ciphertext or wrong secret key',\n );\n }\n}\n\n/** Available KEM algorithms, exported for introspection. */\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', `Unsupported algorithm: ${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/** Validates a key's algorithm, use and length before operating with it. */\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} requires an ${kind === 'kem' ? 'ML-KEM' : 'ML-DSA'} key, got ${key.algorithm}`,\n );\n }\n if (key.use !== use) {\n throw new PqcError('WRONG_KEY_USE', `${operation} requires the ${use} key, got ${key.use}`);\n }\n if (key.bytes.length !== keyLengthFor(spec, use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `${key.algorithm} ${use} key has invalid length: ${key.bytes.length}`,\n );\n }\n return spec as K extends 'kem' ? KemSpec : SignerSpec;\n}\n","/** Error codes the SDK can emit. */\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 * Typed SDK error. Every expected failure exposes a stable `code` so it can\n * be handled programmatically without parsing messages.\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 * // tampered ciphertext or wrong key\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/** Options for {@link generate}. */\nexport interface GenerateOptions<A extends Algorithm = Algorithm> {\n /** Algorithm of the pair. Default: `'ml-kem-768'` (encryption). */\n readonly algorithm?: A;\n}\n\n/**\n * Generates a post-quantum key pair. With no options it generates ML-KEM-768,\n * ready for `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 * Deterministic generation from a seed. Internal and test use (NIST vectors).\n * Prefer {@link generate} for normal use.\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 `${algorithm} seed must be ${spec.seedLength} bytes, got ${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 * Serializes a key to a portable string: `pqcv1.<algorithm>.<use>.<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.algorithm} ${key.use} key has invalid length`);\n }\n return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;\n}\n\n/**\n * Rebuilds a key from the {@link serialize} format. Validates version,\n * algorithm, use and length; on any problem it throws {@link PqcError} with\n * code `INVALID_SERIALIZED_KEY` or `INVALID_KEY`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const publicKey = pqc.keys.deserialize(tokenReceivedFromClient);\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 'Expected format: pqcv1.<algorithm>.<use>.<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', `Unknown key use: ${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 : 'Invalid base64url',\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 `${algorithm} ${use} key must be ${keyLengthFor(spec, key.use)} bytes, got ${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/** Encodes bytes to unpadded base64url. Pure implementation, no 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/** Decodes unpadded base64url. Throws TypeError on invalid characters. */\nexport function fromBase64Url(encoded: string): Uint8Array {\n if (encoded.length % 4 === 1) {\n throw new TypeError('Invalid base64url: impossible length');\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(`Invalid base64url: character ${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 * Signs data with ML-DSA-65 (FIPS 204) in hedged mode (randomized signing,\n * the standard's default). Returns the 3309-byte signature.\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(document, 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 * Verifies an ML-DSA-65 signature. Returns `false` for invalid or malformed\n * signatures (it never throws because of a corrupted signature); it only\n * throws if the key is not ML-DSA.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const valid = await pqc.verify(document, signature, pair.publicKey);\n * if (!valid) throw new Error('invalid signature');\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"]}
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 { ExpectedKey, 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// Injected at build time from package.json (`define` in tsup.config.ts and vitest.config.ts).\ndeclare const __PQC_CORE_VERSION__: string;\n\n/**\n * SDK version.\n *\n * @example\n * ```ts\n * import { version } from '@pqc-sdk/core';\n *\n * console.log(version); // e.g. \"0.1.0\"\n * ```\n */\nexport const version = __PQC_CORE_VERSION__;\n\n/**\n * Implemented PQC algorithms (FIPS 203 and 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 * SDK entry point: post-quantum hybrid encryption and digital signatures\n * with safe defaults, zero configuration.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * // Encryption (ML-KEM-768 + AES-256-GCM)\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('hello', pair.publicKey);\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n *\n * // Signatures (ML-DSA-65)\n * const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign('document', signer.secretKey);\n * await pqc.verify('document', 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 * Hybrid encryption: encapsulates a secret with ML-KEM-768 (FIPS 203) and\n * encrypts the data with AES-256-GCM using that secret. The result is a\n * single self-contained `Uint8Array` that only {@link decrypt} can open.\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('sensitive data', 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\n // Bind the 2-byte header (FORMAT_VERSION, headerId) as AES-GCM additional\n // authenticated data so it is covered by the GCM tag (see decrypt).\n const header = new Uint8Array([FORMAT_VERSION, spec.headerId]);\n const sealed = gcm(sharedSecret, nonce, header).encrypt(plaintext);\n\n const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);\n out.set(header, 0);\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 * Decrypts a ciphertext produced by {@link encrypt}. If the ciphertext was\n * tampered with or the key does not match, it throws {@link PqcError} with\n * code `DECRYPTION_FAILED` — it never returns corrupted data.\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(\n 'INVALID_CIPHERTEXT',\n 'Ciphertext is truncated or was not produced by pqc.encrypt',\n );\n }\n // This equality check is fail-fast input validation: it discriminates the\n // version and algorithm with a clear INVALID_CIPHERTEXT error before any\n // cryptographic work. The AAD binding below is the *cryptographic* integrity\n // of the header — it becomes the only line of defence once more versions or\n // algorithms share this layout (a tampered-but-known header would pass this\n // check yet fail the GCM tag). Do not remove either guard in a refactor.\n if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {\n throw new PqcError(\n 'INVALID_CIPHERTEXT',\n 'Unknown header: the ciphertext does not match this version or algorithm',\n );\n }\n\n // Reconstruct the AAD from the header bytes actually present in the message\n // so AES-GCM authenticates them as part of the tag.\n const header = ciphertext.subarray(0, 2);\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 // decapsulate stays inside the try: ML-KEM uses implicit rejection and does\n // not throw for a valid-length secret key, but any edge case where it (or GCM)\n // throws must still surface as the documented DECRYPTION_FAILED, never a raw\n // upstream error.\n try {\n const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);\n return Promise.resolve(gcm(sharedSecret, nonce, header).decrypt(sealed));\n } catch (cause) {\n if (cause instanceof PqcError) {\n throw cause;\n }\n throw new PqcError(\n 'DECRYPTION_FAILED',\n 'Decryption failed: tampered ciphertext or wrong secret key',\n );\n }\n}\n\n/** Available KEM algorithms, exported for introspection. */\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', `Unsupported algorithm: ${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/** Validates a key's algorithm, use and length before operating with it. */\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} requires an ${kind === 'kem' ? 'ML-KEM' : 'ML-DSA'} key, got ${key.algorithm}`,\n );\n }\n if (key.use !== use) {\n throw new PqcError('WRONG_KEY_USE', `${operation} requires the ${use} key, got ${key.use}`);\n }\n if (key.bytes.length !== keyLengthFor(spec, use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `${key.algorithm} ${use} key has invalid length: ${key.bytes.length}`,\n );\n }\n return spec as K extends 'kem' ? KemSpec : SignerSpec;\n}\n","/** Error codes the SDK can emit. */\nexport type PqcErrorCode =\n | 'UNSUPPORTED_ALGORITHM'\n | 'WRONG_ALGORITHM'\n | 'WRONG_KEY_USE'\n | 'INVALID_KEY'\n | 'INVALID_SERIALIZED_KEY'\n | 'INVALID_CONTEXT'\n | 'INVALID_CIPHERTEXT'\n | 'DECRYPTION_FAILED';\n\n/**\n * Typed SDK error. Every expected failure exposes a stable `code` so it can\n * be handled programmatically without parsing messages.\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 * // tampered ciphertext or wrong key\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, KeyUse, PqcKey } from './types.js';\n\nconst SERIAL_PREFIX = 'pqcv1';\n\n/** Options for {@link generate}. */\nexport interface GenerateOptions<A extends Algorithm = Algorithm> {\n /** Algorithm of the pair. Default: `'ml-kem-768'` (encryption). */\n readonly algorithm?: A;\n}\n\n/**\n * Generates a post-quantum key pair. With no options it generates ML-KEM-768,\n * ready for `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 * Deterministic generation from a seed. Internal and test use (NIST vectors).\n * Prefer {@link generate} for normal use.\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 `${algorithm} seed must be ${spec.seedLength} bytes, got ${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 * Serializes a key to a portable string: `pqcv1.<algorithm>.<use>.<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.algorithm} ${key.use} key has invalid length`);\n }\n return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;\n}\n\n/** Asserts the algorithm and use a caller expects from a deserialized key. */\nexport interface ExpectedKey<A extends Algorithm = Algorithm, U extends KeyUse = KeyUse> {\n readonly algorithm: A;\n readonly use: U;\n}\n\n/**\n * Rebuilds a key from the {@link serialize} format. Validates version,\n * algorithm, use and length; on any problem it throws {@link PqcError} with\n * code `INVALID_SERIALIZED_KEY` or `INVALID_KEY`.\n *\n * Pass `expected` to assert the algorithm and use, getting back a narrow key\n * type (e.g. `PublicKey<'ml-kem-768'>`) that drops straight into `encrypt` /\n * `sign` without an `as never` cast. A mismatch throws `WRONG_ALGORITHM` or\n * `WRONG_KEY_USE`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const token = pqc.keys.serialize((await pqc.keys.generate()).publicKey);\n * // Narrow to a typed key by asserting the expected algorithm and use:\n * const publicKey = pqc.keys.deserialize(token, { algorithm: 'ml-kem-768', use: 'public' });\n * const ciphertext = await pqc.encrypt('payload', publicKey);\n * ```\n */\nexport function deserialize(serialized: string): PqcKey;\nexport function deserialize<A extends Algorithm, U extends KeyUse>(\n serialized: string,\n expected: ExpectedKey<A, U>,\n): PqcKey<A, U>;\nexport function deserialize(serialized: string, expected?: ExpectedKey): PqcKey {\n const parts = serialized.split('.');\n if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {\n throw new PqcError(\n 'INVALID_SERIALIZED_KEY',\n 'Expected format: pqcv1.<algorithm>.<use>.<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', `Unknown key use: ${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 : 'Invalid base64url',\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 `${algorithm} ${use} key must be ${keyLengthFor(spec, key.use)} bytes, got ${bytes.length}`,\n );\n }\n if (expected !== undefined) {\n if (key.algorithm !== expected.algorithm) {\n throw new PqcError(\n 'WRONG_ALGORITHM',\n `Expected an ${expected.algorithm} key, got ${key.algorithm}`,\n );\n }\n if (key.use !== expected.use) {\n throw new PqcError('WRONG_KEY_USE', `Expected the ${expected.use} key, got ${key.use}`);\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/** Encodes bytes to unpadded base64url. Pure implementation, no 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/** Decodes unpadded base64url. Throws TypeError on invalid characters. */\nexport function fromBase64Url(encoded: string): Uint8Array {\n if (encoded.length % 4 === 1) {\n throw new TypeError('Invalid base64url: impossible length');\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(`Invalid base64url: character ${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 // Reject non-canonical input: the leftover bits of the final group (those that\n // do not complete a byte) must be zero, otherwise the string is not the\n // canonical encoding of any byte sequence.\n if ((buffer & ((1 << bits) - 1)) !== 0) {\n throw new TypeError('Invalid base64url: non-canonical trailing bits');\n }\n return out;\n}\n","import { requireKey } from './algorithms.js';\nimport { PqcError } from './errors.js';\nimport type { PublicKey, SecretKey, SignatureOptions } from './types.js';\n\n/** FIPS 204 §5.2 caps the signing context string at 255 bytes. */\nconst MAX_CONTEXT_LENGTH = 255;\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 if (!options?.context) {\n return undefined;\n }\n if (options.context.length > MAX_CONTEXT_LENGTH) {\n throw new PqcError(\n 'INVALID_CONTEXT',\n `Signature context must be at most ${MAX_CONTEXT_LENGTH} bytes, got ${options.context.length}`,\n );\n }\n return { context: options.context };\n}\n\n/**\n * Signs data with ML-DSA-65 (FIPS 204) in hedged mode (randomized signing,\n * the standard's default). Returns the 3309-byte signature.\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(document, 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 * Verifies an ML-DSA-65 signature. Returns `false` for invalid or malformed\n * signatures (it never throws because of a corrupted signature); it only\n * throws if the key is not ML-DSA.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const valid = await pqc.verify(document, signature, pair.publicKey);\n * if (!valid) throw new Error('invalid signature');\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 // Validate the context outside the try so an oversized context throws\n // INVALID_CONTEXT (as sign does) instead of being swallowed as `false`.\n const nobleOptions = toNobleOptions(options);\n try {\n return Promise.resolve(\n spec.signer.verify(signature, toBytes(data), publicKey.bytes, nobleOptions),\n );\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["import_utils","utf8","toBytes"]}
package/dist/index.d.cts CHANGED
@@ -92,20 +92,33 @@ declare function generate(options?: GenerateOptions): Promise<KeyPair>;
92
92
  * ```
93
93
  */
94
94
  declare function serialize(key: PqcKey): string;
95
+ /** Asserts the algorithm and use a caller expects from a deserialized key. */
96
+ interface ExpectedKey<A extends Algorithm = Algorithm, U extends KeyUse = KeyUse> {
97
+ readonly algorithm: A;
98
+ readonly use: U;
99
+ }
95
100
  /**
96
101
  * Rebuilds a key from the {@link serialize} format. Validates version,
97
102
  * algorithm, use and length; on any problem it throws {@link PqcError} with
98
103
  * code `INVALID_SERIALIZED_KEY` or `INVALID_KEY`.
99
104
  *
105
+ * Pass `expected` to assert the algorithm and use, getting back a narrow key
106
+ * type (e.g. `PublicKey<'ml-kem-768'>`) that drops straight into `encrypt` /
107
+ * `sign` without an `as never` cast. A mismatch throws `WRONG_ALGORITHM` or
108
+ * `WRONG_KEY_USE`.
109
+ *
100
110
  * @example
101
111
  * ```ts
102
112
  * import { pqc } from '@pqc-sdk/core';
103
113
  *
104
- * const publicKey = pqc.keys.deserialize(tokenReceivedFromClient);
105
- * const ciphertext = await pqc.encrypt(payload, publicKey);
114
+ * const token = pqc.keys.serialize((await pqc.keys.generate()).publicKey);
115
+ * // Narrow to a typed key by asserting the expected algorithm and use:
116
+ * const publicKey = pqc.keys.deserialize(token, { algorithm: 'ml-kem-768', use: 'public' });
117
+ * const ciphertext = await pqc.encrypt('payload', publicKey);
106
118
  * ```
107
119
  */
108
120
  declare function deserialize(serialized: string): PqcKey;
121
+ declare function deserialize<A extends Algorithm, U extends KeyUse>(serialized: string, expected: ExpectedKey<A, U>): PqcKey<A, U>;
109
122
 
110
123
  /**
111
124
  * Signs data with ML-DSA-65 (FIPS 204) in hedged mode (randomized signing,
@@ -136,7 +149,7 @@ declare function sign(data: Uint8Array | string, secretKey: SecretKey<'ml-dsa-65
136
149
  declare function verify(data: Uint8Array | string, signature: Uint8Array, publicKey: PublicKey<'ml-dsa-65'>, options?: SignatureOptions): Promise<boolean>;
137
150
 
138
151
  /** Error codes the SDK can emit. */
139
- type PqcErrorCode = 'UNSUPPORTED_ALGORITHM' | 'WRONG_ALGORITHM' | 'WRONG_KEY_USE' | 'INVALID_KEY' | 'INVALID_SERIALIZED_KEY' | 'INVALID_CIPHERTEXT' | 'DECRYPTION_FAILED';
152
+ type PqcErrorCode = 'UNSUPPORTED_ALGORITHM' | 'WRONG_ALGORITHM' | 'WRONG_KEY_USE' | 'INVALID_KEY' | 'INVALID_SERIALIZED_KEY' | 'INVALID_CONTEXT' | 'INVALID_CIPHERTEXT' | 'DECRYPTION_FAILED';
140
153
  /**
141
154
  * Typed SDK error. Every expected failure exposes a stable `code` so it can
142
155
  * be handled programmatically without parsing messages.
@@ -213,4 +226,4 @@ declare const pqc: {
213
226
  readonly verify: typeof verify;
214
227
  };
215
228
 
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 };
229
+ export { type Algorithm, type ExpectedKey, 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 CHANGED
@@ -92,20 +92,33 @@ declare function generate(options?: GenerateOptions): Promise<KeyPair>;
92
92
  * ```
93
93
  */
94
94
  declare function serialize(key: PqcKey): string;
95
+ /** Asserts the algorithm and use a caller expects from a deserialized key. */
96
+ interface ExpectedKey<A extends Algorithm = Algorithm, U extends KeyUse = KeyUse> {
97
+ readonly algorithm: A;
98
+ readonly use: U;
99
+ }
95
100
  /**
96
101
  * Rebuilds a key from the {@link serialize} format. Validates version,
97
102
  * algorithm, use and length; on any problem it throws {@link PqcError} with
98
103
  * code `INVALID_SERIALIZED_KEY` or `INVALID_KEY`.
99
104
  *
105
+ * Pass `expected` to assert the algorithm and use, getting back a narrow key
106
+ * type (e.g. `PublicKey<'ml-kem-768'>`) that drops straight into `encrypt` /
107
+ * `sign` without an `as never` cast. A mismatch throws `WRONG_ALGORITHM` or
108
+ * `WRONG_KEY_USE`.
109
+ *
100
110
  * @example
101
111
  * ```ts
102
112
  * import { pqc } from '@pqc-sdk/core';
103
113
  *
104
- * const publicKey = pqc.keys.deserialize(tokenReceivedFromClient);
105
- * const ciphertext = await pqc.encrypt(payload, publicKey);
114
+ * const token = pqc.keys.serialize((await pqc.keys.generate()).publicKey);
115
+ * // Narrow to a typed key by asserting the expected algorithm and use:
116
+ * const publicKey = pqc.keys.deserialize(token, { algorithm: 'ml-kem-768', use: 'public' });
117
+ * const ciphertext = await pqc.encrypt('payload', publicKey);
106
118
  * ```
107
119
  */
108
120
  declare function deserialize(serialized: string): PqcKey;
121
+ declare function deserialize<A extends Algorithm, U extends KeyUse>(serialized: string, expected: ExpectedKey<A, U>): PqcKey<A, U>;
109
122
 
110
123
  /**
111
124
  * Signs data with ML-DSA-65 (FIPS 204) in hedged mode (randomized signing,
@@ -136,7 +149,7 @@ declare function sign(data: Uint8Array | string, secretKey: SecretKey<'ml-dsa-65
136
149
  declare function verify(data: Uint8Array | string, signature: Uint8Array, publicKey: PublicKey<'ml-dsa-65'>, options?: SignatureOptions): Promise<boolean>;
137
150
 
138
151
  /** Error codes the SDK can emit. */
139
- type PqcErrorCode = 'UNSUPPORTED_ALGORITHM' | 'WRONG_ALGORITHM' | 'WRONG_KEY_USE' | 'INVALID_KEY' | 'INVALID_SERIALIZED_KEY' | 'INVALID_CIPHERTEXT' | 'DECRYPTION_FAILED';
152
+ type PqcErrorCode = 'UNSUPPORTED_ALGORITHM' | 'WRONG_ALGORITHM' | 'WRONG_KEY_USE' | 'INVALID_KEY' | 'INVALID_SERIALIZED_KEY' | 'INVALID_CONTEXT' | 'INVALID_CIPHERTEXT' | 'DECRYPTION_FAILED';
140
153
  /**
141
154
  * Typed SDK error. Every expected failure exposes a stable `code` so it can
142
155
  * be handled programmatically without parsing messages.
@@ -213,4 +226,4 @@ declare const pqc: {
213
226
  readonly verify: typeof verify;
214
227
  };
215
228
 
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 };
229
+ export { type Algorithm, type ExpectedKey, 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 CHANGED
@@ -85,10 +85,10 @@ async function encrypt(data, publicKey) {
85
85
  const plaintext = toBytes(data);
86
86
  const { cipherText, sharedSecret } = spec.kem.encapsulate(publicKey.bytes);
87
87
  const nonce = randomBytes(NONCE_LENGTH);
88
- const sealed = gcm(sharedSecret, nonce).encrypt(plaintext);
88
+ const header = new Uint8Array([FORMAT_VERSION, spec.headerId]);
89
+ const sealed = gcm(sharedSecret, nonce, header).encrypt(plaintext);
89
90
  const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);
90
- out[0] = FORMAT_VERSION;
91
- out[1] = spec.headerId;
91
+ out.set(header, 0);
92
92
  out.set(cipherText, 2);
93
93
  out.set(nonce, 2 + cipherText.length);
94
94
  out.set(sealed, 2 + cipherText.length + nonce.length);
@@ -109,16 +109,20 @@ async function decrypt(ciphertext, secretKey) {
109
109
  "Unknown header: the ciphertext does not match this version or algorithm"
110
110
  );
111
111
  }
112
+ const header = ciphertext.subarray(0, 2);
112
113
  const kemCiphertext = ciphertext.subarray(2, 2 + spec.ciphertextLength);
113
114
  const nonce = ciphertext.subarray(
114
115
  2 + spec.ciphertextLength,
115
116
  2 + spec.ciphertextLength + NONCE_LENGTH
116
117
  );
117
118
  const sealed = ciphertext.subarray(2 + spec.ciphertextLength + NONCE_LENGTH);
118
- const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);
119
119
  try {
120
- return Promise.resolve(gcm(sharedSecret, nonce).decrypt(sealed));
121
- } catch {
120
+ const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);
121
+ return Promise.resolve(gcm(sharedSecret, nonce, header).decrypt(sealed));
122
+ } catch (cause) {
123
+ if (cause instanceof PqcError) {
124
+ throw cause;
125
+ }
122
126
  throw new PqcError(
123
127
  "DECRYPTION_FAILED",
124
128
  "Decryption failed: tampered ciphertext or wrong secret key"
@@ -166,6 +170,9 @@ function fromBase64Url(encoded) {
166
170
  out[outIndex++] = buffer >> bits & 255;
167
171
  }
168
172
  }
173
+ if ((buffer & (1 << bits) - 1) !== 0) {
174
+ throw new TypeError("Invalid base64url: non-canonical trailing bits");
175
+ }
169
176
  return out;
170
177
  }
171
178
 
@@ -198,7 +205,7 @@ function serialize(key) {
198
205
  }
199
206
  return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;
200
207
  }
201
- function deserialize(serialized) {
208
+ function deserialize(serialized, expected) {
202
209
  const parts = serialized.split(".");
203
210
  if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {
204
211
  throw new PqcError(
@@ -227,16 +234,37 @@ function deserialize(serialized) {
227
234
  `${algorithm} ${use} key must be ${keyLengthFor(spec, key.use)} bytes, got ${bytes.length}`
228
235
  );
229
236
  }
237
+ if (expected !== void 0) {
238
+ if (key.algorithm !== expected.algorithm) {
239
+ throw new PqcError(
240
+ "WRONG_ALGORITHM",
241
+ `Expected an ${expected.algorithm} key, got ${key.algorithm}`
242
+ );
243
+ }
244
+ if (key.use !== expected.use) {
245
+ throw new PqcError("WRONG_KEY_USE", `Expected the ${expected.use} key, got ${key.use}`);
246
+ }
247
+ }
230
248
  return key;
231
249
  }
232
250
 
233
251
  // src/sign.ts
252
+ var MAX_CONTEXT_LENGTH = 255;
234
253
  var utf82 = new TextEncoder();
235
254
  function toBytes2(data) {
236
255
  return typeof data === "string" ? utf82.encode(data) : data;
237
256
  }
238
257
  function toNobleOptions(options) {
239
- return options?.context ? { context: options.context } : void 0;
258
+ if (!options?.context) {
259
+ return void 0;
260
+ }
261
+ if (options.context.length > MAX_CONTEXT_LENGTH) {
262
+ throw new PqcError(
263
+ "INVALID_CONTEXT",
264
+ `Signature context must be at most ${MAX_CONTEXT_LENGTH} bytes, got ${options.context.length}`
265
+ );
266
+ }
267
+ return { context: options.context };
240
268
  }
241
269
  async function sign(data, secretKey, options) {
242
270
  const spec = requireKey(secretKey, "signer", "secret", "sign");
@@ -244,9 +272,10 @@ async function sign(data, secretKey, options) {
244
272
  }
245
273
  async function verify(data, signature, publicKey, options) {
246
274
  const spec = requireKey(publicKey, "signer", "public", "verify");
275
+ const nobleOptions = toNobleOptions(options);
247
276
  try {
248
277
  return Promise.resolve(
249
- spec.signer.verify(signature, toBytes2(data), publicKey.bytes, toNobleOptions(options))
278
+ spec.signer.verify(signature, toBytes2(data), publicKey.bytes, nobleOptions)
250
279
  );
251
280
  } catch {
252
281
  return false;
@@ -254,7 +283,7 @@ async function verify(data, signature, publicKey, options) {
254
283
  }
255
284
 
256
285
  // src/index.ts
257
- var version = "0.1.2";
286
+ var version = "0.3.0";
258
287
  var SUPPORTED_ALGORITHMS = ["ml-kem-768", "ml-dsa-65"];
259
288
  var pqc = {
260
289
  keys: { generate, serialize, deserialize },
package/dist/index.js.map CHANGED
@@ -1 +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 * Hybrid encryption: encapsulates a secret with ML-KEM-768 (FIPS 203) and\n * encrypts the data with AES-256-GCM using that secret. The result is a\n * single self-contained `Uint8Array` that only {@link decrypt} can open.\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('sensitive data', 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 * Decrypts a ciphertext produced by {@link encrypt}. If the ciphertext was\n * tampered with or the key does not match, it throws {@link PqcError} with\n * code `DECRYPTION_FAILED` — it never returns corrupted data.\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(\n 'INVALID_CIPHERTEXT',\n 'Ciphertext is truncated or was not produced by pqc.encrypt',\n );\n }\n if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {\n throw new PqcError(\n 'INVALID_CIPHERTEXT',\n 'Unknown header: the ciphertext does not match this version or algorithm',\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 'Decryption failed: tampered ciphertext or wrong secret key',\n );\n }\n}\n\n/** Available KEM algorithms, exported for introspection. */\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', `Unsupported algorithm: ${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/** Validates a key's algorithm, use and length before operating with it. */\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} requires an ${kind === 'kem' ? 'ML-KEM' : 'ML-DSA'} key, got ${key.algorithm}`,\n );\n }\n if (key.use !== use) {\n throw new PqcError('WRONG_KEY_USE', `${operation} requires the ${use} key, got ${key.use}`);\n }\n if (key.bytes.length !== keyLengthFor(spec, use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `${key.algorithm} ${use} key has invalid length: ${key.bytes.length}`,\n );\n }\n return spec as K extends 'kem' ? KemSpec : SignerSpec;\n}\n","/** Error codes the SDK can emit. */\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 * Typed SDK error. Every expected failure exposes a stable `code` so it can\n * be handled programmatically without parsing messages.\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 * // tampered ciphertext or wrong key\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/** Options for {@link generate}. */\nexport interface GenerateOptions<A extends Algorithm = Algorithm> {\n /** Algorithm of the pair. Default: `'ml-kem-768'` (encryption). */\n readonly algorithm?: A;\n}\n\n/**\n * Generates a post-quantum key pair. With no options it generates ML-KEM-768,\n * ready for `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 * Deterministic generation from a seed. Internal and test use (NIST vectors).\n * Prefer {@link generate} for normal use.\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 `${algorithm} seed must be ${spec.seedLength} bytes, got ${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 * Serializes a key to a portable string: `pqcv1.<algorithm>.<use>.<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.algorithm} ${key.use} key has invalid length`);\n }\n return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;\n}\n\n/**\n * Rebuilds a key from the {@link serialize} format. Validates version,\n * algorithm, use and length; on any problem it throws {@link PqcError} with\n * code `INVALID_SERIALIZED_KEY` or `INVALID_KEY`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const publicKey = pqc.keys.deserialize(tokenReceivedFromClient);\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 'Expected format: pqcv1.<algorithm>.<use>.<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', `Unknown key use: ${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 : 'Invalid base64url',\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 `${algorithm} ${use} key must be ${keyLengthFor(spec, key.use)} bytes, got ${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/** Encodes bytes to unpadded base64url. Pure implementation, no 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/** Decodes unpadded base64url. Throws TypeError on invalid characters. */\nexport function fromBase64Url(encoded: string): Uint8Array {\n if (encoded.length % 4 === 1) {\n throw new TypeError('Invalid base64url: impossible length');\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(`Invalid base64url: character ${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 * Signs data with ML-DSA-65 (FIPS 204) in hedged mode (randomized signing,\n * the standard's default). Returns the 3309-byte signature.\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(document, 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 * Verifies an ML-DSA-65 signature. Returns `false` for invalid or malformed\n * signatures (it never throws because of a corrupted signature); it only\n * throws if the key is not ML-DSA.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const valid = await pqc.verify(document, signature, pair.publicKey);\n * if (!valid) throw new Error('invalid signature');\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// Injected at build time from package.json (`define` in tsup.config.ts and vitest.config.ts).\ndeclare const __PQC_CORE_VERSION__: string;\n\n/**\n * SDK version.\n *\n * @example\n * ```ts\n * import { version } from '@pqc-sdk/core';\n *\n * console.log(version); // e.g. \"0.1.0\"\n * ```\n */\nexport const version = __PQC_CORE_VERSION__;\n\n/**\n * Implemented PQC algorithms (FIPS 203 and 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 * SDK entry point: post-quantum hybrid encryption and digital signatures\n * with safe defaults, zero configuration.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * // Encryption (ML-KEM-768 + AES-256-GCM)\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('hello', pair.publicKey);\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n *\n * // Signatures (ML-DSA-65)\n * const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign('document', signer.secretKey);\n * await pqc.verify('document', 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"]}
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 * Hybrid encryption: encapsulates a secret with ML-KEM-768 (FIPS 203) and\n * encrypts the data with AES-256-GCM using that secret. The result is a\n * single self-contained `Uint8Array` that only {@link decrypt} can open.\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('sensitive data', 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\n // Bind the 2-byte header (FORMAT_VERSION, headerId) as AES-GCM additional\n // authenticated data so it is covered by the GCM tag (see decrypt).\n const header = new Uint8Array([FORMAT_VERSION, spec.headerId]);\n const sealed = gcm(sharedSecret, nonce, header).encrypt(plaintext);\n\n const out = new Uint8Array(2 + cipherText.length + nonce.length + sealed.length);\n out.set(header, 0);\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 * Decrypts a ciphertext produced by {@link encrypt}. If the ciphertext was\n * tampered with or the key does not match, it throws {@link PqcError} with\n * code `DECRYPTION_FAILED` — it never returns corrupted data.\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(\n 'INVALID_CIPHERTEXT',\n 'Ciphertext is truncated or was not produced by pqc.encrypt',\n );\n }\n // This equality check is fail-fast input validation: it discriminates the\n // version and algorithm with a clear INVALID_CIPHERTEXT error before any\n // cryptographic work. The AAD binding below is the *cryptographic* integrity\n // of the header — it becomes the only line of defence once more versions or\n // algorithms share this layout (a tampered-but-known header would pass this\n // check yet fail the GCM tag). Do not remove either guard in a refactor.\n if (ciphertext[0] !== FORMAT_VERSION || ciphertext[1] !== spec.headerId) {\n throw new PqcError(\n 'INVALID_CIPHERTEXT',\n 'Unknown header: the ciphertext does not match this version or algorithm',\n );\n }\n\n // Reconstruct the AAD from the header bytes actually present in the message\n // so AES-GCM authenticates them as part of the tag.\n const header = ciphertext.subarray(0, 2);\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 // decapsulate stays inside the try: ML-KEM uses implicit rejection and does\n // not throw for a valid-length secret key, but any edge case where it (or GCM)\n // throws must still surface as the documented DECRYPTION_FAILED, never a raw\n // upstream error.\n try {\n const sharedSecret = spec.kem.decapsulate(kemCiphertext, secretKey.bytes);\n return Promise.resolve(gcm(sharedSecret, nonce, header).decrypt(sealed));\n } catch (cause) {\n if (cause instanceof PqcError) {\n throw cause;\n }\n throw new PqcError(\n 'DECRYPTION_FAILED',\n 'Decryption failed: tampered ciphertext or wrong secret key',\n );\n }\n}\n\n/** Available KEM algorithms, exported for introspection. */\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', `Unsupported algorithm: ${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/** Validates a key's algorithm, use and length before operating with it. */\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} requires an ${kind === 'kem' ? 'ML-KEM' : 'ML-DSA'} key, got ${key.algorithm}`,\n );\n }\n if (key.use !== use) {\n throw new PqcError('WRONG_KEY_USE', `${operation} requires the ${use} key, got ${key.use}`);\n }\n if (key.bytes.length !== keyLengthFor(spec, use)) {\n throw new PqcError(\n 'INVALID_KEY',\n `${key.algorithm} ${use} key has invalid length: ${key.bytes.length}`,\n );\n }\n return spec as K extends 'kem' ? KemSpec : SignerSpec;\n}\n","/** Error codes the SDK can emit. */\nexport type PqcErrorCode =\n | 'UNSUPPORTED_ALGORITHM'\n | 'WRONG_ALGORITHM'\n | 'WRONG_KEY_USE'\n | 'INVALID_KEY'\n | 'INVALID_SERIALIZED_KEY'\n | 'INVALID_CONTEXT'\n | 'INVALID_CIPHERTEXT'\n | 'DECRYPTION_FAILED';\n\n/**\n * Typed SDK error. Every expected failure exposes a stable `code` so it can\n * be handled programmatically without parsing messages.\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 * // tampered ciphertext or wrong key\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, KeyUse, PqcKey } from './types.js';\n\nconst SERIAL_PREFIX = 'pqcv1';\n\n/** Options for {@link generate}. */\nexport interface GenerateOptions<A extends Algorithm = Algorithm> {\n /** Algorithm of the pair. Default: `'ml-kem-768'` (encryption). */\n readonly algorithm?: A;\n}\n\n/**\n * Generates a post-quantum key pair. With no options it generates ML-KEM-768,\n * ready for `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 * Deterministic generation from a seed. Internal and test use (NIST vectors).\n * Prefer {@link generate} for normal use.\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 `${algorithm} seed must be ${spec.seedLength} bytes, got ${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 * Serializes a key to a portable string: `pqcv1.<algorithm>.<use>.<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.algorithm} ${key.use} key has invalid length`);\n }\n return `${SERIAL_PREFIX}.${key.algorithm}.${key.use}.${toBase64Url(key.bytes)}`;\n}\n\n/** Asserts the algorithm and use a caller expects from a deserialized key. */\nexport interface ExpectedKey<A extends Algorithm = Algorithm, U extends KeyUse = KeyUse> {\n readonly algorithm: A;\n readonly use: U;\n}\n\n/**\n * Rebuilds a key from the {@link serialize} format. Validates version,\n * algorithm, use and length; on any problem it throws {@link PqcError} with\n * code `INVALID_SERIALIZED_KEY` or `INVALID_KEY`.\n *\n * Pass `expected` to assert the algorithm and use, getting back a narrow key\n * type (e.g. `PublicKey<'ml-kem-768'>`) that drops straight into `encrypt` /\n * `sign` without an `as never` cast. A mismatch throws `WRONG_ALGORITHM` or\n * `WRONG_KEY_USE`.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const token = pqc.keys.serialize((await pqc.keys.generate()).publicKey);\n * // Narrow to a typed key by asserting the expected algorithm and use:\n * const publicKey = pqc.keys.deserialize(token, { algorithm: 'ml-kem-768', use: 'public' });\n * const ciphertext = await pqc.encrypt('payload', publicKey);\n * ```\n */\nexport function deserialize(serialized: string): PqcKey;\nexport function deserialize<A extends Algorithm, U extends KeyUse>(\n serialized: string,\n expected: ExpectedKey<A, U>,\n): PqcKey<A, U>;\nexport function deserialize(serialized: string, expected?: ExpectedKey): PqcKey {\n const parts = serialized.split('.');\n if (parts.length !== 4 || parts[0] !== SERIAL_PREFIX) {\n throw new PqcError(\n 'INVALID_SERIALIZED_KEY',\n 'Expected format: pqcv1.<algorithm>.<use>.<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', `Unknown key use: ${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 : 'Invalid base64url',\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 `${algorithm} ${use} key must be ${keyLengthFor(spec, key.use)} bytes, got ${bytes.length}`,\n );\n }\n if (expected !== undefined) {\n if (key.algorithm !== expected.algorithm) {\n throw new PqcError(\n 'WRONG_ALGORITHM',\n `Expected an ${expected.algorithm} key, got ${key.algorithm}`,\n );\n }\n if (key.use !== expected.use) {\n throw new PqcError('WRONG_KEY_USE', `Expected the ${expected.use} key, got ${key.use}`);\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/** Encodes bytes to unpadded base64url. Pure implementation, no 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/** Decodes unpadded base64url. Throws TypeError on invalid characters. */\nexport function fromBase64Url(encoded: string): Uint8Array {\n if (encoded.length % 4 === 1) {\n throw new TypeError('Invalid base64url: impossible length');\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(`Invalid base64url: character ${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 // Reject non-canonical input: the leftover bits of the final group (those that\n // do not complete a byte) must be zero, otherwise the string is not the\n // canonical encoding of any byte sequence.\n if ((buffer & ((1 << bits) - 1)) !== 0) {\n throw new TypeError('Invalid base64url: non-canonical trailing bits');\n }\n return out;\n}\n","import { requireKey } from './algorithms.js';\nimport { PqcError } from './errors.js';\nimport type { PublicKey, SecretKey, SignatureOptions } from './types.js';\n\n/** FIPS 204 §5.2 caps the signing context string at 255 bytes. */\nconst MAX_CONTEXT_LENGTH = 255;\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 if (!options?.context) {\n return undefined;\n }\n if (options.context.length > MAX_CONTEXT_LENGTH) {\n throw new PqcError(\n 'INVALID_CONTEXT',\n `Signature context must be at most ${MAX_CONTEXT_LENGTH} bytes, got ${options.context.length}`,\n );\n }\n return { context: options.context };\n}\n\n/**\n * Signs data with ML-DSA-65 (FIPS 204) in hedged mode (randomized signing,\n * the standard's default). Returns the 3309-byte signature.\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(document, 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 * Verifies an ML-DSA-65 signature. Returns `false` for invalid or malformed\n * signatures (it never throws because of a corrupted signature); it only\n * throws if the key is not ML-DSA.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * const valid = await pqc.verify(document, signature, pair.publicKey);\n * if (!valid) throw new Error('invalid signature');\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 // Validate the context outside the try so an oversized context throws\n // INVALID_CONTEXT (as sign does) instead of being swallowed as `false`.\n const nobleOptions = toNobleOptions(options);\n try {\n return Promise.resolve(\n spec.signer.verify(signature, toBytes(data), publicKey.bytes, nobleOptions),\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 { ExpectedKey, 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// Injected at build time from package.json (`define` in tsup.config.ts and vitest.config.ts).\ndeclare const __PQC_CORE_VERSION__: string;\n\n/**\n * SDK version.\n *\n * @example\n * ```ts\n * import { version } from '@pqc-sdk/core';\n *\n * console.log(version); // e.g. \"0.1.0\"\n * ```\n */\nexport const version = __PQC_CORE_VERSION__;\n\n/**\n * Implemented PQC algorithms (FIPS 203 and 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 * SDK entry point: post-quantum hybrid encryption and digital signatures\n * with safe defaults, zero configuration.\n *\n * @example\n * ```ts\n * import { pqc } from '@pqc-sdk/core';\n *\n * // Encryption (ML-KEM-768 + AES-256-GCM)\n * const pair = await pqc.keys.generate();\n * const ciphertext = await pqc.encrypt('hello', pair.publicKey);\n * const plaintext = await pqc.decrypt(ciphertext, pair.secretKey);\n *\n * // Signatures (ML-DSA-65)\n * const signer = await pqc.keys.generate({ algorithm: 'ml-dsa-65' });\n * const signature = await pqc.sign('document', signer.secretKey);\n * await pqc.verify('document', 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pqc-sdk/core",
3
- "version": "0.1.2",
4
- "description": "Post-quantum cryptography SDK for JS/TS (ML-KEM, ML-DSA, SLH-DSA)",
3
+ "version": "0.3.0",
4
+ "description": "Post-quantum cryptography SDK for JS/TS (ML-KEM, ML-DSA)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -27,8 +27,8 @@
27
27
  "vitest": "^3.2.2"
28
28
  },
29
29
  "dependencies": {
30
- "@noble/ciphers": "^2.2.0",
31
- "@noble/post-quantum": "^0.6.1"
30
+ "@noble/ciphers": "2.2.0",
31
+ "@noble/post-quantum": "0.6.1"
32
32
  },
33
33
  "license": "MIT",
34
34
  "homepage": "https://jeloercc.github.io/pqc-sdk/",