@cardanowall/crypto-core 0.2.0 → 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/kem.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/kem/x25519.ts","../src/kem/mlkem768x25519.ts"],"names":["x25519","XWing"],"mappings":"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","file":"kem.cjs","sourcesContent":["import { x25519 } from '@noble/curves/ed25519.js';\n\n// RFC 7748 §6.1 contributory-behaviour rejection: a small-order (low-order)\n// Montgomery `u` coordinate makes the X25519 shared secret all-zero, which\n// @noble/curves refuses with `Error: invalid private or public key received`.\n// We rethrow that as a *typed* error so callers can distinguish a structurally\n// valid-but-malicious peer public key (a property of attacker-supplied wire\n// data — trial-decrypt MUST treat the slot as a non-match, not crash) from\n// genuine caller misuse such as a wrong-length key (which @noble raises as a\n// RangeError and which we deliberately let propagate untouched).\nexport class X25519LowOrderPointError extends Error {\n readonly code = 'X25519_LOW_ORDER_POINT' as const;\n constructor(options?: { cause?: unknown }) {\n super('x25519 ECDH rejected: peer public key is a small-order point', options);\n this.name = 'X25519LowOrderPointError';\n }\n}\n\n// @noble/curves v2 signals a small-order/all-zero shared secret with this exact\n// message. Matching on it (rather than the broad Error class) keeps unrelated\n// failures — e.g. a future internal assertion — surfacing as themselves.\nconst NOBLE_LOW_ORDER_MESSAGE = 'invalid private or public key received';\n\nexport interface X25519KeyPair {\n readonly secretKey: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface X25519PublicKeyOpts {\n readonly secretKey: Uint8Array;\n}\n\nexport interface X25519EcdhOpts {\n readonly secretKey: Uint8Array;\n readonly theirPublicKey: Uint8Array;\n}\n\nexport function x25519Keygen(): X25519KeyPair {\n return x25519.keygen();\n}\n\nexport function x25519PublicKey(opts: X25519PublicKeyOpts): Uint8Array {\n return x25519.getPublicKey(opts.secretKey);\n}\n\nexport function x25519Ecdh(opts: X25519EcdhOpts): Uint8Array {\n try {\n return x25519.getSharedSecret(opts.secretKey, opts.theirPublicKey);\n } catch (e) {\n // Translate ONLY the contributory-check rejection into our typed error.\n // A wrong-length key throws a RangeError from @noble's length assertion;\n // that is caller misuse, not malicious wire data, so it must propagate.\n if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {\n throw new X25519LowOrderPointError({ cause: e });\n }\n throw e;\n }\n}\n","import { XWing } from '@noble/post-quantum/hybrid.js';\n\n// X-Wing (ML-KEM-768 + X25519) hybrid KEM per draft-connolly-cfrg-xwing-kem-06.\n// `XWing` is @noble/post-quantum's alias for `ml_kem768_x25519`. We expose it\n// through opts-object wrappers that pin the wire lengths and map noble's field\n// names onto the project's vocabulary.\n//\n// Unlike the bare X25519 KEM, there is no contributory-behaviour rejection to\n// translate: X-Wing combines the ML-KEM and X25519 shared secrets through a\n// SHA3-256 combiner that also binds the X25519 ephemeral and recipient public\n// keys, and ML-KEM's implicit rejection already yields a constant-work\n// pseudorandom secret on a malformed ciphertext. Decapsulation therefore never\n// throws on attacker-supplied wire data — a wrong shared secret is the correct,\n// indistinguishable failure mode, and callers MUST treat it as a non-match\n// rather than expecting an exception.\n\nexport const MLKEM768X25519_PUBLIC_KEY_LENGTH = 1216 as const;\nexport const MLKEM768X25519_ENC_LENGTH = 1120 as const;\nexport const MLKEM768X25519_SHARED_SECRET_LENGTH = 32 as const;\nexport const MLKEM768X25519_SEED_LENGTH = 32 as const;\nexport const MLKEM768X25519_ESEED_LENGTH = 64 as const;\n\nexport interface Mlkem768X25519KeyPair {\n // The 32-byte root seed IS the secret key in draft-06: the ML-KEM coins and\n // the X25519 scalar are re-expanded from it via SHAKE-256 at decapsulation.\n readonly secretSeed: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface Mlkem768X25519EncapsulateOpts {\n readonly publicKey: Uint8Array;\n // Optional 64-byte encapsulation randomness (msgRand). When supplied the\n // ciphertext and shared secret are fully deterministic; a 32-byte value is\n // rejected by noble, so we pin the length here too.\n readonly eseed?: Uint8Array;\n}\n\nexport interface Mlkem768X25519Encapsulation {\n readonly enc: Uint8Array;\n readonly ss: Uint8Array;\n}\n\nexport interface Mlkem768X25519DecapsulateOpts {\n readonly secretSeed: Uint8Array;\n readonly enc: Uint8Array;\n}\n\nexport function mlkem768x25519Keygen(seed: Uint8Array): Mlkem768X25519KeyPair {\n if (seed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${seed.length}`,\n );\n }\n const { secretKey, publicKey } = XWing.keygen(seed);\n return { secretSeed: secretKey, publicKey };\n}\n\nexport function mlkem768x25519Encapsulate(\n opts: Mlkem768X25519EncapsulateOpts,\n): Mlkem768X25519Encapsulation {\n if (opts.publicKey.length !== MLKEM768X25519_PUBLIC_KEY_LENGTH) {\n throw new Error(\n `mlkem768x25519 public key must be ${MLKEM768X25519_PUBLIC_KEY_LENGTH} bytes, got ${opts.publicKey.length}`,\n );\n }\n if (opts.eseed !== undefined && opts.eseed.length !== MLKEM768X25519_ESEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 eseed must be ${MLKEM768X25519_ESEED_LENGTH} bytes, got ${opts.eseed.length}`,\n );\n }\n const { cipherText, sharedSecret } = XWing.encapsulate(opts.publicKey, opts.eseed);\n return { enc: cipherText, ss: sharedSecret };\n}\n\nexport function mlkem768x25519Decapsulate(opts: Mlkem768X25519DecapsulateOpts): Uint8Array {\n // Pre-check both lengths before calling noble: decapsulation must perform a\n // constant amount of work for any caller-supplied ciphertext (implicit\n // rejection), which requires the inputs to be the exact expected sizes.\n if (opts.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts.secretSeed.length}`,\n );\n }\n if (opts.enc.length !== MLKEM768X25519_ENC_LENGTH) {\n throw new Error(\n `mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts.enc.length}`,\n );\n }\n // noble's signature is decapsulate(cipherText, secretKey) — ciphertext first.\n return XWing.decapsulate(opts.enc, opts.secretSeed);\n}\n"]}
1
+ {"version":3,"sources":["../src/kem/x25519.ts","../src/kem/mlkem768x25519.ts"],"names":["x25519","XWing"],"mappings":"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","file":"kem.cjs","sourcesContent":["import { x25519 } from '@noble/curves/ed25519.js';\n\n// RFC 7748 §6.1 contributory-behaviour rejection: a small-order (low-order)\n// Montgomery `u` coordinate makes the X25519 shared secret all-zero, which\n// @noble/curves refuses with `Error: invalid private or public key received`.\n// We rethrow that as a *typed* error so callers can distinguish a structurally\n// valid-but-malicious peer public key (a property of attacker-supplied wire\n// data — trial-decrypt MUST treat the slot as a non-match, not crash) from\n// genuine caller misuse such as a wrong-length key (which @noble raises as a\n// RangeError and which we deliberately let propagate untouched).\nexport class X25519LowOrderPointError extends Error {\n readonly code = 'X25519_LOW_ORDER_POINT' as const;\n constructor(options?: { cause?: unknown }) {\n super('x25519 ECDH rejected: peer public key is a small-order point', options);\n this.name = 'X25519LowOrderPointError';\n }\n}\n\n// @noble/curves v2 signals a small-order/all-zero shared secret with this exact\n// message. Matching on it (rather than the broad Error class) keeps unrelated\n// failures — e.g. a future internal assertion — surfacing as themselves.\nconst NOBLE_LOW_ORDER_MESSAGE = 'invalid private or public key received';\n\nexport interface X25519KeyPair {\n readonly secretKey: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface X25519PublicKeyOpts {\n readonly secretKey: Uint8Array;\n}\n\nexport interface X25519EcdhOpts {\n readonly secretKey: Uint8Array;\n readonly theirPublicKey: Uint8Array;\n}\n\nexport function x25519Keygen(): X25519KeyPair {\n return x25519.keygen();\n}\n\nexport function x25519PublicKey(opts: X25519PublicKeyOpts): Uint8Array {\n return x25519.getPublicKey(opts.secretKey);\n}\n\nexport function x25519Ecdh(opts: X25519EcdhOpts): Uint8Array {\n try {\n return x25519.getSharedSecret(opts.secretKey, opts.theirPublicKey);\n } catch (e) {\n // Translate ONLY the contributory-check rejection into our typed error.\n // A wrong-length key throws a RangeError from @noble's length assertion;\n // that is caller misuse, not malicious wire data, so it must propagate.\n if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {\n throw new X25519LowOrderPointError({ cause: e });\n }\n throw e;\n }\n}\n","import { XWing } from '@noble/post-quantum/hybrid.js';\n\n// X-Wing (ML-KEM-768 + X25519) hybrid KEM per draft-connolly-cfrg-xwing-kem-10.\n// `XWing` is @noble/post-quantum's alias for `ml_kem768_x25519`. We expose it\n// through opts-object wrappers that pin the wire lengths and map noble's field\n// names onto the project's vocabulary.\n//\n// Unlike the bare X25519 KEM, there is no contributory-behaviour rejection to\n// translate: X-Wing combines the ML-KEM and X25519 shared secrets through a\n// SHA3-256 combiner that also binds the X25519 ephemeral and recipient public\n// keys, and ML-KEM's implicit rejection already yields a constant-work\n// pseudorandom secret on a malformed ciphertext. Decapsulation therefore never\n// throws on attacker-supplied wire data — a wrong shared secret is the correct,\n// indistinguishable failure mode, and callers MUST treat it as a non-match\n// rather than expecting an exception.\n\nexport const MLKEM768X25519_PUBLIC_KEY_LENGTH = 1216 as const;\nexport const MLKEM768X25519_ENC_LENGTH = 1120 as const;\nexport const MLKEM768X25519_SHARED_SECRET_LENGTH = 32 as const;\nexport const MLKEM768X25519_SEED_LENGTH = 32 as const;\nexport const MLKEM768X25519_ESEED_LENGTH = 64 as const;\n\nexport interface Mlkem768X25519KeyPair {\n // The 32-byte root seed IS the secret key: the ML-KEM coins and the X25519\n // scalar are re-expanded from it via SHAKE-256 at decapsulation. (Later X-Wing\n // drafts also define an optional expanded decapsulation-key form; we keep the\n // seed-only key, which the draft-10 Appendix C vectors still pin.)\n readonly secretSeed: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface Mlkem768X25519EncapsulateOpts {\n readonly publicKey: Uint8Array;\n // Optional 64-byte encapsulation randomness (msgRand). When supplied the\n // ciphertext and shared secret are fully deterministic; a 32-byte value is\n // rejected by noble, so we pin the length here too.\n readonly eseed?: Uint8Array;\n}\n\nexport interface Mlkem768X25519Encapsulation {\n readonly enc: Uint8Array;\n readonly ss: Uint8Array;\n}\n\nexport interface Mlkem768X25519DecapsulateOpts {\n readonly secretSeed: Uint8Array;\n readonly enc: Uint8Array;\n}\n\nexport function mlkem768x25519Keygen(seed: Uint8Array): Mlkem768X25519KeyPair {\n if (seed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${seed.length}`,\n );\n }\n const { secretKey, publicKey } = XWing.keygen(seed);\n return { secretSeed: secretKey, publicKey };\n}\n\nexport function mlkem768x25519Encapsulate(\n opts: Mlkem768X25519EncapsulateOpts,\n): Mlkem768X25519Encapsulation {\n if (opts.publicKey.length !== MLKEM768X25519_PUBLIC_KEY_LENGTH) {\n throw new Error(\n `mlkem768x25519 public key must be ${MLKEM768X25519_PUBLIC_KEY_LENGTH} bytes, got ${opts.publicKey.length}`,\n );\n }\n if (opts.eseed !== undefined && opts.eseed.length !== MLKEM768X25519_ESEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 eseed must be ${MLKEM768X25519_ESEED_LENGTH} bytes, got ${opts.eseed.length}`,\n );\n }\n const { cipherText, sharedSecret } = XWing.encapsulate(opts.publicKey, opts.eseed);\n return { enc: cipherText, ss: sharedSecret };\n}\n\nexport function mlkem768x25519Decapsulate(opts: Mlkem768X25519DecapsulateOpts): Uint8Array {\n // Pre-check both lengths before calling noble: decapsulation must perform a\n // constant amount of work for any caller-supplied ciphertext (implicit\n // rejection), which requires the inputs to be the exact expected sizes.\n if (opts.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts.secretSeed.length}`,\n );\n }\n if (opts.enc.length !== MLKEM768X25519_ENC_LENGTH) {\n throw new Error(\n `mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts.enc.length}`,\n );\n }\n // noble's signature is decapsulate(cipherText, secretKey) — ciphertext first.\n return XWing.decapsulate(opts.enc, opts.secretSeed);\n}\n"]}
package/dist/kem.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/kem/x25519.ts","../src/kem/mlkem768x25519.ts"],"names":[],"mappings":"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","file":"kem.js","sourcesContent":["import { x25519 } from '@noble/curves/ed25519.js';\n\n// RFC 7748 §6.1 contributory-behaviour rejection: a small-order (low-order)\n// Montgomery `u` coordinate makes the X25519 shared secret all-zero, which\n// @noble/curves refuses with `Error: invalid private or public key received`.\n// We rethrow that as a *typed* error so callers can distinguish a structurally\n// valid-but-malicious peer public key (a property of attacker-supplied wire\n// data — trial-decrypt MUST treat the slot as a non-match, not crash) from\n// genuine caller misuse such as a wrong-length key (which @noble raises as a\n// RangeError and which we deliberately let propagate untouched).\nexport class X25519LowOrderPointError extends Error {\n readonly code = 'X25519_LOW_ORDER_POINT' as const;\n constructor(options?: { cause?: unknown }) {\n super('x25519 ECDH rejected: peer public key is a small-order point', options);\n this.name = 'X25519LowOrderPointError';\n }\n}\n\n// @noble/curves v2 signals a small-order/all-zero shared secret with this exact\n// message. Matching on it (rather than the broad Error class) keeps unrelated\n// failures — e.g. a future internal assertion — surfacing as themselves.\nconst NOBLE_LOW_ORDER_MESSAGE = 'invalid private or public key received';\n\nexport interface X25519KeyPair {\n readonly secretKey: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface X25519PublicKeyOpts {\n readonly secretKey: Uint8Array;\n}\n\nexport interface X25519EcdhOpts {\n readonly secretKey: Uint8Array;\n readonly theirPublicKey: Uint8Array;\n}\n\nexport function x25519Keygen(): X25519KeyPair {\n return x25519.keygen();\n}\n\nexport function x25519PublicKey(opts: X25519PublicKeyOpts): Uint8Array {\n return x25519.getPublicKey(opts.secretKey);\n}\n\nexport function x25519Ecdh(opts: X25519EcdhOpts): Uint8Array {\n try {\n return x25519.getSharedSecret(opts.secretKey, opts.theirPublicKey);\n } catch (e) {\n // Translate ONLY the contributory-check rejection into our typed error.\n // A wrong-length key throws a RangeError from @noble's length assertion;\n // that is caller misuse, not malicious wire data, so it must propagate.\n if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {\n throw new X25519LowOrderPointError({ cause: e });\n }\n throw e;\n }\n}\n","import { XWing } from '@noble/post-quantum/hybrid.js';\n\n// X-Wing (ML-KEM-768 + X25519) hybrid KEM per draft-connolly-cfrg-xwing-kem-06.\n// `XWing` is @noble/post-quantum's alias for `ml_kem768_x25519`. We expose it\n// through opts-object wrappers that pin the wire lengths and map noble's field\n// names onto the project's vocabulary.\n//\n// Unlike the bare X25519 KEM, there is no contributory-behaviour rejection to\n// translate: X-Wing combines the ML-KEM and X25519 shared secrets through a\n// SHA3-256 combiner that also binds the X25519 ephemeral and recipient public\n// keys, and ML-KEM's implicit rejection already yields a constant-work\n// pseudorandom secret on a malformed ciphertext. Decapsulation therefore never\n// throws on attacker-supplied wire data — a wrong shared secret is the correct,\n// indistinguishable failure mode, and callers MUST treat it as a non-match\n// rather than expecting an exception.\n\nexport const MLKEM768X25519_PUBLIC_KEY_LENGTH = 1216 as const;\nexport const MLKEM768X25519_ENC_LENGTH = 1120 as const;\nexport const MLKEM768X25519_SHARED_SECRET_LENGTH = 32 as const;\nexport const MLKEM768X25519_SEED_LENGTH = 32 as const;\nexport const MLKEM768X25519_ESEED_LENGTH = 64 as const;\n\nexport interface Mlkem768X25519KeyPair {\n // The 32-byte root seed IS the secret key in draft-06: the ML-KEM coins and\n // the X25519 scalar are re-expanded from it via SHAKE-256 at decapsulation.\n readonly secretSeed: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface Mlkem768X25519EncapsulateOpts {\n readonly publicKey: Uint8Array;\n // Optional 64-byte encapsulation randomness (msgRand). When supplied the\n // ciphertext and shared secret are fully deterministic; a 32-byte value is\n // rejected by noble, so we pin the length here too.\n readonly eseed?: Uint8Array;\n}\n\nexport interface Mlkem768X25519Encapsulation {\n readonly enc: Uint8Array;\n readonly ss: Uint8Array;\n}\n\nexport interface Mlkem768X25519DecapsulateOpts {\n readonly secretSeed: Uint8Array;\n readonly enc: Uint8Array;\n}\n\nexport function mlkem768x25519Keygen(seed: Uint8Array): Mlkem768X25519KeyPair {\n if (seed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${seed.length}`,\n );\n }\n const { secretKey, publicKey } = XWing.keygen(seed);\n return { secretSeed: secretKey, publicKey };\n}\n\nexport function mlkem768x25519Encapsulate(\n opts: Mlkem768X25519EncapsulateOpts,\n): Mlkem768X25519Encapsulation {\n if (opts.publicKey.length !== MLKEM768X25519_PUBLIC_KEY_LENGTH) {\n throw new Error(\n `mlkem768x25519 public key must be ${MLKEM768X25519_PUBLIC_KEY_LENGTH} bytes, got ${opts.publicKey.length}`,\n );\n }\n if (opts.eseed !== undefined && opts.eseed.length !== MLKEM768X25519_ESEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 eseed must be ${MLKEM768X25519_ESEED_LENGTH} bytes, got ${opts.eseed.length}`,\n );\n }\n const { cipherText, sharedSecret } = XWing.encapsulate(opts.publicKey, opts.eseed);\n return { enc: cipherText, ss: sharedSecret };\n}\n\nexport function mlkem768x25519Decapsulate(opts: Mlkem768X25519DecapsulateOpts): Uint8Array {\n // Pre-check both lengths before calling noble: decapsulation must perform a\n // constant amount of work for any caller-supplied ciphertext (implicit\n // rejection), which requires the inputs to be the exact expected sizes.\n if (opts.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts.secretSeed.length}`,\n );\n }\n if (opts.enc.length !== MLKEM768X25519_ENC_LENGTH) {\n throw new Error(\n `mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts.enc.length}`,\n );\n }\n // noble's signature is decapsulate(cipherText, secretKey) — ciphertext first.\n return XWing.decapsulate(opts.enc, opts.secretSeed);\n}\n"]}
1
+ {"version":3,"sources":["../src/kem/x25519.ts","../src/kem/mlkem768x25519.ts"],"names":[],"mappings":"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","file":"kem.js","sourcesContent":["import { x25519 } from '@noble/curves/ed25519.js';\n\n// RFC 7748 §6.1 contributory-behaviour rejection: a small-order (low-order)\n// Montgomery `u` coordinate makes the X25519 shared secret all-zero, which\n// @noble/curves refuses with `Error: invalid private or public key received`.\n// We rethrow that as a *typed* error so callers can distinguish a structurally\n// valid-but-malicious peer public key (a property of attacker-supplied wire\n// data — trial-decrypt MUST treat the slot as a non-match, not crash) from\n// genuine caller misuse such as a wrong-length key (which @noble raises as a\n// RangeError and which we deliberately let propagate untouched).\nexport class X25519LowOrderPointError extends Error {\n readonly code = 'X25519_LOW_ORDER_POINT' as const;\n constructor(options?: { cause?: unknown }) {\n super('x25519 ECDH rejected: peer public key is a small-order point', options);\n this.name = 'X25519LowOrderPointError';\n }\n}\n\n// @noble/curves v2 signals a small-order/all-zero shared secret with this exact\n// message. Matching on it (rather than the broad Error class) keeps unrelated\n// failures — e.g. a future internal assertion — surfacing as themselves.\nconst NOBLE_LOW_ORDER_MESSAGE = 'invalid private or public key received';\n\nexport interface X25519KeyPair {\n readonly secretKey: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface X25519PublicKeyOpts {\n readonly secretKey: Uint8Array;\n}\n\nexport interface X25519EcdhOpts {\n readonly secretKey: Uint8Array;\n readonly theirPublicKey: Uint8Array;\n}\n\nexport function x25519Keygen(): X25519KeyPair {\n return x25519.keygen();\n}\n\nexport function x25519PublicKey(opts: X25519PublicKeyOpts): Uint8Array {\n return x25519.getPublicKey(opts.secretKey);\n}\n\nexport function x25519Ecdh(opts: X25519EcdhOpts): Uint8Array {\n try {\n return x25519.getSharedSecret(opts.secretKey, opts.theirPublicKey);\n } catch (e) {\n // Translate ONLY the contributory-check rejection into our typed error.\n // A wrong-length key throws a RangeError from @noble's length assertion;\n // that is caller misuse, not malicious wire data, so it must propagate.\n if (e instanceof Error && e.message === NOBLE_LOW_ORDER_MESSAGE) {\n throw new X25519LowOrderPointError({ cause: e });\n }\n throw e;\n }\n}\n","import { XWing } from '@noble/post-quantum/hybrid.js';\n\n// X-Wing (ML-KEM-768 + X25519) hybrid KEM per draft-connolly-cfrg-xwing-kem-10.\n// `XWing` is @noble/post-quantum's alias for `ml_kem768_x25519`. We expose it\n// through opts-object wrappers that pin the wire lengths and map noble's field\n// names onto the project's vocabulary.\n//\n// Unlike the bare X25519 KEM, there is no contributory-behaviour rejection to\n// translate: X-Wing combines the ML-KEM and X25519 shared secrets through a\n// SHA3-256 combiner that also binds the X25519 ephemeral and recipient public\n// keys, and ML-KEM's implicit rejection already yields a constant-work\n// pseudorandom secret on a malformed ciphertext. Decapsulation therefore never\n// throws on attacker-supplied wire data — a wrong shared secret is the correct,\n// indistinguishable failure mode, and callers MUST treat it as a non-match\n// rather than expecting an exception.\n\nexport const MLKEM768X25519_PUBLIC_KEY_LENGTH = 1216 as const;\nexport const MLKEM768X25519_ENC_LENGTH = 1120 as const;\nexport const MLKEM768X25519_SHARED_SECRET_LENGTH = 32 as const;\nexport const MLKEM768X25519_SEED_LENGTH = 32 as const;\nexport const MLKEM768X25519_ESEED_LENGTH = 64 as const;\n\nexport interface Mlkem768X25519KeyPair {\n // The 32-byte root seed IS the secret key: the ML-KEM coins and the X25519\n // scalar are re-expanded from it via SHAKE-256 at decapsulation. (Later X-Wing\n // drafts also define an optional expanded decapsulation-key form; we keep the\n // seed-only key, which the draft-10 Appendix C vectors still pin.)\n readonly secretSeed: Uint8Array;\n readonly publicKey: Uint8Array;\n}\n\nexport interface Mlkem768X25519EncapsulateOpts {\n readonly publicKey: Uint8Array;\n // Optional 64-byte encapsulation randomness (msgRand). When supplied the\n // ciphertext and shared secret are fully deterministic; a 32-byte value is\n // rejected by noble, so we pin the length here too.\n readonly eseed?: Uint8Array;\n}\n\nexport interface Mlkem768X25519Encapsulation {\n readonly enc: Uint8Array;\n readonly ss: Uint8Array;\n}\n\nexport interface Mlkem768X25519DecapsulateOpts {\n readonly secretSeed: Uint8Array;\n readonly enc: Uint8Array;\n}\n\nexport function mlkem768x25519Keygen(seed: Uint8Array): Mlkem768X25519KeyPair {\n if (seed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${seed.length}`,\n );\n }\n const { secretKey, publicKey } = XWing.keygen(seed);\n return { secretSeed: secretKey, publicKey };\n}\n\nexport function mlkem768x25519Encapsulate(\n opts: Mlkem768X25519EncapsulateOpts,\n): Mlkem768X25519Encapsulation {\n if (opts.publicKey.length !== MLKEM768X25519_PUBLIC_KEY_LENGTH) {\n throw new Error(\n `mlkem768x25519 public key must be ${MLKEM768X25519_PUBLIC_KEY_LENGTH} bytes, got ${opts.publicKey.length}`,\n );\n }\n if (opts.eseed !== undefined && opts.eseed.length !== MLKEM768X25519_ESEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 eseed must be ${MLKEM768X25519_ESEED_LENGTH} bytes, got ${opts.eseed.length}`,\n );\n }\n const { cipherText, sharedSecret } = XWing.encapsulate(opts.publicKey, opts.eseed);\n return { enc: cipherText, ss: sharedSecret };\n}\n\nexport function mlkem768x25519Decapsulate(opts: Mlkem768X25519DecapsulateOpts): Uint8Array {\n // Pre-check both lengths before calling noble: decapsulation must perform a\n // constant amount of work for any caller-supplied ciphertext (implicit\n // rejection), which requires the inputs to be the exact expected sizes.\n if (opts.secretSeed.length !== MLKEM768X25519_SEED_LENGTH) {\n throw new Error(\n `mlkem768x25519 secret seed must be ${MLKEM768X25519_SEED_LENGTH} bytes, got ${opts.secretSeed.length}`,\n );\n }\n if (opts.enc.length !== MLKEM768X25519_ENC_LENGTH) {\n throw new Error(\n `mlkem768x25519 enc must be ${MLKEM768X25519_ENC_LENGTH} bytes, got ${opts.enc.length}`,\n );\n }\n // noble's signature is decapsulate(cipherText, secretKey) — ciphertext first.\n return XWing.decapsulate(opts.enc, opts.secretSeed);\n}\n"]}