diff --git a/docs/content/limitations/builtin-apis/crypto.mdx b/docs/content/limitations/builtin-apis/crypto.mdx index 3c830c63d..b07cd6962 100644 --- a/docs/content/limitations/builtin-apis/crypto.mdx +++ b/docs/content/limitations/builtin-apis/crypto.mdx @@ -15,5 +15,11 @@ `createECDH` and `ECDH.convertKey` support prime192v1, secp224r1, prime256v1, secp384r1, secp521r1, secp192k1, secp224k1, secp256k1, brainpoolP256r1, brainpoolP384r1, and brainpoolP512r1, on native targets, through `generateKeys()`, `computeSecret()`, `getPublicKey()`, `getPrivateKey()`, `setPrivateKey()`, and `setPublicKey()`, in compressed, uncompressed, and hybrid forms; the other curves `crypto.getCurves()` lists and wasm32-wasi builds are refused. A call on an ECDH value whose arguments no @types/node overload accepts (`getPublicKey("HEX")`) is refused at compile time, as is `computeSecret(view, encoding)`: Node reads that encoding as the input encoding and answers a Buffer where @types/node declares a string. ECDH property reads and writes throw a coded SC2020 error. + `createDiffieHellman`, `getDiffieHellman`, and `createDiffieHellmanGroup` support the modp1, modp2, modp5, and modp14 through modp18 groups, explicit primes and generators, and generated safe primes, on native targets, through `generateKeys()`, `computeSecret()`, `getPrime()`, `getGenerator()`, `getPublicKey()`, `getPrivateKey()`, `setPublicKey()`, `setPrivateKey()`, and `verifyError`, with Node's private key lengths, DH_check results, and errors; wasm32-wasi builds are refused. + Primes longer than 8192 bits throw a coded SC2020 error; Node accepts primes up to 10000 bits for key generation and secrets. + A call on a DiffieHellman value whose arguments no @types/node overload accepts, or whose output encoding is typed `any`, is refused at compile time, as are its `Object.prototype` methods; other property reads, and property writes, throw a coded SC2020 error. + `Object.keys()` and `in` do not see a DiffieHellman's own `verifyError` property, and `JSON.stringify()` and `instanceof` are refused. + After `createDiffieHellman(primeLength)` throws `Invalid DH parameters`, the next OpenSSL error Node throws can list the failed generation's errors in `opensslErrorStack`; errors in a compiled binary list only their own. + `crypto.diffieHellman`, which takes asymmetric KeyObjects, and the `DiffieHellman` and `DiffieHellmanGroup` constructors are not available. Other public-key operations remain unsupported. Native derivations finish before their deferred callbacks, so computation blocks the caller. diff --git a/docs/src/generated/node-v24-compatibility.json b/docs/src/generated/node-v24-compatibility.json index 66b0ba3dd..09bc7df29 100644 --- a/docs/src/generated/node-v24-compatibility.json +++ b/docs/src/generated/node-v24-compatibility.json @@ -11177,9 +11177,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Supports the modp groups, explicit primes and generators, and generated safe primes up to 8192 bits with Node's argument, generator and modulus errors.", + "verification": "test-backed" } }, { @@ -11200,9 +11200,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Supports the modp groups, explicit primes and generators, and generated safe primes up to 8192 bits with Node's argument, generator and modulus errors.", + "verification": "test-backed" } }, { @@ -11223,9 +11223,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Supports the modp groups, explicit primes and generators, and generated safe primes up to 8192 bits with Node's argument, generator and modulus errors.", + "verification": "test-backed" } }, { @@ -11247,7 +11247,7 @@ }, "static": { "status": "partial", - "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "detail": "Supports prime192v1, secp224r1, prime256v1, secp384r1, secp521r1, secp192k1, secp224k1, secp256k1, brainpoolP256r1, brainpoolP384r1 and brainpoolP512r1 with Node's curve, key, point, format and encoding errors; other curves Node knows are refused.", "verification": "test-backed" } }, @@ -11729,9 +11729,9 @@ "inherited": true }, "static": { - "status": "refused", - "detail": "Explicitly rejected by a named compiler diagnostic.", - "verification": "explicit-refusal" + "status": "partial", + "detail": "Supports the modp groups, explicit primes and generators, and generated safe primes up to 8192 bits with Node's argument, generator and modulus errors.", + "verification": "test-backed" } }, { @@ -12856,9 +12856,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -12879,9 +12879,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -12902,9 +12902,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -12925,9 +12925,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -12948,9 +12948,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -12971,9 +12971,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -12994,9 +12994,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -13017,9 +13017,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -13040,9 +13040,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -13063,9 +13063,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -13086,9 +13086,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused.", + "verification": "test-backed" } }, { @@ -18880,9 +18880,9 @@ "anchor": "err_crypto_invalid_keylen", "nodeStability": null, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented by a dedicated static compiler/runtime path.", + "verification": "test-backed" } }, { @@ -19006,9 +19006,9 @@ "anchor": "err_crypto_invalid_state", "nodeStability": null, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented by a dedicated static compiler/runtime path.", + "verification": "test-backed" } }, { @@ -19276,9 +19276,9 @@ "anchor": "err_crypto_unknown_dh_group", "nodeStability": null, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented by a dedicated static compiler/runtime path.", + "verification": "test-backed" } }, { diff --git a/internal/compatibility/generated/node-v24-internal.json b/internal/compatibility/generated/node-v24-internal.json index c6a7a102c..7ebd09df8 100644 --- a/internal/compatibility/generated/node-v24-internal.json +++ b/internal/compatibility/generated/node-v24-internal.json @@ -19313,11 +19313,18 @@ "tests/corpus/6703-ecdh-private-keys.ts", "tests/corpus/6704-ecdh-set-public-key.cjs", "tests/corpus/6705-ecdh-handles.ts", + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", "tests/corpus/node24-crypto-kdf-errors.ts", "tests/corpus/node24-crypto-kdf-options.ts", "tests/corpus/node24-crypto-kdf.ts", "tests/corpus/node24-crypto-sha2.ts", "tests/fixtures/node-types/cipher.ts", + "tests/fixtures/node-types/dh.ts", "tests/fixtures/node-types/ecdh.ts", "tests/harness/npm-static.test.ts" ] @@ -19759,8 +19766,17 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.crypto.createDiffieHellman" + "status": "partial", + "evidence": "surface-manifest:node-builtin.crypto.createDiffieHellman", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -19782,8 +19798,17 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.crypto.createDiffieHellman" + "status": "partial", + "evidence": "surface-manifest:node-builtin.crypto.createDiffieHellman", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -19805,8 +19830,17 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.crypto.createDiffieHellmanGroup" + "status": "partial", + "evidence": "surface-manifest:node-builtin.crypto.createDiffieHellmanGroup", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -20387,8 +20421,17 @@ "inherited": true }, "static": { - "status": "refused", - "evidence": "surface-manifest:unsupported:node-builtin.crypto.getDiffieHellman" + "status": "partial", + "evidence": "surface-manifest:node-builtin.crypto.getDiffieHellman", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22153,8 +22196,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:DiffieHellman", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22176,8 +22228,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.computeSecret", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22199,8 +22260,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.generateKeys", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22222,8 +22292,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.getGenerator", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22245,8 +22324,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.getPrime", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22268,8 +22356,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.getPrivateKey", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22291,8 +22388,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.getPublicKey", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22314,8 +22420,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.setPrivateKey", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22337,8 +22452,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.setPublicKey", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22360,8 +22484,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:diffieHellman.verifyError", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -22383,8 +22516,17 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:crypto" + "status": "partial", + "evidence": "compiler-dedicated:DiffieHellmanGroup", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -27448,8 +27590,17 @@ "anchor": "dep0167-weak-diffiehellmangroup-instances-modp1-modp2-modp5", "nodeStability": null, "static": { - "status": "not-applicable", - "evidence": "compiler-chapter-policy:deprecations" + "status": "partial", + "evidence": "compiler-dedicated:DiffieHellmanGroup", + "tests": [ + "tests/corpus/6706-dh-groups.ts", + "tests/corpus/6707-dh-explicit-params.ts", + "tests/corpus/6708-dh-errors.js", + "tests/corpus/6709-dh-generate.ts", + "tests/corpus/6710-dh-encodings.cjs", + "tests/corpus/6711-dh-handles.ts", + "tests/fixtures/node-types/dh.ts" + ] }, "anchorSource": "exact" }, @@ -32191,6 +32342,7 @@ "evidence": "derived:descendants", "tests": [ "tests/corpus/6702-ecdh-errors.js", + "tests/corpus/6708-dh-errors.js", "tests/corpus/caught-system-error-fields.ts", "tests/corpus/error-stack-frames.ts", "tests/corpus/error-stack-limit.ts", @@ -33601,8 +33753,11 @@ "anchor": "err_crypto_invalid_keylen", "nodeStability": null, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:errors" + "status": "partial", + "evidence": "compiler-dedicated:ERR_CRYPTO_INVALID_KEYLEN", + "tests": [ + "tests/corpus/6708-dh-errors.js" + ] }, "anchorSource": "exact" }, @@ -33727,8 +33882,11 @@ "anchor": "err_crypto_invalid_state", "nodeStability": null, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:errors" + "status": "partial", + "evidence": "compiler-dedicated:ERR_CRYPTO_INVALID_STATE", + "tests": [ + "tests/corpus/6708-dh-errors.js" + ] }, "anchorSource": "exact" }, @@ -33997,8 +34155,11 @@ "anchor": "err_crypto_unknown_dh_group", "nodeStability": null, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:errors" + "status": "partial", + "evidence": "compiler-dedicated:ERR_CRYPTO_UNKNOWN_DH_GROUP", + "tests": [ + "tests/corpus/6708-dh-errors.js" + ] }, "anchorSource": "exact" }, diff --git a/internal/compatibility/src/generate.mjs b/internal/compatibility/src/generate.mjs index 9bd3927b8..536d4ce5e 100644 --- a/internal/compatibility/src/generate.mjs +++ b/internal/compatibility/src/generate.mjs @@ -582,17 +582,27 @@ function publicDetail(tier) { if (/^surface-manifest:node-builtin\.crypto\.create(?:Cipheriv|Decipheriv)$/.test(source)) { return "Supports aes-128/192/256 in cbc, ctr, and gcm modes and chacha20-poly1305 with Node's argument, IV, tag-length, and key-length errors; other OpenSSL ciphers and KeyObject keys are refused."; } + if (/^surface-manifest:node-builtin\.crypto\.createECDH$/.test(source)) { + return "Supports prime192v1, secp224r1, prime256v1, secp384r1, secp521r1, secp192k1, secp224k1, secp256k1, brainpoolP256r1, brainpoolP384r1 and brainpoolP512r1 with Node's curve, key, point, format and encoding errors; other curves Node knows are refused."; + } + if ( + /^surface-manifest:node-builtin\.crypto\.(?:createDiffieHellman|createDiffieHellmanGroup|getDiffieHellman)$/.test( + source, + ) + ) { + return "Supports the modp groups, explicit primes and generators, and generated safe primes up to 8192 bits with Node's argument, generator and modulus errors."; + } if (source.startsWith("surface-manifest:")) return "Implemented for the call shapes accepted by the compiler lowering."; if (/^compiler-dedicated:assert\.doesNot(?:Throw|Reject)$/.test(source)) { return "Supports callbacks or native promises with messages, Error classes, regular expressions, and validators accepting unknown values; object expectations and complete AssertionError metadata remain unsupported."; } - if (/^surface-manifest:node-builtin\.crypto\.createECDH$/.test(source)) { - return "Supports prime192v1, secp224r1, prime256v1, secp384r1, secp521r1, secp192k1, secp224k1, secp256k1, brainpoolP256r1, brainpoolP384r1 and brainpoolP512r1 with Node's curve, key, point, format and encoding errors; other curves Node knows are refused."; - } if (/^compiler-dedicated:(?:ECDH|ecdh\.)/.test(source)) { return "Matches Node's key formats, point encodings, private-key range checks, key-pair checks and errors on the supported curves; ECDH.convertKey is supported, and property reads and writes are refused."; } + if (/^compiler-dedicated:(?:DiffieHellman|diffieHellman\.)/.test(source)) { + return "Matches Node's private key lengths, DH_check results, public key range checks, secret padding and errors; primes longer than 8192 bits, property writes and property reads other than verifyError are refused."; + } if (/^compiler-dedicated:(?:Cipheriv|Decipheriv|cipher\.|decipher\.)/.test(source)) { return "Matches Node's update() output lengths, padding, authentication tags, string encodings, and state errors for the supported ciphers; the stream.Transform interface is refused."; } diff --git a/internal/compatibility/static-support.json b/internal/compatibility/static-support.json index 23c7a159b..b6cfed75d 100644 --- a/internal/compatibility/static-support.json +++ b/internal/compatibility/static-support.json @@ -44,6 +44,8 @@ "node-builtin.console": ["tests/corpus/console-imports.cjs", "tests/corpus/2440-console-inspect-args.ts", "tests/corpus/console-native-value.js", "tests/harness/npm-static.test.ts"], "node-builtin.crypto.createCipheriv": ["tests/corpus/5250-cipher-aes-gcm.ts", "tests/corpus/5251-cipher-aes-cbc.ts", "tests/corpus/5252-cipher-aes-ctr.ts", "tests/corpus/5253-cipher-chacha20-poly1305.ts", "tests/corpus/5254-cipher-errors.js", "tests/corpus/5255-cipher-encodings.cjs", "tests/corpus/5256-cipher-deprecation.cjs", "tests/corpus/5257-cipher-vectors.ts", "tests/corpus/5258-cipher-handles.js", "tests/corpus/5259-cipher-chunked.ts", "tests/fixtures/node-types/cipher.ts"], "node-builtin.crypto.createDecipheriv": ["tests/corpus/5250-cipher-aes-gcm.ts", "tests/corpus/5251-cipher-aes-cbc.ts", "tests/corpus/5252-cipher-aes-ctr.ts", "tests/corpus/5253-cipher-chacha20-poly1305.ts", "tests/corpus/5254-cipher-errors.js", "tests/corpus/5255-cipher-encodings.cjs", "tests/corpus/5256-cipher-deprecation.cjs", "tests/corpus/5257-cipher-vectors.ts", "tests/corpus/5258-cipher-handles.js", "tests/corpus/5259-cipher-chunked.ts", "tests/fixtures/node-types/cipher.ts"], + "node-builtin.crypto.createDiffieHellman": ["tests/corpus/6706-dh-groups.ts", "tests/corpus/6707-dh-explicit-params.ts", "tests/corpus/6708-dh-errors.js", "tests/corpus/6709-dh-generate.ts", "tests/corpus/6710-dh-encodings.cjs", "tests/corpus/6711-dh-handles.ts", "tests/fixtures/node-types/dh.ts"], + "node-builtin.crypto.createDiffieHellmanGroup": ["tests/corpus/6706-dh-groups.ts", "tests/corpus/6707-dh-explicit-params.ts", "tests/corpus/6708-dh-errors.js", "tests/corpus/6709-dh-generate.ts", "tests/corpus/6710-dh-encodings.cjs", "tests/corpus/6711-dh-handles.ts", "tests/fixtures/node-types/dh.ts"], "node-builtin.crypto.createECDH": ["tests/corpus/6700-ecdh-shared-secret.ts", "tests/corpus/6701-ecdh-formats.ts", "tests/corpus/6702-ecdh-errors.js", "tests/corpus/6703-ecdh-private-keys.ts", "tests/corpus/6704-ecdh-set-public-key.cjs", "tests/corpus/6705-ecdh-handles.ts", "tests/fixtures/node-types/ecdh.ts"], "node-builtin.crypto": ["tests/corpus/1358-crypto-random.ts", "tests/corpus/1534-crypto-hash-chain.ts", "tests/corpus/2556-crypto-introspection.ts", "tests/corpus/2890-crypto-static-utility.ts", "tests/harness/npm-static.test.ts", "tests/corpus/node24-crypto-sha2.ts", "tests/corpus/node24-crypto-kdf.ts", "tests/corpus/node24-crypto-kdf-errors.ts", "tests/corpus/node24-crypto-kdf-options.ts"], "node-builtin.dgram": ["tests/corpus/2597-dgram-send-ladders.cjs"], @@ -118,6 +120,7 @@ "node-builtin.os.version": ["tests/corpus/5140-os-host-facts.ts"], "node-builtin.os.constants": ["tests/corpus/5141-os-constants.ts"], "node-builtin.fs.constants": ["tests/corpus/6207-fs-constants-object.ts", "tests/corpus/6208-fs-constants-require.cjs"], + "node-builtin.crypto.getDiffieHellman": ["tests/corpus/6706-dh-groups.ts", "tests/corpus/6707-dh-explicit-params.ts", "tests/corpus/6708-dh-errors.js", "tests/corpus/6709-dh-generate.ts", "tests/corpus/6710-dh-encodings.cjs", "tests/corpus/6711-dh-handles.ts", "tests/fixtures/node-types/dh.ts"], "node-builtin.crypto.getRandomValues": ["tests/corpus/6204-crypto-get-random-values.ts", "tests/corpus/6205-crypto-get-random-values.js"], "node-builtin.url.parse": ["tests/corpus/5220-url-parse-warnings.ts", "tests/corpus/5221-url-parse-shapes.ts", "tests/corpus/5222-url-parse-hosts.ts", "tests/corpus/5223-url-parse-query.ts", "tests/corpus/5227-url-legacy-dependency/main.ts", "tests/corpus/5229-url-legacy-errors.cjs", "packages/runtime/test/url-legacy.test.ts", "tests/fixtures/node-types/url-legacy.ts"], "node-builtin.url.format": ["tests/corpus/5224-url-format-objects.ts", "tests/corpus/5225-url-format-whatwg.ts", "tests/corpus/5227-url-legacy-dependency/main.ts", "tests/corpus/5229-url-legacy-errors.cjs", "packages/runtime/test/url-legacy.test.ts", "tests/fixtures/node-types/url-legacy.ts"], @@ -414,6 +417,8 @@ { "symbols": ["assert", "assert.ok", "assert.equal", "assert.notEqual", "assert.strictEqual", "assert.notStrictEqual", "assert.deepStrictEqual", "assert.notDeepStrictEqual", "assert.fail", "assert.match", "assert.doesNotMatch", "assert.throws", "assert.rejects", "assert.ifError"], "status": "partial", "evidence": ["tests/corpus/1600-assert-passing.ts", "tests/corpus/1604-assert-deep-structures.ts", "tests/corpus/1609-assert-async.ts", "tests/corpus/1721-assert-throws-regex-class.ts", "tests/corpus/2936-static-loose-equality.ts", "tests/corpus/5515-assert-deep-strict-typed-messages.ts"] }, { "chapter": "crypto", "symbols": ["Cipheriv", "cipher.update", "cipher.final", "cipher.setAAD", "cipher.getAuthTag", "cipher.setAutoPadding", "Decipheriv", "decipher.update", "decipher.final", "decipher.setAAD", "decipher.setAuthTag", "decipher.setAutoPadding"], "status": "partial", "evidence": ["tests/corpus/5250-cipher-aes-gcm.ts", "tests/corpus/5251-cipher-aes-cbc.ts", "tests/corpus/5252-cipher-aes-ctr.ts", "tests/corpus/5253-cipher-chacha20-poly1305.ts", "tests/corpus/5254-cipher-errors.js", "tests/corpus/5255-cipher-encodings.cjs", "tests/corpus/5256-cipher-deprecation.cjs", "tests/corpus/5257-cipher-vectors.ts", "tests/corpus/5258-cipher-handles.js", "tests/corpus/5259-cipher-chunked.ts", "tests/fixtures/node-types/cipher.ts"] }, { "chapter": "crypto", "symbols": ["ECDH", "ecdh.computeSecret", "ecdh.generateKeys", "ecdh.getPrivateKey", "ecdh.getPublicKey", "ecdh.setPrivateKey", "ecdh.setPublicKey"], "status": "partial", "evidence": ["tests/corpus/6700-ecdh-shared-secret.ts", "tests/corpus/6701-ecdh-formats.ts", "tests/corpus/6702-ecdh-errors.js", "tests/corpus/6703-ecdh-private-keys.ts", "tests/corpus/6704-ecdh-set-public-key.cjs", "tests/corpus/6705-ecdh-handles.ts", "tests/fixtures/node-types/ecdh.ts"] }, + { "chapter": "crypto", "symbols": ["DiffieHellman", "DiffieHellmanGroup", "diffieHellman.computeSecret", "diffieHellman.generateKeys", "diffieHellman.getGenerator", "diffieHellman.getPrime", "diffieHellman.getPrivateKey", "diffieHellman.getPublicKey", "diffieHellman.setPrivateKey", "diffieHellman.setPublicKey", "diffieHellman.verifyError"], "status": "partial", "evidence": ["tests/corpus/6706-dh-groups.ts", "tests/corpus/6707-dh-explicit-params.ts", "tests/corpus/6708-dh-errors.js", "tests/corpus/6709-dh-generate.ts", "tests/corpus/6710-dh-encodings.cjs", "tests/corpus/6711-dh-handles.ts", "tests/fixtures/node-types/dh.ts"] }, + { "chapter": "errors", "symbols": ["ERR_CRYPTO_UNKNOWN_DH_GROUP", "ERR_CRYPTO_INVALID_KEYLEN", "ERR_CRYPTO_INVALID_STATE"], "status": "partial", "evidence": ["tests/corpus/6708-dh-errors.js"] }, { "chapter": "errors", "symbols": ["ERR_CRYPTO_ECDH_INVALID_FORMAT", "ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY"], "status": "partial", "evidence": ["tests/corpus/6702-ecdh-errors.js"] }, { "symbols": ["crypto.createHash", "Hash", "hash.update", "hash.digest", "hash.copy", "crypto.createHmac", "Hmac", "hmac.update", "hmac.digest", "crypto.hash", "crypto.pbkdf2", "crypto.pbkdf2Sync"], "status": "partial", "evidence": ["tests/corpus/5760-crypto-hash-stream.ts", "tests/corpus/5768-crypto-hash-unknown.ts", "tests/corpus/5769-crypto-hash-unknown.cjs", "tests/corpus/5761-crypto-hash-stream-states.ts", "tests/corpus/5762-crypto-hash-stream.cjs", "tests/corpus/2890-crypto-static-utility.ts", "tests/corpus/node24-crypto-sha2.ts", "tests/corpus/5435-crypto-hash-algorithms.ts", "tests/corpus/5436-crypto-hash-names.js", "tests/corpus/5437-crypto-hmac-algorithms.ts", "tests/corpus/5438-crypto-kdf-digests.ts", "tests/corpus/5440-crypto-hash-encodings.js", "tests/corpus/5441-crypto-hash-output-length.ts", "tests/harness/npm-static.test.ts"] }, { "symbols": ["crypto.randomFill", "crypto.randomFillSync", "crypto.randomUUID", "crypto.randomUUIDv7"], "status": "supported", "evidence": ["tests/corpus/5442-crypto-random-values.ts", "tests/corpus/5443-crypto-random-arguments.js", "tests/corpus/1358-crypto-random.ts"] }, diff --git a/packages/compiler/ambient/scriptc-node-fallback.d.ts b/packages/compiler/ambient/scriptc-node-fallback.d.ts index dbb3cd8fd..ab4b59eb8 100644 --- a/packages/compiler/ambient/scriptc-node-fallback.d.ts +++ b/packages/compiler/ambient/scriptc-node-fallback.d.ts @@ -2968,6 +2968,68 @@ declare module "crypto" { setPrivateKey(privateKey: string, encoding: BinaryToTextEncoding): void; } export function createECDH(curveName: string): ECDH; + export class DiffieHellman { + private constructor(); + generateKeys(): Buffer; + generateKeys(encoding: BinaryToTextEncoding): string; + computeSecret( + otherPublicKey: ArrayBufferView, + inputEncoding?: null, + outputEncoding?: null, + ): Buffer; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding?: null, + ): Buffer; + computeSecret( + otherPublicKey: ArrayBufferView, + inputEncoding: null, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding: BinaryToTextEncoding, + ): string; + getPrime(): Buffer; + getPrime(encoding: BinaryToTextEncoding): string; + getGenerator(): Buffer; + getGenerator(encoding: BinaryToTextEncoding): string; + getPublicKey(): Buffer; + getPublicKey(encoding: BinaryToTextEncoding): string; + getPrivateKey(): Buffer; + getPrivateKey(encoding: BinaryToTextEncoding): string; + setPublicKey(publicKey: ArrayBufferView): void; + setPublicKey(publicKey: string, encoding: BufferEncoding): void; + setPrivateKey(privateKey: ArrayBufferView): void; + setPrivateKey(privateKey: string, encoding: BufferEncoding): void; + verifyError: number; + } + export type DiffieHellmanGroup = Omit; + export function createDiffieHellman(primeLength: number, generator?: number): DiffieHellman; + export function createDiffieHellman( + prime: ArrayBuffer | ArrayBufferView, + generator?: number | ArrayBuffer | ArrayBufferView, + ): DiffieHellman; + export function createDiffieHellman( + prime: ArrayBuffer | ArrayBufferView, + generator: string, + generatorEncoding: BinaryToTextEncoding, + ): DiffieHellman; + export function createDiffieHellman( + prime: string, + primeEncoding: BinaryToTextEncoding, + generator?: number | ArrayBuffer | ArrayBufferView, + ): DiffieHellman; + export function createDiffieHellman( + prime: string, + primeEncoding: BinaryToTextEncoding, + generator: string, + generatorEncoding: BinaryToTextEncoding, + ): DiffieHellman; + export function getDiffieHellman(groupName: string): DiffieHellmanGroup; + export function createDiffieHellmanGroup(name: string): DiffieHellmanGroup; export interface ScryptOptions { N?: number; r?: number; diff --git a/packages/compiler/src/backend/executable-features.ts b/packages/compiler/src/backend/executable-features.ts index f7cae8508..92b16965d 100644 --- a/packages/compiler/src/backend/executable-features.ts +++ b/packages/compiler/src/backend/executable-features.ts @@ -56,6 +56,7 @@ export function executableLinkFeatures(mod: IrModule): NativeLinkFeatures { tlsCa: features.tlsCa, cipher: features.cipher, ecdh: features.ecdh, + dh: features.dh, webcrypto: features.webcrypto, perfLoop: features.perfLoop, }; diff --git a/packages/compiler/src/backend/library-pack.ts b/packages/compiler/src/backend/library-pack.ts index 805198df1..a58203688 100644 --- a/packages/compiler/src/backend/library-pack.ts +++ b/packages/compiler/src/backend/library-pack.ts @@ -60,6 +60,7 @@ export async function compilePackedLibrary( tls: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, tlsCa: false, diff --git a/packages/compiler/src/backend/llvm/lib-shared.ts b/packages/compiler/src/backend/llvm/lib-shared.ts index 8bb8bc3aa..0745b8806 100644 --- a/packages/compiler/src/backend/llvm/lib-shared.ts +++ b/packages/compiler/src/backend/llvm/lib-shared.ts @@ -577,6 +577,8 @@ export const LIB_FN_SYMS: Record = { "crypto.cipherNew": "scr_crypto_cipher_new", "crypto.ecdhNew": "scr_crypto_ecdh_new", "crypto.ecdhConvertKey": "scr_crypto_ecdh_convert_key", + "crypto.dhNew": "scr_crypto_dh_new", + "crypto.dhGroupNew": "scr_crypto_dh_group_new", "crypto.hmacNewStr": "scr_crypto_hmac_new_str", "crypto.hmacNewBytes": "scr_crypto_hmac_new_bytes", "crypto.hashUpdateStr": "scr_crypto_hash_update_str", diff --git a/packages/compiler/src/backend/native-link-info-core.ts b/packages/compiler/src/backend/native-link-info-core.ts index a476e1f7f..ad8b662ea 100644 --- a/packages/compiler/src/backend/native-link-info-core.ts +++ b/packages/compiler/src/backend/native-link-info-core.ts @@ -90,6 +90,9 @@ export interface NativeLinkFeatures { /** crypto.createECDH and ECDH.convertKey: scr_ecdh.c plus the mbedTLS * archive. */ ecdh: boolean; + /** crypto.createDiffieHellman, getDiffieHellman and + * createDiffieHellmanGroup: scr_dh.c plus the mbedTLS archive. */ + dh: boolean; /** perf_hooks event-loop metrics: scr_perf_loop.c. */ perfLoop: boolean; tlsCa: boolean; diff --git a/packages/compiler/src/backend/native/contracts.ts b/packages/compiler/src/backend/native/contracts.ts index 06a41c9b2..f268e67b2 100644 --- a/packages/compiler/src/backend/native/contracts.ts +++ b/packages/compiler/src/backend/native/contracts.ts @@ -249,6 +249,10 @@ export interface CcOptions { /** crypto.createECDH and ECDH.convertKey: compiles scr_ecdh.c and links * the mbedTLS archive (shared with tls). */ ecdh?: boolean; + /** crypto.createDiffieHellman, getDiffieHellman and + * createDiffieHellmanGroup: compiles scr_dh.c and links the mbedTLS + * archive (shared with tls). */ + dh?: boolean; /** crypto.subtle importKey/sign/verify/digest: compiles scr_webcrypto.c. */ webcrypto?: boolean; /** perf_hooks event-loop metrics: compiles scr_perf_loop.c. */ diff --git a/packages/compiler/src/backend/native/executable.ts b/packages/compiler/src/backend/native/executable.ts index a551ad600..8caf79a07 100644 --- a/packages/compiler/src/backend/native/executable.ts +++ b/packages/compiler/src/backend/native/executable.ts @@ -114,6 +114,7 @@ interface RuntimeUnitContext { tlsCa: boolean; cipher: boolean; ecdh: boolean; + dh: boolean; lreObjects: readonly string[]; zlibObjects: readonly string[]; tlsArchive: string | null; @@ -308,6 +309,7 @@ const RUNTIME_UNITS: readonly RuntimeUnit[] = [ ...(u.tls ? [u.rt(join(u.rtDir, "scr_tls.c"))] : []), ...(u.cipher ? [u.rt(join(u.rtDir, "scr_cipher.c"))] : []), ...(u.ecdh ? [u.rt(join(u.rtDir, "scr_ecdh.c"))] : []), + ...(u.dh ? [u.rt(join(u.rtDir, "scr_dh.c"))] : []), u.tlsArchive, // mbedTLS's win32 entropy poll is BCryptGenRandom (bcrypt.h). // The unconditional win32 libs above do not carry it. @@ -372,10 +374,12 @@ export async function compileCInternal( const http = (opts.http ?? false) || nativeFetch; const tls = (opts.tls ?? false) || nativeFetch; const tlsCa = (opts.tlsCa ?? false) || tls; - // Symmetric ciphers and ECDH link the same mbedTLS archive as TLS. + // Symmetric ciphers, ECDH and DiffieHellman link the same mbedTLS + // archive as TLS. const cipher = opts.cipher ?? false; const ecdh = opts.ecdh ?? false; - const mbedtls = tls || cipher || ecdh; + const dh = opts.dh ?? false; + const mbedtls = tls || cipher || ecdh || dh; const driver = resolveCc(); const darwinDebugSymbols = needsDarwinDebugSymbols( targetPlatform(driver), @@ -733,6 +737,7 @@ export async function compileCInternal( tlsCa, cipher, ecdh, + dh, lreObjects, zlibObjects, tlsArchive, diff --git a/packages/compiler/src/backend/runtime-pack-targets.test.ts b/packages/compiler/src/backend/runtime-pack-targets.test.ts index 19519e1f2..e2ba393a2 100644 --- a/packages/compiler/src/backend/runtime-pack-targets.test.ts +++ b/packages/compiler/src/backend/runtime-pack-targets.test.ts @@ -125,6 +125,7 @@ const features = { tlsCa: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, }; diff --git a/packages/compiler/src/backend/runtime-pack.test.ts b/packages/compiler/src/backend/runtime-pack.test.ts index ee2301172..2a1c46f72 100644 --- a/packages/compiler/src/backend/runtime-pack.test.ts +++ b/packages/compiler/src/backend/runtime-pack.test.ts @@ -61,6 +61,7 @@ const BASE: NativeLinkFeatures = { tlsCa: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, }; diff --git a/packages/compiler/src/backend/target-diagnostics.ts b/packages/compiler/src/backend/target-diagnostics.ts index b7049f6fe..e480beae4 100644 --- a/packages/compiler/src/backend/target-diagnostics.ts +++ b/packages/compiler/src/backend/target-diagnostics.ts @@ -215,6 +215,13 @@ export function moduleWasiUnavailableSurface( }; return false; } + if (node.fn === "crypto.dhNew" || node.fn === "crypto.dhGroupNew") { + found = { + surface: "DiffieHellman (the wasm32-wasi runtime does not include the mbedTLS library)", + loc, + }; + return false; + } if (node.fn === "os.networkInterfaces") { found = { surface: "network-interface enumeration (WASI Preview 1 has no interface API)", diff --git a/packages/compiler/src/backend/warm-cache.ts b/packages/compiler/src/backend/warm-cache.ts index 2d8ae6641..8870e21e7 100644 --- a/packages/compiler/src/backend/warm-cache.ts +++ b/packages/compiler/src/backend/warm-cache.ts @@ -67,6 +67,7 @@ export async function warmNativeCaches( tls: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, tlsCa: false, diff --git a/packages/compiler/src/executable/executable-cache.test.ts b/packages/compiler/src/executable/executable-cache.test.ts index 94b136c90..3770a6bd6 100644 --- a/packages/compiler/src/executable/executable-cache.test.ts +++ b/packages/compiler/src/executable/executable-cache.test.ts @@ -65,6 +65,7 @@ const native: EarlyExecutableNativeFeatures = { tlsCa: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, }; diff --git a/packages/compiler/src/executable/executable-cache.ts b/packages/compiler/src/executable/executable-cache.ts index 2312f5e85..6f96f8f75 100644 --- a/packages/compiler/src/executable/executable-cache.ts +++ b/packages/compiler/src/executable/executable-cache.ts @@ -96,6 +96,7 @@ export interface EarlyExecutableNativeFeatures { tls: boolean; cipher: boolean; ecdh: boolean; + dh: boolean; perfLoop: boolean; tlsCa: boolean; webcrypto: boolean; @@ -228,6 +229,7 @@ const BOOLEAN_NATIVE_KEYS = [ "tlsCa", "cipher", "ecdh", + "dh", "webcrypto", "perfLoop", ] as const satisfies readonly (keyof EarlyExecutableNativeFeatures)[]; diff --git a/packages/compiler/src/frontend/lowering/builtins/crypto.ts b/packages/compiler/src/frontend/lowering/builtins/crypto.ts index f4270dbe8..6ea83fce5 100644 --- a/packages/compiler/src/frontend/lowering/builtins/crypto.ts +++ b/packages/compiler/src/frontend/lowering/builtins/crypto.ts @@ -1,6 +1,7 @@ import { lowerBuiltinByteInput, errorFirstBytesCallback, errorFirstCallback } from "./arguments.js"; import { isKeyObjectValue } from "./key-object.js"; import { lowerCreateEcdh } from "./ecdh.js"; +import { lowerCreateDiffieHellman, lowerGetDiffieHellman } from "./dh.js"; import { boolLit, dynUndefinedExpr, nodeThrowExpr, varRef } from "../../../ir/build.js"; import * as ts from "../../ts7/adapter.js"; import { type Lowerer, own } from "../lowerer.js"; @@ -410,6 +411,9 @@ export function lowerCryptoModuleCall( }; } if (bi.member === "createECDH") return lowerCreateEcdh(lowerer, expr); + if (bi.member === "createDiffieHellman") return lowerCreateDiffieHellman(lowerer, expr); + if (bi.member === "getDiffieHellman" || bi.member === "createDiffieHellmanGroup") + return lowerGetDiffieHellman(lowerer, expr); if (bi.member === "createHmac") { if (args.length < 1 || args.length > 3 || args.some(ts.isSpreadElement)) { lowerer.noLowering( diff --git a/packages/compiler/src/frontend/lowering/builtins/dh.ts b/packages/compiler/src/frontend/lowering/builtins/dh.ts new file mode 100644 index 000000000..eea5a4813 --- /dev/null +++ b/packages/compiler/src/frontend/lowering/builtins/dh.ts @@ -0,0 +1,166 @@ +import * as ts from "../../ts7/adapter.js"; +import { nodeThrowExpr } from "../../../ir/build.js"; +import { type Lowerer } from "../lowerer.js"; +import { locOf } from "../../program.js"; +import { DYN, type IrExpr, type IrStmt } from "../../../ir/ir.js"; +import { v8CalleeText } from "./crypto.js"; +import { dynArguments, refuseUnmatchedOverload } from "./ecdh.js"; + +/* node:crypto's DiffieHellman and DiffieHellmanGroup are native + * checked-dynamic handles (scr_dh.c): both types map to dyn, their methods + * dispatch through the handle, and these helpers lower + * createDiffieHellman, getDiffieHellman, createDiffieHellmanGroup and the + * method calls. The runtime applies Node's argument checks, so the + * constructors take any arguments; method calls are refused when their + * argument types leave the Buffer or string result undetermined. */ + +/** Whether a checker type is node:crypto's DiffieHellman class or its + * DiffieHellmanGroup alias, or a union of them. */ +export function isDiffieHellmanType(lowerer: Lowerer, type: ts.Type): boolean { + if (type.isUnionType()) + return ts.constituentTypes(type).every((part) => isDiffieHellmanType(lowerer, part)); + const symbol = type.getAliasSymbol() ?? type.getSymbol(); + if (!symbol || !lowerer.isStdlibSymbol(symbol)) return false; + const name = ts.symbolName(symbol); + return name === "DiffieHellman" || name === "DiffieHellmanGroup"; +} + +function isDiffieHellmanGroupType(type: ts.Type): boolean { + if (type.isUnionType()) return ts.constituentTypes(type).some(isDiffieHellmanGroupType); + const symbol = type.getAliasSymbol(); + return !!symbol && ts.symbolName(symbol) === "DiffieHellmanGroup"; +} + +function newHandle( + lowerer: Lowerer, + call: ts.CallExpression, + fn: "crypto.dhNew" | "crypto.dhGroupNew", +): IrExpr { + const loc = locOf(call); + const stmts: IrStmt[] = []; + const values = dynArguments(lowerer, call, fn === "crypto.dhNew" ? 4 : 1, stmts, "%dhInput"); + return { + kind: "seqExpr", + stmts, + result: { kind: "libCall", fn, args: values, type: DYN, loc }, + type: DYN, + loc, + }; +} + +/** crypto.createDiffieHellman(sizeOrKey, keyEncoding, generator, + * generatorEncoding): a native DiffieHellman handle. */ +export function lowerCreateDiffieHellman(lowerer: Lowerer, call: ts.CallExpression): IrExpr { + return newHandle(lowerer, call, "crypto.dhNew"); +} + +/** crypto.getDiffieHellman(groupName) and createDiffieHellmanGroup(name): + * a native DiffieHellmanGroup handle. */ +export function lowerGetDiffieHellman(lowerer: Lowerer, call: ts.CallExpression): IrExpr { + return newHandle(lowerer, call, "crypto.dhGroupNew"); +} + +const DH_METHODS = new Set([ + "generateKeys", + "computeSecret", + "getPrime", + "getGenerator", + "getPublicKey", + "getPrivateKey", + "setPublicKey", + "setPrivateKey", +]); + +/** Object.prototype methods, which a DiffieHellman inherits. */ +const OBJECT_METHODS = new Set([ + "constructor", + "hasOwnProperty", + "isPrototypeOf", + "propertyIsEnumerable", + "toLocaleString", + "toString", + "valueOf", +]); + +/** The argument that selects each method's Buffer or string result: the + * output encoding. */ +const OUTPUT_ENCODING_ARG: Record = { + generateKeys: 0, + computeSecret: 2, + getPrime: 0, + getGenerator: 0, + getPublicKey: 0, + getPrivateKey: 0, +}; + +/** An output encoding typed any: the checker's chosen overload then types + * the result as a Buffer or a string that Node may not answer, so the call + * is refused rather than checked against that type at run time. */ +function refuseAnyOutputEncoding(lowerer: Lowerer, call: ts.CallExpression, name: string): void { + const index = OUTPUT_ENCODING_ARG[name]; + const arg = index === undefined ? undefined : call.arguments.nodes[index]; + if (!arg || (lowerer.typeOf(arg).flags & ts.TypeFlags.Any) === 0) return; + lowerer.noLowering( + `DiffieHellman.${name} with an output encoding typed any`, + arg, + "type the encoding as a BinaryToTextEncoding such as 'hex', or as null or undefined for a Buffer", + ); +} + +/** Calls on native DiffieHellman and DiffieHellmanGroup handles. Methods + * dispatch through the handle with checked-dynamic arguments. */ +export function lowerDiffieHellmanCall(lowerer: Lowerer, call: ts.CallExpression): IrExpr | null { + if (call.questionDotToken || !ts.isPropertyAccessExpression(call.expression)) return null; + const access = call.expression; + if (access.questionDotToken) return null; + const receiver = lowerer.typeOf(access.expression); + if (!isDiffieHellmanType(lowerer, receiver)) return null; + const name = access.name.text; + const loc = locOf(call); + const setter = name === "setPublicKey" || name === "setPrivateKey"; + if (!DH_METHODS.has(name) || (setter && isDiffieHellmanGroupType(receiver))) { + if (OBJECT_METHODS.has(name)) + lowerer.noLowering( + `DiffieHellman.${name}`, + call, + "generateKeys(), computeSecret(), getPrime(), getGenerator(), getPublicKey(), getPrivateKey(), " + + "setPublicKey(), setPrivateKey() and verifyError are supported", + ); + // Node reads the member as undefined (a DiffieHellmanGroup has no key + // setters) and throws a TypeError once the receiver and arguments are + // evaluated. + const evaluated = [access.expression, ...call.arguments.nodes].map((node): IrStmt => ({ + kind: "exprStmt", + expr: lowerer.lowerExpr(node), + loc, + })); + return { + kind: "seqExpr", + stmts: evaluated, + result: nodeThrowExpr(1, "", `${v8CalleeText(access)} is not a function`, DYN, loc), + type: DYN, + loc, + }; + } + if (call.arguments.nodes.some(ts.isSpreadElement)) + lowerer.noLowering(`DiffieHellman.${name} with spread arguments`, call); + refuseAnyOutputEncoding(lowerer, call, name); + refuseUnmatchedOverload(lowerer, call, `DiffieHellman.${name}`); + const invoke: IrExpr = { + kind: "dynInvoke", + recv: lowerer.lowerExprExpecting(access.expression, DYN), + method: name, + calleeName: v8CalleeText(access), + args: call.arguments.nodes.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), + type: DYN, + loc, + }; + // setPublicKey and setPrivateKey answer the receiver where @types/node + // declares void. Other results take the static representation of the + // declared overload the arguments match. + if (setter) return invoke; + const result = lowerer.irTypeOf(call); + return result.kind === "bytes" || result.kind === "string" + ? lowerer.coerceInto(call, invoke, result) + : invoke; +} diff --git a/packages/compiler/src/frontend/lowering/builtins/ecdh.ts b/packages/compiler/src/frontend/lowering/builtins/ecdh.ts index e08be56f4..6e937284c 100644 --- a/packages/compiler/src/frontend/lowering/builtins/ecdh.ts +++ b/packages/compiler/src/frontend/lowering/builtins/ecdh.ts @@ -28,15 +28,16 @@ export function isEcdhType(lowerer: Lowerer, type: ts.Type): boolean { return !!symbol && ts.symbolName(symbol) === "ECDH" && lowerer.isStdlibSymbol(symbol); } -/** The call's arguments as checked-dynamic values in hidden locals, the - * first `count` of them padded with undefined, so every argument is - * evaluated left to right before the runtime checks them. Surplus - * arguments are evaluated and dropped. */ -function dynArguments( +/** The call's arguments as checked-dynamic values in hidden locals named + * `local`, the first `count` of them padded with undefined, so every + * argument is evaluated left to right before the runtime checks them. + * Surplus arguments are evaluated and dropped. */ +export function dynArguments( lowerer: Lowerer, call: ts.CallExpression, count: number, stmts: IrStmt[], + local = "%ecdhInput", ): IrExpr[] { const args = call.arguments.nodes; const loc = locOf(call); @@ -45,7 +46,7 @@ function dynArguments( const input: IrExpr = args[i] ? lowerer.lowerExprExpecting(args[i]!, DYN) : dynUndefinedExpr(loc); - const saved = lowerer.declareHiddenLocal("%ecdhInput", DYN); + const saved = lowerer.declareHiddenLocal(local, DYN); stmts.push({ kind: "varDecl", localId: saved.id, init: input, loc }); values.push(varRef(saved.id, DYN, loc)); } @@ -137,7 +138,11 @@ function refuseViewKeyWithEncoding(lowerer: Lowerer, call: ts.CallExpression, na * not follow (getPublicKey("HEX") answers a string where the encoding-less * overload declares a Buffer), so the call is refused rather than checked * against that type at run time. Arguments typed any are accepted. */ -function refuseUnmatchedOverload(lowerer: Lowerer, call: ts.CallExpression, what: string): void { +export function refuseUnmatchedOverload( + lowerer: Lowerer, + call: ts.CallExpression, + what: string, +): void { const signature = lowerer.checker.getResolvedSignature(call); if (signature === undefined) return; const params = signature.getParameters(); diff --git a/packages/compiler/src/frontend/lowering/lowerer.ts b/packages/compiler/src/frontend/lowering/lowerer.ts index 870e21b1f..8072e410d 100644 --- a/packages/compiler/src/frontend/lowering/lowerer.ts +++ b/packages/compiler/src/frontend/lowering/lowerer.ts @@ -58,6 +58,7 @@ import { encodeBase64 } from "../../wasm/base64.js"; import { resolveRequireRuntime } from "../runtime-resolve.js"; import { lowerKeyObjectCall } from "./builtins/key-object.js"; import { isEcdhType, lowerEcdhCall } from "./builtins/ecdh.js"; +import { isDiffieHellmanType, lowerDiffieHellmanCall } from "./builtins/dh.js"; import { fenceWebCryptoMember, lowerWebCryptoCall } from "./builtins/webcrypto.js"; import { hashStreamOf } from "./builtins/hash-stream.js"; import { lowerNetAddressCall, lowerNetAddressNew } from "./builtins/net-address.js"; @@ -3620,9 +3621,9 @@ export class Lowerer { * is `unknown` or `object` (also with `null` or `undefined`, or an array * of those), names checked protocol identity, is an owned runtime * handle (a Worker or MessagePort, a fetch Headers/Request/Response, or - * a node:crypto ECDH), or is a WeakMap/WeakSet (native weak collections - * live in checked-dynamic storage, as locals of those types do); `any` - * fields stay refused. */ + * a node:crypto ECDH, DiffieHellman or DiffieHellmanGroup), or is a + * WeakMap/WeakSet (native weak collections live in checked-dynamic + * storage, as locals of those types do); `any` fields stay refused. */ classFieldStoresDyn(type: ts.Type): boolean { return ( this.opaqueValueType(type) || @@ -3630,7 +3631,10 @@ export class Lowerer { isWorkerHandleType(type, this.typeCtx) || isFetchHandleType(type, this.typeCtx) || this.weakCollectionType(type) || - this.stdlibHandleType(type, (part) => isEcdhType(this, part)) + this.stdlibHandleType( + type, + (part) => isEcdhType(this, part) || isDiffieHellmanType(this, part), + ) ); } @@ -12737,6 +12741,7 @@ export class Lowerer { lowerWebCryptoCall(this, expr) ?? lowerKeyObjectCall(this, expr) ?? lowerEcdhCall(this, expr) ?? + lowerDiffieHellmanCall(this, expr) ?? lowerNetAddressCall(this, expr) ?? lowerFetchElementMethodCall(this, expr) ?? lowerDynamicImportCall(this, expr) ?? diff --git a/packages/compiler/src/frontend/lowering/surfaces.ts b/packages/compiler/src/frontend/lowering/surfaces.ts index c77938337..75bf42516 100644 --- a/packages/compiler/src/frontend/lowering/surfaces.ts +++ b/packages/compiler/src/frontend/lowering/surfaces.ts @@ -1574,6 +1574,12 @@ export const BUILTIN_MODULE_FNS: Record< // A checked-dynamic curve name; the result is a native ECDH handle // (lowerCreateEcdh). createECDH: { fn: "crypto.ecdhNew", params: [DYN], result: DYN }, + // Checked-dynamic arguments; the result is a native DiffieHellman or + // DiffieHellmanGroup handle (lowerCreateDiffieHellman and + // lowerGetDiffieHellman). + createDiffieHellman: { fn: "crypto.dhNew", params: [DYN, DYN, DYN, DYN], result: DYN }, + getDiffieHellman: { fn: "crypto.dhGroupNew", params: [DYN], result: DYN }, + createDiffieHellmanGroup: { fn: "crypto.dhGroupNew", params: [DYN], result: DYN }, }, zlib: { // The default-options convenience family is special-cased in @@ -3063,10 +3069,6 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record< "createVerify", "sign", "verify", - "createDiffieHellman", - "createDiffieHellmanGroup", - "getDiffieHellman", - "diffieHellman", "publicEncrypt", "publicDecrypt", "privateEncrypt", @@ -3078,6 +3080,9 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record< "can be small; the lowered crypto surface is hashing, randomness, secret KeyObjects, and the introspection statics", ]), ), + diffieHellman: + "crypto.diffieHellman takes asymmetric KeyObjects, which the static runtime does not have; " + + "the objects of createDiffieHellman, getDiffieHellman and createECDH compute shared secrets", ...Object.fromEntries( ["getCipherInfo"].map((m) => [ m, diff --git a/packages/compiler/src/frontend/type-mapper.ts b/packages/compiler/src/frontend/type-mapper.ts index a233f0186..189efaac0 100644 --- a/packages/compiler/src/frontend/type-mapper.ts +++ b/packages/compiler/src/frontend/type-mapper.ts @@ -2973,6 +2973,23 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { ) { return DYN; } + // node:crypto DiffieHellman values, and @types/node's DiffieHellmanGroup + // alias over them, are native handles carried by the checked-dynamic + // representation; lowerDiffieHellmanCall dispatches their methods. + { + const dhSym = widened.getAliasSymbol() ?? psym; + if ( + dhSym !== undefined && + (ts.symbolName(dhSym) === "DiffieHellman" || ts.symbolName(dhSym) === "DiffieHellmanGroup") && + dhSym.declarations.some( + (d) => + (ts.isClassDeclaration(d) || ts.isTypeAliasDeclaration(d)) && + ctx.isStdlibFile(ts.getSourceFileOfNode(d)), + ) + ) { + return DYN; + } + } // ReadonlyMap/ReadonlySet are the SAME runtime values as Map/Set — the // readonly-ness is a checker-only view (no mutating members on the // interface), so both map to the identical IR kinds and the read-side diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts index 92ee6b75e..acd9f168a 100644 --- a/packages/compiler/src/index.ts +++ b/packages/compiler/src/index.ts @@ -620,6 +620,7 @@ async function compileExecutableNative( tlsCa: features.tlsCa, cipher: features.cipher, ecdh: features.ecdh, + dh: features.dh, webcrypto: features.webcrypto, perfLoop: features.perfLoop, ...(onArtifactReady === undefined ? {} : { onArtifactReady }), diff --git a/packages/compiler/src/ir/builtin-effects.ts b/packages/compiler/src/ir/builtin-effects.ts index ca8c52118..21215d28f 100644 --- a/packages/compiler/src/ir/builtin-effects.ts +++ b/packages/compiler/src/ir/builtin-effects.ts @@ -13,6 +13,9 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ // Node's curve, key, point, encoding and format errors. "crypto.ecdhNew", "crypto.ecdhConvertKey", + // Node's prime, generator, size, encoding and group-name errors. + "crypto.dhNew", + "crypto.dhGroupNew", // node:test's { timeout } and { concurrency } range checks. "test.register", "test.registerEmpty", diff --git a/packages/compiler/src/ir/builtin-signatures.ts b/packages/compiler/src/ir/builtin-signatures.ts index 771b53152..d362e13fa 100644 --- a/packages/compiler/src/ir/builtin-signatures.ts +++ b/packages/compiler/src/ir/builtin-signatures.ts @@ -2782,6 +2782,13 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ /** ECDH.convertKey(key, curve, inputEncoding, outputEncoding, format): * the converted point as a Buffer or string. */ "crypto.ecdhConvertKey": { argTypes: [DYN, DYN, DYN, DYN, DYN], result: DYN }, + /** createDiffieHellman(sizeOrKey, keyEncoding, generator, + * generatorEncoding) over checked-dynamic arguments (scr_dh.c): a native + * DiffieHellman handle whose methods dispatch through dynInvoke. */ + "crypto.dhNew": { argTypes: [DYN, DYN, DYN, DYN], result: DYN }, + /** getDiffieHellman(groupName) and createDiffieHellmanGroup(name): a + * native DiffieHellmanGroup handle. */ + "crypto.dhGroupNew": { argTypes: [DYN], result: DYN }, "crypto.hmacNewStr": { argTypes: [STRING, STRING], result: CRYPTOHMAC_T }, "crypto.hmacNewBytes": { argTypes: [STRING, BYTES_U8], result: CRYPTOHMAC_T }, "crypto.hashUpdateStr": { argTypes: [CRYPTOHASH_T, STRING], result: CRYPTOHASH_T }, diff --git a/packages/compiler/src/ir/runtime-features.ts b/packages/compiler/src/ir/runtime-features.ts index 4142b6efe..eec599e94 100644 --- a/packages/compiler/src/ir/runtime-features.ts +++ b/packages/compiler/src/ir/runtime-features.ts @@ -744,6 +744,9 @@ const RUNTIME_FEATURE_RULES = { * process-warning unit, and unknown encodings and formats render through * the inspect unit. */ ecdh: rule({ fns: ["crypto.ecdhNew", "crypto.ecdhConvertKey"] }), + /** scr_dh.c over the mbedTLS archive; unknown encodings render through + * the inspect unit. */ + dh: rule({ fns: ["crypto.dhNew", "crypto.dhGroupNew"] }), emitter: rule({ initial: (mod) => mod.workers === true || (mod.classes ?? []).some((c) => c.name === RUNTIME_EMITTER_CLASS), @@ -789,6 +792,8 @@ const RUNTIME_FEATURE_RULES = { "crypto.cipherNew", "crypto.ecdhNew", "crypto.ecdhConvertKey", + "crypto.dhNew", + "crypto.dhGroupNew", ...BUILTIN_CLASS_LIB_FNS, ...UTIL_CALLABLE_LIB_FNS, ], @@ -910,6 +915,7 @@ function noRuntimeFeatures(): RuntimeFeatures { dynInvoke: false, dynTier: false, ecdh: false, + dh: false, emitter: false, eventTarget: false, eventsOnceSignal: false, diff --git a/packages/compiler/src/library/ir-policy.ts b/packages/compiler/src/library/ir-policy.ts index 51aeb6146..4fc912ea6 100644 --- a/packages/compiler/src/library/ir-policy.ts +++ b/packages/compiler/src/library/ir-policy.ts @@ -52,6 +52,8 @@ const LIB_MODE_REFUSED_PREFIXES: readonly [string, string][] = [ ["tls.", "the node:tls surface"], ["crypto.cipherNew", "crypto.createCipheriv and createDecipheriv"], ["crypto.ecdh", "crypto.createECDH and ECDH.convertKey"], + ["crypto.dhNew", "crypto.createDiffieHellman"], + ["crypto.dhGroupNew", "crypto.getDiffieHellman and createDiffieHellmanGroup"], ["webcrypto.", "WebCrypto (crypto.subtle)"], ["fs.watch", "fs.watch"], ["watcher.", "fs.watch"], diff --git a/packages/compiler/src/native/cli.ts b/packages/compiler/src/native/cli.ts index ed8d52293..8529678ff 100644 --- a/packages/compiler/src/native/cli.ts +++ b/packages/compiler/src/native/cli.ts @@ -110,6 +110,7 @@ async function main(): Promise { tls: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, tlsCa: false, diff --git a/packages/compiler/src/native/prepare.ts b/packages/compiler/src/native/prepare.ts index b90760616..1a1263035 100644 --- a/packages/compiler/src/native/prepare.ts +++ b/packages/compiler/src/native/prepare.ts @@ -161,6 +161,7 @@ function validFeatures(value: unknown): value is NativeLinkFeatures { "tlsCa", "cipher", "ecdh", + "dh", "webcrypto", "perfLoop", ].every((name) => typeof features[name] === "boolean"); diff --git a/packages/compiler/src/native/sanitizer.test.ts b/packages/compiler/src/native/sanitizer.test.ts index e6beb24cc..ec9580ccc 100644 --- a/packages/compiler/src/native/sanitizer.test.ts +++ b/packages/compiler/src/native/sanitizer.test.ts @@ -120,6 +120,7 @@ test.skipIf(process.platform !== "linux" && process.platform !== "darwin")( tlsCa: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, nativeFetch: false, diff --git a/packages/compiler/surface-manifest.json b/packages/compiler/surface-manifest.json index c05e994a8..6998a574e 100644 --- a/packages/compiler/surface-manifest.json +++ b/packages/compiler/surface-manifest.json @@ -496,17 +496,13 @@ "id": "node-builtin.crypto.createDiffieHellman", "kind": "node-builtin", "name": "crypto.createDiffieHellman", - "status": "unsupported", - "code": "SC2020", - "note": "asymmetric-key operations need a public-key stack (bignum, RSA/EC/EdDSA math) and public and private KeyObjects — neither exists in the static runtime, so no faithful lowering can be small; the lowered crypto surface is hashing, randomness, secret KeyObjects, and the introspection statics" + "status": "static" }, { "id": "node-builtin.crypto.createDiffieHellmanGroup", "kind": "node-builtin", "name": "crypto.createDiffieHellmanGroup", - "status": "unsupported", - "code": "SC2020", - "note": "asymmetric-key operations need a public-key stack (bignum, RSA/EC/EdDSA math) and public and private KeyObjects — neither exists in the static runtime, so no faithful lowering can be small; the lowered crypto surface is hashing, randomness, secret KeyObjects, and the introspection statics" + "status": "static" }, { "id": "node-builtin.crypto.createECDH", @@ -570,7 +566,7 @@ "name": "crypto.diffieHellman", "status": "unsupported", "code": "SC2020", - "note": "asymmetric-key operations need a public-key stack (bignum, RSA/EC/EdDSA math) and public and private KeyObjects — neither exists in the static runtime, so no faithful lowering can be small; the lowered crypto surface is hashing, randomness, secret KeyObjects, and the introspection statics" + "note": "crypto.diffieHellman takes asymmetric KeyObjects, which the static runtime does not have; the objects of createDiffieHellman, getDiffieHellman and createECDH compute shared secrets" }, { "id": "node-builtin.crypto.generateKey", @@ -612,9 +608,7 @@ "id": "node-builtin.crypto.getDiffieHellman", "kind": "node-builtin", "name": "crypto.getDiffieHellman", - "status": "unsupported", - "code": "SC2020", - "note": "asymmetric-key operations need a public-key stack (bignum, RSA/EC/EdDSA math) and public and private KeyObjects — neither exists in the static runtime, so no faithful lowering can be small; the lowered crypto surface is hashing, randomness, secret KeyObjects, and the introspection statics" + "status": "static" }, { "id": "node-builtin.crypto.getRandomValues", diff --git a/packages/compiler/test/cc-driver.test.ts b/packages/compiler/test/cc-driver.test.ts index b9f0403c6..5ddae6534 100644 --- a/packages/compiler/test/cc-driver.test.ts +++ b/packages/compiler/test/cc-driver.test.ts @@ -291,6 +291,11 @@ test.skipIf(!zigOnPath())( options: { ecdh: true, dynAsync: true, dynInvoke: true, inspect: true }, call: "scr_dyn_release(scr_crypto_ecdh_new(scr_dyn_undefined()))", }, + { + name: "dh", + options: { dh: true, dynInvoke: true, inspect: true }, + call: "scr_dyn_release(scr_crypto_dh_group_new(scr_dyn_undefined()))", + }, ]; try { await withCcEnv("zigcc", "x86_64-windows-gnu", async () => { diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6706-dh-groups.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6706-dh-groups.ts.json new file mode 100644 index 000000000..d8f5b48c3 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6706-dh-groups.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6706-dh-groups.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6707-dh-explicit-params.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6707-dh-explicit-params.ts.json new file mode 100644 index 000000000..4cb3c882c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6707-dh-explicit-params.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6707-dh-explicit-params.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6708-dh-errors.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6708-dh-errors.js.json new file mode 100644 index 000000000..c252657a8 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6708-dh-errors.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6708-dh-errors.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6709-dh-generate.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6709-dh-generate.ts.json new file mode 100644 index 000000000..5c50c8ec1 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6709-dh-generate.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6709-dh-generate.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6710-dh-encodings.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6710-dh-encodings.cjs.json new file mode 100644 index 000000000..86abbb6ba --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6710-dh-encodings.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6710-dh-encodings.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6711-dh-handles.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6711-dh-handles.ts.json new file mode 100644 index 000000000..ae8daf9f1 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6711-dh-handles.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6711-dh-handles.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/dh.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/dh.ts.json new file mode 100644 index 000000000..db078292a --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/dh.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/node-types/dh.ts" + ], + "diags": [] +} diff --git a/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs b/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs index db72284f6..16fe348f2 100644 --- a/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs +++ b/packages/runtime-darwin-arm64/runtime-pack-matrix.mjs @@ -43,7 +43,7 @@ export const RUNTIME_PACK_MATRIX = withLibraryRuntimeFlavors( { id: "quickjs", predicate: false }, { id: "libregexp", predicate: "regex" }, { id: "zlib", predicate: "zlibEffective" }, - { id: "mbedtls", predicate: any("tlsEffective", "cipher", "ecdh") }, + { id: "mbedtls", predicate: any("tlsEffective", "cipher", "ecdh", "dh") }, ], system_libraries: [{ name: "System", predicate: true }], }), diff --git a/packages/runtime-darwin-arm64/scripts/build.mjs b/packages/runtime-darwin-arm64/scripts/build.mjs index 856de4b86..e44ba4a33 100644 --- a/packages/runtime-darwin-arm64/scripts/build.mjs +++ b/packages/runtime-darwin-arm64/scripts/build.mjs @@ -172,7 +172,8 @@ async function build() { ...(unit.source === "scr_regex.c" ? ["-I", quickjs] : []), ...(unit.source === "scr_tls.c" || unit.source === "scr_cipher.c" || - unit.source === "scr_ecdh.c" + unit.source === "scr_ecdh.c" || + unit.source === "scr_dh.c" ? ["-I", join(mbedtls, "include")] : []), ...(unit.source === "scr_zlib.c" || unit.source === "scr_fetch.c" diff --git a/packages/runtime-pack-common/runtime-pack-matrix.mjs b/packages/runtime-pack-common/runtime-pack-matrix.mjs index 0f40218a1..ad426c953 100644 --- a/packages/runtime-pack-common/runtime-pack-matrix.mjs +++ b/packages/runtime-pack-common/runtime-pack-matrix.mjs @@ -149,6 +149,7 @@ export const OPTIONAL_RUNTIME_SOURCES = [ ["scr_tls.c", "tlsEffective"], ["scr_cipher.c", "cipher"], ["scr_ecdh.c", "ecdh"], + ["scr_dh.c", "dh"], ["scr_wasm.c", "wasm"], ["scr_webcrypto.c", "webcrypto"], ["scr_fetch.c", "nativeFetch"], @@ -212,7 +213,7 @@ export function createRuntimePackMatrix({ { id: "quickjs", predicate: false }, { id: "libregexp", predicate: "regex" }, { id: "zlib", predicate: "zlibEffective" }, - { id: "mbedtls", predicate: any("tlsEffective", "cipher", "ecdh") }, + { id: "mbedtls", predicate: any("tlsEffective", "cipher", "ecdh", "dh") }, ]; const matrix = withLibraryRuntimeFlavors( withWorkerRuntimeVariants({ diff --git a/packages/runtime-pack-common/scripts/build.mjs b/packages/runtime-pack-common/scripts/build.mjs index fa84e9c4d..b99bd8438 100644 --- a/packages/runtime-pack-common/scripts/build.mjs +++ b/packages/runtime-pack-common/scripts/build.mjs @@ -223,7 +223,8 @@ async function build() { ...(unit.source === "scr_regex.c" ? ["-I", quickjs] : []), ...(unit.source === "scr_tls.c" || unit.source === "scr_cipher.c" || - unit.source === "scr_ecdh.c" + unit.source === "scr_ecdh.c" || + unit.source === "scr_dh.c" ? ["-I", join(mbedtls, "include")] : []), ...(unit.source === "scr_zlib.c" || unit.source === "scr_fetch.c" diff --git a/packages/runtime-wasm32-wasi/scripts/build.mjs b/packages/runtime-wasm32-wasi/scripts/build.mjs index 2dd983973..818ebac94 100644 --- a/packages/runtime-wasm32-wasi/scripts/build.mjs +++ b/packages/runtime-wasm32-wasi/scripts/build.mjs @@ -22,6 +22,7 @@ process.env.SCRIPTC_RUNTIME_PACK_CONFIG = JSON.stringify({ "scr_tls.c", "scr_cipher.c", "scr_ecdh.c", + "scr_dh.c", "scr_wasm.c", "scr_fetch.c", ], diff --git a/packages/runtime/src/scr_crypto.h b/packages/runtime/src/scr_crypto.h index d12858abf..65a6245ef 100644 --- a/packages/runtime/src/scr_crypto.h +++ b/packages/runtime/src/scr_crypto.h @@ -94,6 +94,13 @@ ScrDyn *scr_crypto_cipher_new(bool decrypt, ScrDyn *cipher, ScrDyn *key, ScrDyn * or result, or NULL with Node's error pending. */ ScrDyn *scr_crypto_ecdh_new(ScrDyn *curve); ScrDyn *scr_crypto_ecdh_convert_key(ScrDyn *key, ScrDyn *curve, ScrDyn *in_enc, ScrDyn *out_enc, ScrDyn *format); +/* crypto.createDiffieHellman(sizeOrKey, keyEncoding, generator, + * generatorEncoding) and crypto.getDiffieHellman(groupName) over + * checked-dynamic arguments (scr_dh.c, linked with the vendored mbedTLS + * archive). +1 DiffieHellman or DiffieHellmanGroup handle, or NULL with + * Node's error pending. */ +ScrDyn *scr_crypto_dh_new(ScrDyn *size_or_key, ScrDyn *key_encoding, ScrDyn *generator, ScrDyn *generator_encoding); +ScrDyn *scr_crypto_dh_group_new(ScrDyn *name); ScrBytes *scr_crypto_pbkdf2(ScrBytes *password, ScrBytes *salt, double iterations, double keylen, ScrStr *digest); ScrBytes *scr_crypto_pbkdf2_job(ScrBytes *password, ScrBytes *salt, double iterations, diff --git a/packages/runtime/src/scr_crypto_args.h b/packages/runtime/src/scr_crypto_args.h new file mode 100644 index 000000000..b8bdcc1c9 --- /dev/null +++ b/packages/runtime/src/scr_crypto_args.h @@ -0,0 +1,181 @@ +/* Argument and result handling shared by the checked-dynamic node:crypto + * key-exchange handles (scr_ecdh.c and scr_dh.c), as Node 24's + * lib/internal/crypto/diffiehellman.js and lib/internal/crypto/util.js + * read keys and encodings: getArrayBufferOrView for key inputs and + * encode() for Buffer results. `subject` names the value in the coded + * SC2020 error for encodings that have no lowering ("An ECDH encoding or + * format"). All arguments are borrowed. */ +#ifndef SCR_CRYPTO_ARGS_H +#define SCR_CRYPTO_ARGS_H + +#include "scr_runtime.h" + +#include + +/* An object's toString the program defined, as an own or inherited + * function property. */ +static bool ca_custom_to_string(ScrDyn *value) { + if (value->kind != SCR_DYN_OBJ && value->kind != SCR_DYN_TYPED_REF) return false; + ScrDyn *member = scr_dyn_obj_read(value, "toString", 8); + bool custom = member && member->kind == SCR_DYN_FUNC; + scr_dyn_release(member); + return custom; +} + +/* Whether value is a class instance whose class defines toString. */ +static bool ca_class_to_string(const ScrDyn *value) { + ScrStr *name = scr_str_new("toString", 8); + bool method = scr_dyn_typed_ref_class_has(value, name); + scr_str_release(name); + return method; +} + +/* `prefix` and the value as util.format's %s renders it: strings and + * objects with a program-defined toString as String(value), numbers as + * util.inspect does, other values inspected at depth 0. Functions and + * class instances that define toString have no lowering. */ +static void ca_throw_formatted(const char *subject, const char *code, const char *prefix, const ScrDyn *value) { + ScrDyn *v = (ScrDyn *)value; + if (v->kind == SCR_DYN_FUNC || ca_class_to_string(v)) { + ScrJsonBuf b; + scr_jb_init(&b); + scr_jb_puts(&b, subject); + scr_jb_puts(&b, " that is a function or an instance of a class defining toString has no lowering; " + "pass the name as a string"); + ScrStr *message = scr_jb_finish(&b); + scr_throw_error_msg_code(SCR_ERR_ERROR, message->data, message->len, "SC2020"); + scr_str_release(message); + return; + } + ScrStr *text; + if (v->kind == SCR_DYN_STR || ca_custom_to_string(v)) text = scr_dyn_string_coerce_js(v); + else if (v->kind == SCR_DYN_NUM) text = scr_insp_f64(v->v.num); + else text = scr_insp_dyn_s(v, 0); + if (!text) return; + ScrJsonBuf b; + scr_jb_init(&b); + scr_jb_puts(&b, prefix); + scr_jb_put_str(&b, text); + scr_str_release(text); + ScrStr *message = scr_jb_finish(&b); + scr_throw_error_msg_code(SCR_ERR_TYPE, message->data, message->len, code); + scr_str_release(message); +} + +static bool ca_ascii_eq_ci(const char *a, size_t a_len, const char *b) { + size_t b_len = strlen(b); + if (a_len != b_len) return false; + for (size_t i = 0; i < a_len; i++) { + char x = a[i]; + if (x >= 'A' && x <= 'Z') x = (char)(x - 'A' + 'a'); + if (x != b[i]) return false; + } + return true; +} + +static bool ca_str_is(const ScrStr *text, const char *literal) { + size_t len = strlen(literal); + return text->len == len && memcmp(text->data, literal, len) == 0; +} + +/* Node's normalizeEncoding: the canonical name, or NULL. */ +static const char *ca_normalize_encoding(const ScrStr *name) { + static const struct { const char *from; const char *to; } map[] = { + {"utf8", "utf8"}, {"utf-8", "utf8"}, {"hex", "hex"}, + {"base64", "base64"}, {"base64url", "base64url"}, {"latin1", "latin1"}, + {"binary", "latin1"}, {"ascii", "ascii"}, {"utf16le", "utf16le"}, + {"utf-16le", "utf16le"}, {"ucs2", "utf16le"}, {"ucs-2", "utf16le"}, + }; + for (size_t i = 0; i < sizeof map / sizeof map[0]; i++) + if (ca_ascii_eq_ci(name->data, name->len, map[i].from)) return map[i].to; + return NULL; +} + +/* The value a typed-ref box holds when it is a string, view, ArrayBuffer + * or native handle (+1). Other values are retained as passed, so argument + * errors describe the original value. NULL after throwing. */ +static ScrDyn *ca_unbox(ScrDyn *value) { + if (value->kind != SCR_DYN_TYPED_REF) return scr_dyn_retain(value); + ScrDyn *view = scr_dyn_typed_ref_materialize(value); + if (!view) return NULL; + if (view->kind == SCR_DYN_STR || view->kind == SCR_DYN_BYTES || view->kind == SCR_DYN_HANDLE || + scr_buffer_storage_is(view)) + return view; + scr_dyn_release(view); + return scr_dyn_retain(value); +} + +/* Buffer.from(text, encoding) as toBuf calls it: an encoding that is not + * a non-empty string, or is 'buffer', reads as utf8. +1 or NULL after + * throwing. */ +static ScrBytes *ca_string_bytes(const char *subject, const ScrStr *text, const ScrDyn *encoding) { + const char *name = "utf8"; + if (encoding->kind == SCR_DYN_STR && encoding->v.str->len > 0 && !ca_str_is(encoding->v.str, "buffer")) { + name = ca_normalize_encoding(encoding->v.str); + if (!name) { + ca_throw_formatted(subject, "ERR_UNKNOWN_ENCODING", "Unknown encoding: ", encoding); + return NULL; + } + } + ScrStr *canonical = scr_str_new(name, strlen(name)); + ScrBytes *bytes = scr_bytes_from_str(text, canonical); + scr_str_release(canonical); + return bytes; +} + +/* getArrayBufferOrView(key, 'key', encoding): a string is Buffer.from(key, + * encoding) with 'buffer' read as utf8; views and ArrayBuffers are their + * bytes. +1 or NULL after throwing. */ +static ScrBytes *ca_key_bytes(const char *subject, const ScrDyn *key, const ScrDyn *encoding) { + if (key->kind == SCR_DYN_STR) return ca_string_bytes(subject, key->v.str, encoding); + if (key->kind == SCR_DYN_BYTES || scr_buffer_storage_is(key)) return scr_bytes_buffer_source(key); + scr_dyn_arg_type_fail("key", "of type string or an instance of ArrayBuffer, Buffer, TypedArray, or DataView", key); + return NULL; +} + +/* encode(buffer, encoding): the Buffer, or buffer.toString(encoding) for + * a truthy encoding other than 'buffer'. Consumes bytes. */ +static ScrDyn *ca_encode(const char *subject, ScrBytes *bytes, const ScrDyn *encoding) { + if (!scr_dyn_truthy(encoding) || (encoding->kind == SCR_DYN_STR && ca_str_is(encoding->v.str, "buffer"))) { + ScrDyn *result = scr_dyn_new_buffer(bytes); + scr_bytes_release(bytes); + return result; + } + /* toString answers "" for no bytes before it reads the encoding. */ + ScrStr *text = NULL; + if (bytes->len == 0) { + text = scr_str_new("", 0); + } else { + /* getEncodingOps reads `encoding + ''`. */ + ScrStr *name = encoding->kind == SCR_DYN_STR ? scr_str_retain(encoding->v.str) : NULL; + if (!name) { + if (encoding->kind == SCR_DYN_FUNC || ca_class_to_string(encoding)) { + ca_throw_formatted(subject, "ERR_UNKNOWN_ENCODING", "Unknown encoding: ", encoding); + scr_bytes_release(bytes); + return NULL; + } + name = scr_dyn_string_coerce_js(encoding); + if (!name) { + scr_bytes_release(bytes); + return NULL; + } + } + const char *canonical = ca_normalize_encoding(name); + scr_str_release(name); + if (!canonical) { + ca_throw_formatted(subject, "ERR_UNKNOWN_ENCODING", "Unknown encoding: ", encoding); + scr_bytes_release(bytes); + return NULL; + } + ScrStr *canonical_text = scr_str_new(canonical, strlen(canonical)); + text = scr_bytes_to_str(bytes, canonical_text); + scr_str_release(canonical_text); + } + scr_bytes_release(bytes); + if (!text) return NULL; + ScrDyn *result = scr_dyn_new_str(text); + scr_str_release(text); + return result; +} + +#endif diff --git a/packages/runtime/src/scr_dh.c b/packages/runtime/src/scr_dh.c new file mode 100644 index 000000000..c514b45ba --- /dev/null +++ b/packages/runtime/src/scr_dh.c @@ -0,0 +1,1186 @@ +/* node:crypto DiffieHellman over the vendored mbedTLS bignum arithmetic: + * crypto.createDiffieHellman, crypto.getDiffieHellman and + * crypto.createDiffieHellmanGroup. A DiffieHellman or DiffieHellmanGroup + * is a native dyn handle. Construction follows Node 24's + * lib/internal/crypto/diffiehellman.js argument handling and + * src/crypto/crypto_dh.cc; key generation, verifyError, the public key + * range check and computeSecret follow the OpenSSL 3 paths of + * deps/ncrypto and OpenSSL 3.5's DH provider: + * - a group draws modp5 through modp18 private keys of a fixed width + * (200 to 400 bits) and modp1 and modp2 keys in [2, p - 2], and its + * verifyError is 0; + * - createDiffieHellman parameters that equal an OpenSSL named group + * draw keys below min(2^N, q) for the group's key length N; other + * parameters draw keys of bits(p) - 2 bits, and key generation and + * computeSecret need a 512- to 10000-bit prime; + * - createDiffieHellman(primeLength, generator) generates a safe prime + * p ≡ 23 (mod 24) for the generator 2, p ≡ 59 (mod 60) for 5 and + * p ≡ 11 (mod 12) otherwise, as OpenSSL's dh_builtin_genparams does. + * Primes longer than mbedTLS's 8192-bit limit throw a coded SC2020 + * error. All arguments are borrowed. */ +#include "scr_runtime.h" + +#include +#include +#include +#include +#include + +#include "mbedtls/bignum.h" +#include "mbedtls/platform_util.h" + +#include "scr_crypto_args.h" +#include "scr_dh_groups.h" + +/* The value the SC2020 error for an unlowered encoding names. */ +#define SD_SUBJECT "A DiffieHellman encoding" + +/* DH_MIN_MODULUS_BITS and OPENSSL_DH_MAX_MODULUS_BITS. */ +#define SD_MIN_MODULUS_BITS 512 +#define SD_MAX_MODULUS_BITS 10000 + +/* DH_check result bits, as verifyError reports them. */ +enum { + SD_P_NOT_PRIME = 0x01, + SD_P_NOT_SAFE_PRIME = 0x02, + SD_NOT_SUITABLE_GENERATOR = 0x08, + SD_MODULUS_TOO_SMALL = 0x80, +}; + +/* A crypto.getDiffieHellman group. */ +typedef struct { + const char *name; + const unsigned char *p; + size_t p_len; + int private_bits; /* ncrypto's GetDhGroupPrivateBits; 0 draws keys in [2, p - 2] */ +} ScDhGroup; + +static const ScDhGroup sd_groups[] = { + {"modp1", scr_dh_modp1_p, sizeof scr_dh_modp1_p, 0}, + {"modp2", scr_dh_modp2_p, sizeof scr_dh_modp2_p, 0}, + {"modp5", scr_dh_modp5_p, sizeof scr_dh_modp5_p, 200}, + {"modp14", scr_dh_modp14_p, sizeof scr_dh_modp14_p, 225}, + {"modp15", scr_dh_modp15_p, sizeof scr_dh_modp15_p, 275}, + {"modp16", scr_dh_modp16_p, sizeof scr_dh_modp16_p, 325}, + {"modp17", scr_dh_modp17_p, sizeof scr_dh_modp17_p, 375}, + {"modp18", scr_dh_modp18_p, sizeof scr_dh_modp18_p, 400}, +}; + +/* An OpenSSL DH named group, which the DH provider recognizes in explicit + * p and g. A NULL q is (p - 1) / 2. */ +typedef struct { + const unsigned char *p; + size_t p_len; + const unsigned char *g; + size_t g_len; + const unsigned char *q; + size_t q_len; + int keylength; +} ScDhNamed; + +static const unsigned char sd_two[] = {2}; + +#define SD_SAFE(p, keylength) {p, sizeof p, sd_two, 1, NULL, 0, keylength} +#define SD_RFC5114(name, keylength) \ + {scr_dh_##name##_p, sizeof scr_dh_##name##_p, scr_dh_##name##_g, sizeof scr_dh_##name##_g, \ + scr_dh_##name##_q, sizeof scr_dh_##name##_q, keylength} + +static const ScDhNamed sd_named[] = { + SD_SAFE(scr_dh_ffdhe2048_p, 225), SD_SAFE(scr_dh_ffdhe3072_p, 275), SD_SAFE(scr_dh_ffdhe4096_p, 325), + SD_SAFE(scr_dh_ffdhe6144_p, 375), SD_SAFE(scr_dh_ffdhe8192_p, 400), SD_SAFE(scr_dh_modp5_p, 200), + SD_SAFE(scr_dh_modp14_p, 225), SD_SAFE(scr_dh_modp15_p, 275), SD_SAFE(scr_dh_modp16_p, 325), + SD_SAFE(scr_dh_modp17_p, 375), SD_SAFE(scr_dh_modp18_p, 400), SD_RFC5114(dh_1024_160, 160), + SD_RFC5114(dh_2048_224, 224), SD_RFC5114(dh_2048_256, 224), +}; + +typedef struct ScDh { + size_t rc; + const ScDhGroup *group; /* crypto.getDiffieHellman's group, or NULL */ + const ScDhNamed *named; /* the named group createDiffieHellman's parameters equal, or NULL */ + mbedtls_mpi p, g; + mbedtls_mpi pub; /* when has_pub */ + mbedtls_mpi priv; /* when has_priv */ + bool has_pub; + bool has_priv; + int verify_error; /* -1 until computed */ +} ScDh; + +static ScrDynHandleOps sd_ops; +static ScrDynHandleOps sd_group_ops; + +static void *sd_retain(void *h) { + ((ScDh *)h)->rc++; + return h; +} + +static void sd_release(void *h) { + ScDh *d = h; + if (--d->rc) return; + mbedtls_mpi_free(&d->p); + mbedtls_mpi_free(&d->g); + mbedtls_mpi_free(&d->pub); + mbedtls_mpi_free(&d->priv); + mbedtls_platform_zeroize(d, sizeof *d); + free(d); +} + +static int sd_random(void *state, unsigned char *out, size_t len) { + (void)state; + arc4random_buf(out, len); + return 0; +} + +/* ── errors ───────────────────────────────────────────────────────────── */ + +static void sd_throw(int kind, const char *code, const char *message) { + scr_throw_error_msg_code(kind, message, strlen(message), code); +} + +static void sd_operation_failed(const char *message) { + sd_throw(SCR_ERR_ERROR, "ERR_CRYPTO_OPERATION_FAILED", message); +} + +/* An OpenSSL DH error as ThrowCryptoError reports it: the formatted + * message plus own library, reason and code properties, in Node's key + * order. */ +static void sd_throw_openssl(const char *packed, const char *reason, const char *code) { + char message[96]; + snprintf(message, sizeof message, "error:%s:Diffie-Hellman routines::%s", packed, reason); + ScrStr *text = scr_str_new(message, strlen(message)); + ScrError *e = scr_error_new(SCR_ERR_ERROR, text); + scr_str_release(text); + ScrStr *library = scr_str_new("Diffie-Hellman routines", 23); + scr_error_set_prop(e, "library", 7, scr_dyn_new_str(library)); + scr_str_release(library); + ScrStr *reason_text = scr_str_new(reason, strlen(reason)); + scr_error_set_prop(e, "reason", 6, scr_dyn_new_str(reason_text)); + scr_str_release(reason_text); + scr_error_set_code(e, code); + ScrStr *code_text = scr_str_new(code, strlen(code)); + scr_error_set_prop(e, "code", 4, scr_dyn_new_str(code_text)); + scr_str_release(code_text); + scr_throw_obj(e, &scr_error_retain_v, &scr_error_release_v, scr_error_trace_arg()); +} + +static void sd_bad_generator(void) { + sd_throw_openssl("02800065", "bad generator", "ERR_OSSL_DH_BAD_GENERATOR"); +} + +static void sd_too_long(void) { + static const char message[] = "DiffieHellman primes longer than 8192 bits have no native implementation"; + scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); +} + +/* validateInt32(value, name). */ +static bool sd_validate_int32(const char *name, double value) { + bool integer = isfinite(value) && trunc(value) == value; + if (integer && value >= INT32_MIN && value <= INT32_MAX) return true; + char received[48]; + scr_num_received(value, received); + char text[192]; + int n = integer ? snprintf(text, sizeof text, + "The value of \"%s\" is out of range. It must be >= -2147483648 && <= 2147483647. " + "Received %s", + name, received) + : snprintf(text, sizeof text, + "The value of \"%s\" is out of range. It must be an integer. Received %s", name, + received); + scr_throw_error_msg_code(SCR_ERR_RANGE, text, n < 0 ? 0 : (size_t)n, "ERR_OUT_OF_RANGE"); + return false; +} + +/* ── arithmetic ───────────────────────────────────────────────────────── */ + +/* The exponent digit width that keeps every mbedtls_mpi_exp_mod exponent + * within its 8192-bit limit. */ +#define SD_DIGIT_BITS (MBEDTLS_MPI_MAX_BITS - 1) + +/* base^e mod p, as BN_mod_exp_mont_consttime computes it: false for an + * even modulus or one below 3. Exponents past 8192 bits are split into + * SD_DIGIT_BITS-bit digits. */ +static bool sd_exp(mbedtls_mpi *out, const mbedtls_mpi *base, const mbedtls_mpi *e, const mbedtls_mpi *p) { + if (mbedtls_mpi_cmp_int(p, 1) <= 0 || mbedtls_mpi_get_bit(p, 0) == 0) return false; + size_t ebits = mbedtls_mpi_bitlen(e); + if (ebits <= MBEDTLS_MPI_MAX_BITS) return mbedtls_mpi_exp_mod(out, base, e, p, NULL) == 0; + /* base^e = Π (base^(2^(k·w)))^digit_k for the w-bit digits of e. */ + mbedtls_mpi acc, power, digit, term, step; + mbedtls_mpi_init(&acc); + mbedtls_mpi_init(&power); + mbedtls_mpi_init(&digit); + mbedtls_mpi_init(&term); + mbedtls_mpi_init(&step); + int ret = 0; + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&acc, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&power, base)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&step, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&step, SD_DIGIT_BITS)); + for (size_t offset = 0; offset < ebits; offset += SD_DIGIT_BITS) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&digit, e)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&digit, offset)); + for (size_t bit = SD_DIGIT_BITS; bit < mbedtls_mpi_bitlen(&digit); bit++) + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&digit, bit, 0)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&term, &power, &digit, p, NULL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&acc, &acc, &term)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&acc, &acc, p)); + if (offset + SD_DIGIT_BITS < ebits) MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&power, &power, &step, p, NULL)); + } + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(out, &acc)); +cleanup: + mbedtls_mpi_free(&acc); + mbedtls_mpi_free(&power); + mbedtls_mpi_free(&digit); + mbedtls_mpi_free(&term); + mbedtls_mpi_free(&step); + return ret == 0; +} + +/* A uniformly random number of exactly `bits` bits (the top bit set), and + * odd when `odd`, as BN_priv_rand writes it. */ +static bool sd_random_bits(mbedtls_mpi *x, size_t bits, bool odd) { + size_t len = (bits + 7) / 8; + unsigned char *buf = malloc(len); + if (!buf) scr_trap("scriptc: out of memory\n"); + arc4random_buf(buf, len); + unsigned extra = (unsigned)(len * 8 - bits); + buf[0] &= (unsigned char)(0xff >> extra); + buf[0] |= (unsigned char)(0x80 >> extra); + if (odd) buf[len - 1] |= 1; + bool ok = mbedtls_mpi_read_binary(x, buf, len) == 0; + mbedtls_platform_zeroize(buf, len); + free(buf); + return ok; +} + +/* ── primes ───────────────────────────────────────────────────────────── */ + +/* The odd primes below 17864 (OpenSSL's first 2048 primes but 2), which + * trial division divides by. Consecutive primes are grouped into products + * below the half-limb bound mbedtls_mpi_mod_int divides correctly by, so + * one call reduces by several. */ +#define SD_SMALL_PRIMES 2047 +#define SD_SMALL_LIMIT 17864 +#define SD_PRODUCT_LIMIT ((uint64_t)1 << (sizeof(mbedtls_mpi_uint) * 4)) + +typedef struct { + uint16_t primes[SD_SMALL_PRIMES]; + uint64_t products[SD_SMALL_PRIMES]; + uint16_t starts[SD_SMALL_PRIMES + 1]; /* products[g] covers primes[starts[g]] to primes[starts[g + 1] - 1] */ + size_t groups; + uint32_t mods[SD_SMALL_PRIMES]; +} ScDhSmallPrimes; + +static ScDhSmallPrimes *sd_small_primes(void) { + ScDhSmallPrimes *t = calloc(1, sizeof *t); + unsigned char *composite = calloc(SD_SMALL_LIMIT, 1); + if (!t || !composite) scr_trap("scriptc: out of memory\n"); + size_t count = 0; + for (uint32_t n = 3; n < SD_SMALL_LIMIT && count < SD_SMALL_PRIMES; n += 2) { + if (composite[n]) continue; + t->primes[count++] = (uint16_t)n; + for (uint32_t m = n * n; m < SD_SMALL_LIMIT; m += 2 * n) composite[m] = 1; + } + free(composite); + uint64_t product = 1; + for (size_t i = 0; i < SD_SMALL_PRIMES; i++) { + if (product > SD_PRODUCT_LIMIT / t->primes[i]) { + t->products[t->groups++] = product; + t->starts[t->groups] = (uint16_t)i; + product = 1; + } + product *= t->primes[i]; + } + t->products[t->groups++] = product; + t->starts[t->groups] = SD_SMALL_PRIMES; + return t; +} + +/* x mod each small prime, into t->mods. */ +static bool sd_small_residues(ScDhSmallPrimes *t, const mbedtls_mpi *x) { + for (size_t g = 0; g < t->groups; g++) { + mbedtls_mpi_uint r; + if (mbedtls_mpi_mod_int(&r, x, (mbedtls_mpi_sint)t->products[g]) != 0) return false; + for (size_t i = t->starts[g]; i < t->starts[g + 1]; i++) t->mods[i] = (uint32_t)(r % t->primes[i]); + } + return true; +} + +#if defined(__SIZEOF_INT128__) +#define SD_MONT 1 +typedef unsigned __int128 sd_u128; + +/* Montgomery arithmetic modulo an odd n of k 64-bit limbs, little-endian, + * with R = 2^(64k), for the Miller-Rabin rounds. It runs in variable time + * and reads only public values. */ +typedef struct { + size_t k; + uint64_t mm; /* -n^-1 mod 2^64 */ + uint64_t *n; /* k limbs */ + uint64_t *rr; /* R^2 mod n, k limbs */ + uint64_t *acc; /* k limbs */ + uint64_t *table; /* 16·k limbs: base^i·R mod n */ + uint64_t *t; /* 2k + 1 limbs of products */ +} ScDhMont; + +static void sd_limbs_read(uint64_t *out, size_t k, const mbedtls_mpi *x, unsigned char *buf) { + if (mbedtls_mpi_write_binary_le(x, buf, 8 * k) != 0) scr_trap("scriptc: internal error: limb overflow\n"); + for (size_t i = 0; i < k; i++) { + uint64_t w = 0; + for (size_t b = 8; b-- > 0;) w = (w << 8) | buf[8 * i + b]; + out[i] = w; + } +} + +static bool sd_limbs_write(mbedtls_mpi *x, const uint64_t *in, size_t k, unsigned char *buf) { + for (size_t i = 0; i < k; i++) + for (size_t b = 0; b < 8; b++) buf[8 * i + b] = (unsigned char)(in[i] >> (8 * b)); + return mbedtls_mpi_read_binary_le(x, buf, 8 * k) == 0; +} + +static bool sd_mont_init(ScDhMont *m, const mbedtls_mpi *n) { + size_t k = (mbedtls_mpi_bitlen(n) + 63) / 64; + m->k = k; + m->n = calloc(21 * k + 1, sizeof(uint64_t)); + if (!m->n) scr_trap("scriptc: out of memory\n"); + m->rr = m->n + k; + m->acc = m->rr + k; + m->table = m->acc + k; + m->t = m->table + 16 * k; + unsigned char *buf = malloc(8 * k); + if (!buf) scr_trap("scriptc: out of memory\n"); + sd_limbs_read(m->n, k, n, buf); + /* Newton's iteration doubles the correct low bits of n^-1 from 3. */ + uint64_t inverse = m->n[0]; + for (int i = 0; i < 6; i++) inverse *= 2 - m->n[0] * inverse; + m->mm = (uint64_t)0 - inverse; + mbedtls_mpi rr; + mbedtls_mpi_init(&rr); + bool ok = mbedtls_mpi_lset(&rr, 1) == 0 && mbedtls_mpi_shift_l(&rr, 128 * k) == 0 && + mbedtls_mpi_mod_mpi(&rr, &rr, n) == 0; + if (ok) sd_limbs_read(m->rr, k, &rr, buf); + mbedtls_mpi_free(&rr); + free(buf); + return ok; +} + +static void sd_mont_free(ScDhMont *m) { + free(m->n); +} + +/* r = v mod n for v < 2n, given as k limbs and a top limb. */ +static void sd_mont_final(const ScDhMont *m, uint64_t *r, const uint64_t *v, uint64_t top) { + size_t k = m->k; + bool ge = top != 0; + if (!ge) { + ge = true; + for (size_t j = k; j-- > 0;) + if (v[j] != m->n[j]) { + ge = v[j] > m->n[j]; + break; + } + } + if (!ge) { + if (r != v) memmove(r, v, k * sizeof *r); + return; + } + uint64_t borrow = 0; + for (size_t j = 0; j < k; j++) { + sd_u128 d = (sd_u128)v[j] - m->n[j] - borrow; + r[j] = (uint64_t)d; + borrow = (uint64_t)(d >> 64) & 1; + } +} + +/* r = t·R^-1 mod n for the 2k-limb product in t. */ +static void sd_mont_redc(ScDhMont *m, uint64_t *r) { + size_t k = m->k; + uint64_t *t = m->t; + t[2 * k] = 0; + for (size_t i = 0; i < k; i++) { + uint64_t q = t[i] * m->mm; + uint64_t carry = 0; + for (size_t j = 0; j < k; j++) { + sd_u128 v = (sd_u128)q * m->n[j] + t[i + j] + carry; + t[i + j] = (uint64_t)v; + carry = (uint64_t)(v >> 64); + } + for (size_t j = i + k; carry != 0 && j <= 2 * k; j++) { + sd_u128 v = (sd_u128)t[j] + carry; + t[j] = (uint64_t)v; + carry = (uint64_t)(v >> 64); + } + } + sd_mont_final(m, r, t + k, t[2 * k]); +} + +static void sd_mont_mul(ScDhMont *m, uint64_t *r, const uint64_t *a, const uint64_t *b) { + size_t k = m->k; + uint64_t *t = m->t; + memset(t, 0, 2 * k * sizeof *t); + for (size_t i = 0; i < k; i++) { + uint64_t carry = 0; + for (size_t j = 0; j < k; j++) { + sd_u128 v = (sd_u128)a[i] * b[j] + t[i + j] + carry; + t[i + j] = (uint64_t)v; + carry = (uint64_t)(v >> 64); + } + t[i + k] = carry; + } + sd_mont_redc(m, r); +} + +/* r = a^2·R^-1 mod n: the cross products once, doubled, plus the squares. */ +static void sd_mont_sqr(ScDhMont *m, uint64_t *r, const uint64_t *a) { + size_t k = m->k; + uint64_t *t = m->t; + memset(t, 0, 2 * k * sizeof *t); + for (size_t i = 0; i < k; i++) { + uint64_t carry = 0; + for (size_t j = i + 1; j < k; j++) { + sd_u128 v = (sd_u128)a[i] * a[j] + t[i + j] + carry; + t[i + j] = (uint64_t)v; + carry = (uint64_t)(v >> 64); + } + t[i + k] = carry; + } + uint64_t shifted = 0; + for (size_t i = 0; i < 2 * k; i++) { + uint64_t v = t[i]; + t[i] = (v << 1) | shifted; + shifted = v >> 63; + } + uint64_t carry = 0; + for (size_t i = 0; i < k; i++) { + sd_u128 square = (sd_u128)a[i] * a[i]; + sd_u128 low = (sd_u128)t[2 * i] + (uint64_t)square + carry; + t[2 * i] = (uint64_t)low; + sd_u128 high = (sd_u128)t[2 * i + 1] + (uint64_t)(square >> 64) + (uint64_t)(low >> 64); + t[2 * i + 1] = (uint64_t)high; + carry = (uint64_t)(high >> 64); + } + sd_mont_redc(m, r); +} + +/* x = 2x mod n. */ +static void sd_mont_double(const ScDhMont *m, uint64_t *x) { + size_t k = m->k; + uint64_t top = x[k - 1] >> 63; + for (size_t j = k; j-- > 1;) x[j] = (x[j] << 1) | (x[j - 1] >> 63); + x[0] <<= 1; + sd_mont_final(m, x, x, top); +} + +/* out = base^e mod n, or 2^e mod n for a NULL base. A base of 2 doubles + * instead of multiplying; others use a 4-bit fixed window. */ +static bool sd_mont_pow(ScDhMont *m, mbedtls_mpi *out, const mbedtls_mpi *base, const mbedtls_mpi *e) { + size_t k = m->k; + unsigned char *buf = malloc(8 * k); + if (!buf) scr_trap("scriptc: out of memory\n"); + /* acc = R mod n, the Montgomery form of 1. */ + memset(m->t, 0, 2 * k * sizeof *m->t); + memcpy(m->t, m->rr, k * sizeof *m->t); + sd_mont_redc(m, m->acc); + size_t bits = mbedtls_mpi_bitlen(e); + if (!base) { + for (size_t i = bits; i-- > 0;) { + sd_mont_sqr(m, m->acc, m->acc); + if (mbedtls_mpi_get_bit(e, i)) sd_mont_double(m, m->acc); + } + } else { + memcpy(m->table, m->acc, k * sizeof *m->table); + sd_limbs_read(m->table + k, k, base, buf); + sd_mont_mul(m, m->table + k, m->table + k, m->rr); + for (size_t i = 2; i < 16; i++) sd_mont_mul(m, m->table + i * k, m->table + (i - 1) * k, m->table + k); + size_t windows = (bits + 3) / 4; + for (size_t w = windows; w-- > 0;) { + if (w + 1 < windows) + for (int s = 0; s < 4; s++) sd_mont_sqr(m, m->acc, m->acc); + unsigned digit = 0; + for (size_t b = 4; b-- > 0;) digit = (digit << 1) | (unsigned)mbedtls_mpi_get_bit(e, 4 * w + b); + if (digit) sd_mont_mul(m, m->acc, m->acc, m->table + digit * k); + } + } + /* Leave the Montgomery form. */ + memset(m->t, 0, 2 * k * sizeof *m->t); + memcpy(m->t, m->acc, k * sizeof *m->t); + sd_mont_redc(m, m->acc); + bool ok = sd_limbs_write(out, m->acc, k, buf); + free(buf); + return ok; +} +#else +#define SD_MONT 0 +#endif + +/* Miller-Rabin state for an odd n > 3: n - 1 = d·2^s. */ +typedef struct { + mbedtls_mpi n1, d, a, x; + size_t s; +#if SD_MONT + ScDhMont mont; +#else + mbedtls_mpi rr; /* the Montgomery constant mbedtls_mpi_exp_mod caches */ +#endif +} ScDhMillerRabin; + +static bool sd_mr_init(ScDhMillerRabin *m, const mbedtls_mpi *n) { + mbedtls_mpi_init(&m->n1); + mbedtls_mpi_init(&m->d); + mbedtls_mpi_init(&m->a); + mbedtls_mpi_init(&m->x); +#if SD_MONT + if (!sd_mont_init(&m->mont, n)) return false; +#else + mbedtls_mpi_init(&m->rr); +#endif + if (mbedtls_mpi_sub_int(&m->n1, n, 1) != 0 || mbedtls_mpi_copy(&m->d, &m->n1) != 0) return false; + m->s = mbedtls_mpi_lsb(&m->d); + return mbedtls_mpi_shift_r(&m->d, m->s) == 0; +} + +static void sd_mr_free(ScDhMillerRabin *m) { + mbedtls_mpi_free(&m->n1); + mbedtls_mpi_free(&m->d); + mbedtls_mpi_free(&m->a); + mbedtls_mpi_free(&m->x); +#if SD_MONT + sd_mont_free(&m->mont); +#else + mbedtls_mpi_free(&m->rr); +#endif +} + +/* x = a^e mod n, or 2^e mod n when base2. */ +static bool sd_mr_pow(ScDhMillerRabin *m, const mbedtls_mpi *n, const mbedtls_mpi *e, bool base2) { +#if SD_MONT + (void)n; + return sd_mont_pow(&m->mont, &m->x, base2 ? NULL : &m->a, e); +#else + if (base2 && mbedtls_mpi_lset(&m->a, 2) != 0) return false; + return mbedtls_mpi_exp_mod(&m->x, &m->a, e, n, &m->rr) == 0; +#endif +} + +/* One round with the base 2, or with a uniformly random base in + * [2, n - 2]: 1 when n is a strong probable prime to the base, 0 when it + * is composite, -1 when an operation fails. */ +static int sd_mr_round(ScDhMillerRabin *m, const mbedtls_mpi *n, bool base2) { + if (!base2 && mbedtls_mpi_random(&m->a, 2, &m->n1, sd_random, NULL) != 0) return -1; + if (!sd_mr_pow(m, n, &m->d, base2)) return -1; + if (mbedtls_mpi_cmp_int(&m->x, 1) == 0 || mbedtls_mpi_cmp_mpi(&m->x, &m->n1) == 0) return 1; + for (size_t i = 1; i < m->s; i++) { + if (mbedtls_mpi_mul_mpi(&m->x, &m->x, &m->x) != 0 || mbedtls_mpi_mod_mpi(&m->x, &m->x, n) != 0) return -1; + if (mbedtls_mpi_cmp_mpi(&m->x, &m->n1) == 0) return 1; + if (mbedtls_mpi_cmp_int(&m->x, 1) == 0) return 0; + } + return 0; +} + +/* 2^(n-1) ≡ 1 (mod n) for an odd n > 3. */ +static int sd_fermat2(const mbedtls_mpi *n) { + ScDhMillerRabin m; + int ret = sd_mr_init(&m, n) && sd_mr_pow(&m, n, &m.n1, true) ? mbedtls_mpi_cmp_int(&m.x, 1) == 0 : -1; + sd_mr_free(&m); + return ret; +} + +/* OpenSSL's Miller-Rabin round count: 64, or 128 past 2048 bits. */ +static int sd_mr_rounds(size_t bits) { + return bits > 2048 ? 128 : 64; +} + +/* BN_check_prime: trial division by the small primes, then Miller-Rabin + * rounds with random bases. */ +static bool sd_is_prime(ScDhSmallPrimes *t, const mbedtls_mpi *x) { + if (mbedtls_mpi_cmp_int(x, 2) < 0) return false; + if (mbedtls_mpi_get_bit(x, 0) == 0) return mbedtls_mpi_cmp_int(x, 2) == 0; + if (!sd_small_residues(t, x)) return false; + for (size_t i = 0; i < SD_SMALL_PRIMES; i++) + if (t->mods[i] == 0) return mbedtls_mpi_cmp_int(x, t->primes[i]) == 0; + /* No prime factor up to sqrt(x). */ + if (mbedtls_mpi_cmp_int(x, (mbedtls_mpi_sint)SD_SMALL_LIMIT * SD_SMALL_LIMIT) < 0) return true; + ScDhMillerRabin m; + bool prime = sd_mr_init(&m, x); + int rounds = sd_mr_rounds(mbedtls_mpi_bitlen(x)); + for (int i = 0; i < rounds && prime; i++) prime = sd_mr_round(&m, x, false) == 1; + sd_mr_free(&m); + return prime; +} + +/* DH_check for parameters without q, as ncrypto's CheckDhParams reports + * it. */ +static int sd_check_params(const mbedtls_mpi *p, const mbedtls_mpi *g) { + int codes = 0; + if (mbedtls_mpi_get_bit(p, 0) == 0) codes |= SD_P_NOT_PRIME; + if (mbedtls_mpi_cmp_int(g, 1) <= 0) codes |= SD_NOT_SUITABLE_GENERATOR; + if (mbedtls_mpi_bitlen(p) < SD_MIN_MODULUS_BITS) codes |= SD_MODULUS_TOO_SMALL; + mbedtls_mpi t; + mbedtls_mpi_init(&t); + if (mbedtls_mpi_sub_int(&t, p, 1) != 0) scr_trap("scriptc: out of memory\n"); + if (mbedtls_mpi_cmp_mpi(g, &t) >= 0) codes |= SD_NOT_SUITABLE_GENERATOR; + /* BN_check_prime(p), then BN_check_prime((p - 1) / 2) for a prime p. + * For an odd p > 3 whose (p - 1) / 2 is prime, p is prime exactly when + * 3 does not divide it and 2^(p-1) ≡ 1 (mod p) (Pocklington's + * criterion), so (p - 1) / 2 is tested first. */ + ScDhSmallPrimes *small = sd_small_primes(); + if (mbedtls_mpi_copy(&t, p) != 0 || mbedtls_mpi_shift_r(&t, 1) != 0) scr_trap("scriptc: out of memory\n"); + bool p_prime, q_prime; + if (mbedtls_mpi_get_bit(p, 0) == 0 || mbedtls_mpi_cmp_int(p, 3) <= 0) { + p_prime = sd_is_prime(small, p); + q_prime = p_prime && sd_is_prime(small, &t); + } else { + q_prime = sd_is_prime(small, &t); + mbedtls_mpi_uint r = 0; + if (!q_prime) p_prime = sd_is_prime(small, p); + else p_prime = mbedtls_mpi_mod_int(&r, p, 3) == 0 && r != 0 && sd_fermat2(p) == 1; + } + if (!p_prime) codes |= SD_P_NOT_PRIME; + else if (!q_prime) codes |= SD_P_NOT_SAFE_PRIME; + free(small); + mbedtls_mpi_free(&t); + return codes; +} + +/* The named group whose p and g equal these, as ossl_dh_cache_named_group + * finds it, or NULL. */ +static const ScDhNamed *sd_find_named(const mbedtls_mpi *p, const mbedtls_mpi *g) { + size_t p_len = mbedtls_mpi_size(p); + const ScDhNamed *found = NULL; + mbedtls_mpi x; + mbedtls_mpi_init(&x); + for (size_t i = 0; i < sizeof sd_named / sizeof sd_named[0] && !found; i++) { + const ScDhNamed *n = &sd_named[i]; + if (n->p_len != p_len) continue; + if (mbedtls_mpi_read_binary(&x, n->p, n->p_len) != 0 || mbedtls_mpi_cmp_mpi(&x, p) != 0) continue; + if (mbedtls_mpi_read_binary(&x, n->g, n->g_len) == 0 && mbedtls_mpi_cmp_mpi(&x, g) == 0) found = n; + } + mbedtls_mpi_free(&x); + return found; +} + +/* The safe-prime sieve: the odd primes below a bound that grows with the + * prime's length and do not divide the step `add`, each with add^-1 and a + * start's residue modulo it, and the marks of a window of candidates + * start + k·add. */ +#define SD_SIEVE_WINDOW 65536 + +static uint32_t sd_sieve_bound(size_t bits) { + /* mbedtls_mpi_mod_int divides correctly by values below 2^(limb bits / 2). */ + if (sizeof(mbedtls_mpi_uint) < 8) return 1u << 16; + return bits < 1024 ? 1u << 18 : bits < 2048 ? 1u << 22 : 1u << 24; +} + +typedef struct { + uint32_t *primes; + uint32_t *inverse; + uint32_t *residue; + size_t count; + unsigned char marks[SD_SIEVE_WINDOW]; +} ScDhSieve; + +/* a^-1 mod m for gcd(a, m) = 1. */ +static uint32_t sd_inverse(uint32_t a, uint32_t m) { + int64_t t = 0, next = 1; + int64_t r = m, rest = a; + while (rest != 0) { + int64_t quotient = r / rest; + int64_t t_next = t - quotient * next; + t = next; + next = t_next; + int64_t r_next = r - quotient * rest; + r = rest; + rest = r_next; + } + return (uint32_t)(t < 0 ? t + m : t); +} + +static ScDhSieve *sd_sieve_new(size_t bits, unsigned add) { + uint32_t bound = sd_sieve_bound(bits); + ScDhSieve *sv = calloc(1, sizeof *sv); + unsigned char *composite = calloc(bound / 2, 1); /* composite[i] for 2i + 1 */ + if (!sv || !composite) scr_trap("scriptc: out of memory\n"); + size_t count = 0; + for (uint32_t i = 1; i < bound / 2; i++) { + if (composite[i]) continue; + uint32_t n = 2 * i + 1; + for (uint64_t m = (uint64_t)n * n; m < bound; m += 2 * n) composite[m / 2] = 1; + if (add % n != 0) count++; + } + sv->primes = malloc(count * sizeof(uint32_t)); + sv->inverse = malloc(count * sizeof(uint32_t)); + sv->residue = malloc(count * sizeof(uint32_t)); + if (!sv->primes || !sv->inverse || !sv->residue) scr_trap("scriptc: out of memory\n"); + for (uint32_t i = 1; i < bound / 2; i++) { + uint32_t n = 2 * i + 1; + if (composite[i] || add % n == 0) continue; + sv->inverse[sv->count] = sd_inverse(add % n, n); + sv->primes[sv->count++] = n; + } + free(composite); + return sv; +} + +static void sd_sieve_free(ScDhSieve *sv) { + free(sv->primes); + free(sv->inverse); + free(sv->residue); + free(sv); +} + +/* Marks the candidates start + k·add of the window that a sieve prime + * divides, or whose (p - 1) / 2 it divides, then moves the residues to + * the next window. */ +static void sd_sieve_window(ScDhSieve *sv, unsigned add) { + memset(sv->marks, 0, sizeof sv->marks); + for (size_t i = 0; i < sv->count; i++) { + uint64_t r = sv->primes[i]; + uint64_t s = sv->residue[i]; + /* start + k·add ≡ t (mod r) for t = 0 and t = 1. */ + for (uint64_t t = 0; t < 2; t++) { + uint64_t k = (r + t - s) % r * sv->inverse[i] % r; + for (; k < SD_SIEVE_WINDOW; k += r) sv->marks[k] = 1; + } + sv->residue[i] = (uint32_t)((s + (uint64_t)SD_SIEVE_WINDOW * add) % r); + } +} + +/* A random safe prime of exactly `bits` bits with p ≡ rem (mod add), + * rem ≡ 2 (mod 3), as BN_generate_prime_ex2(p, bits, safe = 1, add, rem) + * draws one: a random start of `bits` bits moved to p ≡ rem (mod add), + * then candidates stepping by `add` past values that a sieve prime + * divides or that make (p - 1) / 2 divisible by one. A candidate is kept + * when p is a strong probable prime to the base 2 and q = (p - 1) / 2 + * passes OpenSSL's Miller-Rabin round count; a prime q then proves p prime + * (Pocklington's criterion with 2^(p-1) ≡ 1 and gcd(2^2 - 1, p) = 1). + * Candidates past `bits` bits take a new start. */ +static bool sd_generate_safe_prime(mbedtls_mpi *p, size_t bits, unsigned add, unsigned rem) { + ScDhSieve *sv = sd_sieve_new(bits, add); + int rounds = sd_mr_rounds(bits); + mbedtls_mpi start, q; + mbedtls_mpi_init(&start); + mbedtls_mpi_init(&q); + int ret = 0; + while (ret == 0) { + mbedtls_mpi_uint r; + if (!sd_random_bits(&start, bits, true) || mbedtls_mpi_mod_int(&r, &start, add) != 0 || + mbedtls_mpi_sub_int(&start, &start, (mbedtls_mpi_sint)r) != 0 || + mbedtls_mpi_add_int(&start, &start, rem) != 0 || + (mbedtls_mpi_bitlen(&start) < bits && mbedtls_mpi_add_int(&start, &start, add) != 0)) { + ret = -1; + break; + } + for (size_t i = 0; i < sv->count && ret == 0; i++) { + if (mbedtls_mpi_mod_int(&r, &start, sv->primes[i]) != 0) ret = -1; + sv->residue[i] = (uint32_t)r; + } + bool fits = true; + while (ret == 0 && fits) { + sd_sieve_window(sv, add); + for (uint32_t k = 0; k < SD_SIEVE_WINDOW && ret == 0 && fits; k++) { + if (sv->marks[k]) continue; + if (mbedtls_mpi_add_int(p, &start, (mbedtls_mpi_sint)k * add) != 0 || mbedtls_mpi_copy(&q, p) != 0 || + mbedtls_mpi_shift_r(&q, 1) != 0) { + ret = -1; + break; + } + fits = mbedtls_mpi_bitlen(p) == bits; + if (!fits) break; + ScDhMillerRabin mr; + ret = sd_mr_init(&mr, p) ? sd_mr_round(&mr, p, true) : -1; + sd_mr_free(&mr); + if (ret != 1) continue; + ret = sd_mr_init(&mr, &q) ? sd_mr_round(&mr, &q, true) : -1; + for (int i = 1; i < rounds && ret == 1; i++) ret = sd_mr_round(&mr, &q, false); + sd_mr_free(&mr); + } + if (ret == 0 && fits && mbedtls_mpi_add_int(&start, &start, (mbedtls_mpi_sint)SD_SIEVE_WINDOW * add) != 0) + ret = -1; + } + } + sd_sieve_free(sv); + mbedtls_mpi_free(&start); + mbedtls_mpi_free(&q); + return ret == 1; +} + +/* ── construction ─────────────────────────────────────────────────────── */ + +static ScDh *sd_alloc(void) { + ScDh *d = calloc(1, sizeof *d); + if (!d) scr_trap("scriptc: out of memory\n"); + d->rc = 1; + mbedtls_mpi_init(&d->p); + mbedtls_mpi_init(&d->g); + mbedtls_mpi_init(&d->pub); + mbedtls_mpi_init(&d->priv); + return d; +} + +static ScrDyn *sd_box(ScDh *d) { + ScrDynHandleTag tag = d->group ? SCR_DYNH_DIFFIE_HELLMAN_GROUP : SCR_DYNH_DIFFIE_HELLMAN; + scr_dyn_handle_install(tag, d->group ? &sd_group_ops : &sd_ops); + ScrDyn *result = scr_dyn_new_handle(d, tag); /* retains */ + sd_release(d); + return result; +} + +static bool sd_is_view(const ScrDyn *value) { + return value->kind == SCR_DYN_BYTES || scr_buffer_storage_is(value); +} + +/* Buffer.isEncoding(value). */ +static bool sd_is_encoding(const ScrDyn *value) { + return value->kind == SCR_DYN_STR && value->v.str->len > 0 && ca_normalize_encoding(value->v.str) != NULL; +} + +/* new DiffieHellman(primeLength, generator): generated parameters. */ +static ScrDyn *sd_generate(double bits, const ScrBytes *generator_bytes, double generator) { + if (bits < 2) { + sd_throw_openssl("0280007E", "modulus too small", "ERR_OSSL_DH_MODULUS_TOO_SMALL"); + return NULL; + } + if (generator_bytes) { + sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_TYPE", "Second argument must be an int32"); + return NULL; + } + if (generator < 2) { + sd_bad_generator(); + return NULL; + } + if (bits < SD_MIN_MODULUS_BITS || bits > SD_MAX_MODULUS_BITS) { + sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_VALUE", "Invalid DH parameters"); + return NULL; + } + if (bits > MBEDTLS_MPI_MAX_BITS) { + sd_too_long(); + return NULL; + } + unsigned add = 12; + if (generator == 2) add = 24; + else if (generator == 5) add = 60; + ScDh *d = sd_alloc(); + if (!sd_generate_safe_prime(&d->p, (size_t)bits, add, add - 1) || mbedtls_mpi_lset(&d->g, (mbedtls_mpi_sint)generator) != 0) { + sd_release(d); + sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_VALUE", "Invalid DH parameters"); + return NULL; + } + /* A safe prime of at least 512 bits with a generator below p - 1 has no + * DH_check findings. */ + d->verify_error = 0; + return sd_box(d); +} + +/* new DiffieHellman(prime, generator) over explicit parameters. */ +static ScrDyn *sd_from_prime(const ScrBytes *prime, const ScrBytes *generator_bytes, double generator) { + ScDh *d = sd_alloc(); + if (mbedtls_mpi_read_binary(&d->p, prime->data, prime->len) != 0) scr_trap("scriptc: out of memory\n"); + if (generator_bytes) { + if (mbedtls_mpi_read_binary(&d->g, generator_bytes->data, generator_bytes->len) != 0) + scr_trap("scriptc: out of memory\n"); + } else if (mbedtls_mpi_lset(&d->g, (mbedtls_mpi_sint)generator) != 0) { + scr_trap("scriptc: out of memory\n"); + } + if (mbedtls_mpi_cmp_int(&d->g, 2) < 0 || + (mbedtls_mpi_bitlen(&d->p) >= SD_MIN_MODULUS_BITS && mbedtls_mpi_cmp_mpi(&d->g, &d->p) >= 0)) { + sd_release(d); + sd_bad_generator(); + return NULL; + } + if (mbedtls_mpi_bitlen(&d->p) > MBEDTLS_MPI_MAX_BITS) { + sd_release(d); + sd_too_long(); + return NULL; + } + d->named = sd_find_named(&d->p, &d->g); + /* A named group's parameters have no DH_check findings. Other + * parameters are checked when verifyError is first read. */ + d->verify_error = d->named ? 0 : -1; + return sd_box(d); +} + +/* new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding). */ +static ScrDyn *sd_new(const ScrDyn *size_or_key, const ScrDyn *key_encoding, const ScrDyn *generator, + const ScrDyn *generator_encoding) { + static const char *const types = + "one of type number or string or an instance of ArrayBuffer, Buffer, TypedArray, or DataView"; + bool size = size_or_key->kind == SCR_DYN_NUM; + if (!size && size_or_key->kind != SCR_DYN_STR && !sd_is_view(size_or_key)) { + scr_dyn_arg_type_fail("sizeOrKey", types, size_or_key); + return NULL; + } + if (size && !sd_validate_int32("sizeOrKey", size_or_key->v.num)) return NULL; + /* A truthy keyEncoding that names no encoding is the generator. */ + const ScrDyn *gen = generator; + const ScrDyn *gen_encoding = generator_encoding; + const ScrDyn *encoding = key_encoding; + if (scr_dyn_truthy(key_encoding) && !sd_is_encoding(key_encoding) && + !(key_encoding->kind == SCR_DYN_STR && ca_str_is(key_encoding->v.str, "buffer"))) { + gen_encoding = generator; + gen = key_encoding; + encoding = scr_dyn_undefined(); + } + ScrBytes *prime = NULL; + if (size_or_key->kind == SCR_DYN_STR) prime = ca_string_bytes(SD_SUBJECT, size_or_key->v.str, encoding); + else if (!size) prime = scr_bytes_buffer_source(size_or_key); + if (!size && !prime) return NULL; + ScrBytes *gen_bytes = NULL; + double gen_number = 2; + ScrDyn *result = NULL; + if (!scr_dyn_truthy(gen)) { + gen_number = 2; + } else if (gen->kind == SCR_DYN_NUM) { + if (!sd_validate_int32("generator", gen->v.num)) goto done; + gen_number = gen->v.num; + } else if (gen->kind == SCR_DYN_STR) { + if (!(gen_bytes = ca_string_bytes(SD_SUBJECT, gen->v.str, gen_encoding))) goto done; + } else if (sd_is_view(gen)) { + gen_bytes = scr_bytes_buffer_source(gen); + } else { + scr_dyn_arg_type_fail("generator", types, gen); + goto done; + } + result = size ? sd_generate(size_or_key->v.num + 0, gen_bytes, gen_number) + : sd_from_prime(prime, gen_bytes, gen_number); +done: + if (prime) scr_bytes_release(prime); + if (gen_bytes) scr_bytes_release(gen_bytes); + return result; +} + +ScrDyn *scr_crypto_dh_new(ScrDyn *size_or_key, ScrDyn *key_encoding, ScrDyn *generator, ScrDyn *generator_encoding) { + ScrDyn *args[4] = {size_or_key, key_encoding, generator, generator_encoding}; + ScrDyn *unboxed[4] = {NULL, NULL, NULL, NULL}; + ScrDyn *result = NULL; + for (size_t i = 0; i < 4; i++) + if (!(unboxed[i] = ca_unbox(args[i]))) goto done; + result = sd_new(unboxed[0], unboxed[1], unboxed[2], unboxed[3]); +done: + for (size_t i = 0; i < 4; i++) scr_dyn_release(unboxed[i]); + return result; +} + +/* new DiffieHellmanGroup(name): names match case-insensitively. */ +ScrDyn *scr_crypto_dh_group_new(ScrDyn *name) { + ScrDyn *unboxed = ca_unbox(name); + if (!unboxed) return NULL; + if (unboxed->kind != SCR_DYN_STR) { + scr_dyn_release(unboxed); + sd_throw(SCR_ERR_TYPE, "ERR_INVALID_ARG_TYPE", "Group name must be a string"); + return NULL; + } + const ScDhGroup *group = NULL; + for (size_t i = 0; i < sizeof sd_groups / sizeof sd_groups[0] && !group; i++) + if (ca_ascii_eq_ci(unboxed->v.str->data, unboxed->v.str->len, sd_groups[i].name)) group = &sd_groups[i]; + scr_dyn_release(unboxed); + if (!group) { + sd_throw(SCR_ERR_ERROR, "ERR_CRYPTO_UNKNOWN_DH_GROUP", "Unknown DH group"); + return NULL; + } + ScDh *d = sd_alloc(); + d->group = group; + if (mbedtls_mpi_read_binary(&d->p, group->p, group->p_len) != 0 || mbedtls_mpi_lset(&d->g, 2) != 0) + scr_trap("scriptc: out of memory\n"); + d->verify_error = 0; + return sd_box(d); +} + +/* ── methods ──────────────────────────────────────────────────────────── */ + +static ScrDyn *sd_arg(ScrDyn *const *args, size_t argc, size_t index) { + return index < argc ? args[index] : scr_dyn_undefined(); +} + +/* The minimal big-endian bytes of a value, as BignumPointer::Encode + * writes them (none for 0), passed through encode(). */ +static ScrDyn *sd_encode_value(const mbedtls_mpi *value, const ScrDyn *encoding) { + size_t len = mbedtls_mpi_size(value); + ScrBytes *out = scr_bytes_new(SCR_BYTES_U8, (double)len); + if (mbedtls_mpi_write_binary(value, out->data, len) != 0) scr_trap("scriptc: out of memory\n"); + return ca_encode(SD_SUBJECT, out, encoding); +} + +/* A new private key for generateKeys(). */ +static bool sd_new_private_key(ScDh *d, mbedtls_mpi *priv) { + if (d->group) { + if (d->group->private_bits) return sd_random_bits(priv, (size_t)d->group->private_bits, false); + /* BN_priv_rand_range(p - 3) + 2. */ + mbedtls_mpi bound; + mbedtls_mpi_init(&bound); + bool ok = mbedtls_mpi_sub_int(&bound, &d->p, 1) == 0 && mbedtls_mpi_random(priv, 2, &bound, sd_random, NULL) == 0; + mbedtls_mpi_free(&bound); + return ok; + } + size_t bits = mbedtls_mpi_bitlen(&d->p); + if (bits < SD_MIN_MODULUS_BITS || bits > SD_MAX_MODULUS_BITS) return false; + if (!d->named) return sd_random_bits(priv, bits - 2, false); + /* ossl_ffc_generate_private_key: 1 <= priv < min(2^N, q). */ + mbedtls_mpi bound, q; + mbedtls_mpi_init(&bound); + mbedtls_mpi_init(&q); + bool ok = mbedtls_mpi_lset(&bound, 1) == 0 && mbedtls_mpi_shift_l(&bound, (size_t)d->named->keylength) == 0; + if (ok && d->named->q) ok = mbedtls_mpi_read_binary(&q, d->named->q, d->named->q_len) == 0; + else if (ok) ok = mbedtls_mpi_copy(&q, &d->p) == 0 && mbedtls_mpi_shift_r(&q, 1) == 0; + if (ok && mbedtls_mpi_cmp_mpi(&q, &bound) < 0) ok = mbedtls_mpi_copy(&bound, &q) == 0; + ok = ok && mbedtls_mpi_random(priv, 1, &bound, sd_random, NULL) == 0; + mbedtls_mpi_free(&bound); + mbedtls_mpi_free(&q); + return ok; +} + +/* generateKeys(encoding): a new key pair, or the public key of the + * private key the object has. */ +static ScrDyn *sd_generate_keys(ScDh *d, const ScrDyn *encoding) { + mbedtls_mpi priv, pub; + mbedtls_mpi_init(&priv); + mbedtls_mpi_init(&pub); + bool fresh = !d->has_priv; + bool ok = fresh ? sd_new_private_key(d, &priv) : mbedtls_mpi_copy(&priv, &d->priv) == 0; + ok = ok && sd_exp(&pub, &d->g, &priv, &d->p); + if (ok) { + mbedtls_mpi_swap(&d->priv, &priv); + mbedtls_mpi_swap(&d->pub, &pub); + d->has_priv = true; + d->has_pub = true; + } + mbedtls_mpi_free(&priv); + mbedtls_mpi_free(&pub); + if (!ok || mbedtls_mpi_cmp_int(&d->pub, 0) == 0) { + sd_operation_failed("Key generation failed"); + return NULL; + } + return sd_encode_value(&d->pub, encoding); +} + +/* Whether OpenSSL's DH derivation accepts explicit parameters' private + * key: a nonzero key and a 512- to 10000-bit prime. */ +static bool sd_derives(const ScDh *d) { + size_t bits = mbedtls_mpi_bitlen(&d->p); + return mbedtls_mpi_cmp_int(&d->priv, 0) != 0 && bits >= SD_MIN_MODULUS_BITS && bits <= SD_MAX_MODULUS_BITS; +} + +/* computeSecret(key, inputEncoding, outputEncoding): key^priv mod p, + * padded to the prime's length. */ +static ScrDyn *sd_compute_secret(ScDh *d, const ScrDyn *key, const ScrDyn *in_enc, const ScrDyn *out_enc) { + ScrBytes *bytes = ca_key_bytes(SD_SUBJECT, key, in_enc); + if (!bytes) return NULL; + ScrDyn *result = NULL; + mbedtls_mpi peer, bound, secret; + mbedtls_mpi_init(&peer); + mbedtls_mpi_init(&bound); + mbedtls_mpi_init(&secret); + if (mbedtls_mpi_read_binary(&peer, bytes->data, bytes->len) != 0 || mbedtls_mpi_sub_int(&bound, &d->p, 1) != 0) + scr_trap("scriptc: out of memory\n"); + if (mbedtls_mpi_cmp_int(&peer, 1) <= 0) { + sd_throw(SCR_ERR_RANGE, "ERR_CRYPTO_INVALID_KEYLEN", "Supplied key is too small"); + } else if (mbedtls_mpi_cmp_mpi(&peer, &bound) >= 0) { + sd_throw(SCR_ERR_RANGE, "ERR_CRYPTO_INVALID_KEYLEN", "Supplied key is too large"); + } else if (!d->has_priv || (!d->group && !sd_derives(d)) || !sd_exp(&secret, &peer, &d->priv, &d->p)) { + sd_operation_failed("Failed to compute shared secret"); + } else { + size_t len = mbedtls_mpi_size(&d->p); + ScrBytes *out = scr_bytes_new(SCR_BYTES_U8, (double)len); + if (mbedtls_mpi_write_binary(&secret, out->data, len) != 0) scr_trap("scriptc: out of memory\n"); + result = ca_encode(SD_SUBJECT, out, out_enc); + } + mbedtls_mpi_free(&peer); + mbedtls_mpi_free(&bound); + mbedtls_mpi_free(&secret); + scr_bytes_release(bytes); + return result; +} + +static ScrDyn *sd_get_value(const mbedtls_mpi *value, bool present, const char *missing, const ScrDyn *encoding) { + if (!present || mbedtls_mpi_cmp_int(value, 0) == 0) { + sd_throw(SCR_ERR_ERROR, "ERR_CRYPTO_INVALID_STATE", missing); + return NULL; + } + return sd_encode_value(value, encoding); +} + +/* setPublicKey(key, encoding) and setPrivateKey(key, encoding). */ +static ScrDyn *sd_set_value(mbedtls_mpi *value, bool *present, ScrDyn *self, const ScrDyn *key, const ScrDyn *encoding) { + ScrBytes *bytes = ca_key_bytes(SD_SUBJECT, key, encoding); + if (!bytes) return NULL; + if (mbedtls_mpi_read_binary(value, bytes->data, bytes->len) != 0) scr_trap("scriptc: out of memory\n"); + *present = true; + scr_bytes_release(bytes); + return scr_dyn_retain(self); +} + +static bool sd_name_is(const char *method, const char *name) { + return strcmp(method, name) == 0; +} + +static ScrDyn *sd_dispatch(ScDh *d, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc, + const char *what) { + const ScrDyn *a0 = sd_arg(args, argc, 0); + if (sd_name_is(method, "generateKeys")) return sd_generate_keys(d, a0); + if (sd_name_is(method, "computeSecret")) + return sd_compute_secret(d, a0, sd_arg(args, argc, 1), sd_arg(args, argc, 2)); + if (sd_name_is(method, "getPrime")) return sd_get_value(&d->p, true, "p is null", a0); + if (sd_name_is(method, "getGenerator")) return sd_get_value(&d->g, true, "g is null", a0); + if (sd_name_is(method, "getPublicKey")) + return sd_get_value(&d->pub, d->has_pub, "No public key - did you forget to generate one?", a0); + if (sd_name_is(method, "getPrivateKey")) + return sd_get_value(&d->priv, d->has_priv, "No private key - did you forget to generate one?", a0); + /* DiffieHellmanGroup has no key setters. */ + if (!d->group && sd_name_is(method, "setPublicKey")) + return sd_set_value(&d->pub, &d->has_pub, self, a0, sd_arg(args, argc, 1)); + if (!d->group && sd_name_is(method, "setPrivateKey")) + return sd_set_value(&d->priv, &d->has_priv, self, a0, sd_arg(args, argc, 1)); + /* The Object.prototype methods a DiffieHellman inherits. */ + static const char *const inherited[] = {"constructor", "hasOwnProperty", "isPrototypeOf", + "propertyIsEnumerable", "toLocaleString", "toString", + "valueOf"}; + for (size_t i = 0; i < sizeof inherited / sizeof inherited[0]; i++) + if (sd_name_is(method, inherited[i])) { + static const char message[] = "DiffieHellman's Object.prototype methods have no lowering"; + scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); + return NULL; + } + ScrJsonBuf b; + scr_jb_init(&b); + scr_jb_puts(&b, what); + scr_jb_puts(&b, " is not a function"); + scr_throw_error(SCR_ERR_TYPE, scr_jb_finish(&b)); + return NULL; +} + +/* Every method reads at most three arguments. */ +static ScrDyn *sd_invoke(void *h, ScrDyn *self, const char *method, ScrDyn *const *args, size_t argc, + const char *what) { + ScrDyn *unboxed[3] = {NULL, NULL, NULL}; + size_t count = argc < 3 ? argc : 3; + ScrDyn *result = NULL; + for (size_t i = 0; i < count; i++) + if (!(unboxed[i] = ca_unbox(args[i]))) goto done; + result = sd_dispatch(h, self, method, unboxed, count, what); +done: + for (size_t i = 0; i < count; i++) scr_dyn_release(unboxed[i]); + return result; +} + +/* verifyError, the one own data property. */ +static ScrDyn *sd_get(void *h, const char *key, size_t key_len) { + ScDh *d = h; + if (key_len == 11 && memcmp(key, "verifyError", 11) == 0) { + if (d->verify_error < 0) d->verify_error = sd_check_params(&d->p, &d->g); + return scr_dyn_new_num(d->verify_error); + } + static const char message[] = + "DiffieHellman property reads other than verifyError have no lowering; call generateKeys(), " + "computeSecret(), getPrime(), getGenerator(), getPublicKey(), getPrivateKey(), setPublicKey() or " + "setPrivateKey()"; + scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); + return NULL; +} + +static bool sd_set(void *h, const char *key, size_t key_len, const ScrDyn *value) { + (void)h; (void)key; (void)key_len; (void)value; + static const char message[] = "Writing DiffieHellman properties has no lowering"; + scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); + return true; +} + +static ScrDynHandleOps sd_ops = {"DiffieHellman", sd_retain, sd_release, sd_invoke, sd_get, sd_set, NULL, NULL, NULL}; +static ScrDynHandleOps sd_group_ops = {"DiffieHellmanGroup", sd_retain, sd_release, sd_invoke, sd_get, sd_set, + NULL, NULL, NULL}; diff --git a/packages/runtime/src/scr_dh_groups.h b/packages/runtime/src/scr_dh_groups.h new file mode 100644 index 000000000..c82ec7270 --- /dev/null +++ b/packages/runtime/src/scr_dh_groups.h @@ -0,0 +1,557 @@ +/* Diffie-Hellman group parameters, big-endian: the primes behind Node 24's + * crypto.getDiffieHellman groups (modp1 and modp2 from RFC 2409, modp5 and + * modp14 through modp18 from RFC 3526), and the RFC 7919 (ffdhe2048 through + * ffdhe8192) and RFC 5114 (dh_1024_160, dh_2048_224 and dh_2048_256) + * parameters OpenSSL 3.5 lists among its DH named groups + * (crypto/ffc/ffc_dh.c). Every group but RFC 5114's uses the generator 2 + * and a safe prime. */ +#ifndef SCR_DH_GROUPS_H +#define SCR_DH_GROUPS_H + +static const unsigned char scr_dh_modp1_p[96] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_modp2_p[128] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_modp5_p[192] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x23, 0x73, 0x27, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_modp14_p[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, + 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, + 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, + 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xac, 0xaa, 0x68, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_modp15_p[384] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, + 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, + 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, + 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, + 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, + 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, + 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, + 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, + 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, + 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, + 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, + 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_modp16_p[512] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, + 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, + 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, + 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, + 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, + 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, + 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, + 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, + 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, + 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, + 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, + 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x21, 0x08, 0x01, 0x1a, 0x72, 0x3c, 0x12, 0xa7, 0x87, 0xe6, 0xd7, + 0x88, 0x71, 0x9a, 0x10, 0xbd, 0xba, 0x5b, 0x26, 0x99, 0xc3, 0x27, 0x18, 0x6a, 0xf4, 0xe2, 0x3c, + 0x1a, 0x94, 0x68, 0x34, 0xb6, 0x15, 0x0b, 0xda, 0x25, 0x83, 0xe9, 0xca, 0x2a, 0xd4, 0x4c, 0xe8, + 0xdb, 0xbb, 0xc2, 0xdb, 0x04, 0xde, 0x8e, 0xf9, 0x2e, 0x8e, 0xfc, 0x14, 0x1f, 0xbe, 0xca, 0xa6, + 0x28, 0x7c, 0x59, 0x47, 0x4e, 0x6b, 0xc0, 0x5d, 0x99, 0xb2, 0x96, 0x4f, 0xa0, 0x90, 0xc3, 0xa2, + 0x23, 0x3b, 0xa1, 0x86, 0x51, 0x5b, 0xe7, 0xed, 0x1f, 0x61, 0x29, 0x70, 0xce, 0xe2, 0xd7, 0xaf, + 0xb8, 0x1b, 0xdd, 0x76, 0x21, 0x70, 0x48, 0x1c, 0xd0, 0x06, 0x91, 0x27, 0xd5, 0xb0, 0x5a, 0xa9, + 0x93, 0xb4, 0xea, 0x98, 0x8d, 0x8f, 0xdd, 0xc1, 0x86, 0xff, 0xb7, 0xdc, 0x90, 0xa6, 0xc0, 0x8f, + 0x4d, 0xf4, 0x35, 0xc9, 0x34, 0x06, 0x31, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_modp17_p[768] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, + 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, + 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, + 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, + 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, + 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, + 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, + 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, + 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, + 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, + 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, + 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x21, 0x08, 0x01, 0x1a, 0x72, 0x3c, 0x12, 0xa7, 0x87, 0xe6, 0xd7, + 0x88, 0x71, 0x9a, 0x10, 0xbd, 0xba, 0x5b, 0x26, 0x99, 0xc3, 0x27, 0x18, 0x6a, 0xf4, 0xe2, 0x3c, + 0x1a, 0x94, 0x68, 0x34, 0xb6, 0x15, 0x0b, 0xda, 0x25, 0x83, 0xe9, 0xca, 0x2a, 0xd4, 0x4c, 0xe8, + 0xdb, 0xbb, 0xc2, 0xdb, 0x04, 0xde, 0x8e, 0xf9, 0x2e, 0x8e, 0xfc, 0x14, 0x1f, 0xbe, 0xca, 0xa6, + 0x28, 0x7c, 0x59, 0x47, 0x4e, 0x6b, 0xc0, 0x5d, 0x99, 0xb2, 0x96, 0x4f, 0xa0, 0x90, 0xc3, 0xa2, + 0x23, 0x3b, 0xa1, 0x86, 0x51, 0x5b, 0xe7, 0xed, 0x1f, 0x61, 0x29, 0x70, 0xce, 0xe2, 0xd7, 0xaf, + 0xb8, 0x1b, 0xdd, 0x76, 0x21, 0x70, 0x48, 0x1c, 0xd0, 0x06, 0x91, 0x27, 0xd5, 0xb0, 0x5a, 0xa9, + 0x93, 0xb4, 0xea, 0x98, 0x8d, 0x8f, 0xdd, 0xc1, 0x86, 0xff, 0xb7, 0xdc, 0x90, 0xa6, 0xc0, 0x8f, + 0x4d, 0xf4, 0x35, 0xc9, 0x34, 0x02, 0x84, 0x92, 0x36, 0xc3, 0xfa, 0xb4, 0xd2, 0x7c, 0x70, 0x26, + 0xc1, 0xd4, 0xdc, 0xb2, 0x60, 0x26, 0x46, 0xde, 0xc9, 0x75, 0x1e, 0x76, 0x3d, 0xba, 0x37, 0xbd, + 0xf8, 0xff, 0x94, 0x06, 0xad, 0x9e, 0x53, 0x0e, 0xe5, 0xdb, 0x38, 0x2f, 0x41, 0x30, 0x01, 0xae, + 0xb0, 0x6a, 0x53, 0xed, 0x90, 0x27, 0xd8, 0x31, 0x17, 0x97, 0x27, 0xb0, 0x86, 0x5a, 0x89, 0x18, + 0xda, 0x3e, 0xdb, 0xeb, 0xcf, 0x9b, 0x14, 0xed, 0x44, 0xce, 0x6c, 0xba, 0xce, 0xd4, 0xbb, 0x1b, + 0xdb, 0x7f, 0x14, 0x47, 0xe6, 0xcc, 0x25, 0x4b, 0x33, 0x20, 0x51, 0x51, 0x2b, 0xd7, 0xaf, 0x42, + 0x6f, 0xb8, 0xf4, 0x01, 0x37, 0x8c, 0xd2, 0xbf, 0x59, 0x83, 0xca, 0x01, 0xc6, 0x4b, 0x92, 0xec, + 0xf0, 0x32, 0xea, 0x15, 0xd1, 0x72, 0x1d, 0x03, 0xf4, 0x82, 0xd7, 0xce, 0x6e, 0x74, 0xfe, 0xf6, + 0xd5, 0x5e, 0x70, 0x2f, 0x46, 0x98, 0x0c, 0x82, 0xb5, 0xa8, 0x40, 0x31, 0x90, 0x0b, 0x1c, 0x9e, + 0x59, 0xe7, 0xc9, 0x7f, 0xbe, 0xc7, 0xe8, 0xf3, 0x23, 0xa9, 0x7a, 0x7e, 0x36, 0xcc, 0x88, 0xbe, + 0x0f, 0x1d, 0x45, 0xb7, 0xff, 0x58, 0x5a, 0xc5, 0x4b, 0xd4, 0x07, 0xb2, 0x2b, 0x41, 0x54, 0xaa, + 0xcc, 0x8f, 0x6d, 0x7e, 0xbf, 0x48, 0xe1, 0xd8, 0x14, 0xcc, 0x5e, 0xd2, 0x0f, 0x80, 0x37, 0xe0, + 0xa7, 0x97, 0x15, 0xee, 0xf2, 0x9b, 0xe3, 0x28, 0x06, 0xa1, 0xd5, 0x8b, 0xb7, 0xc5, 0xda, 0x76, + 0xf5, 0x50, 0xaa, 0x3d, 0x8a, 0x1f, 0xbf, 0xf0, 0xeb, 0x19, 0xcc, 0xb1, 0xa3, 0x13, 0xd5, 0x5c, + 0xda, 0x56, 0xc9, 0xec, 0x2e, 0xf2, 0x96, 0x32, 0x38, 0x7f, 0xe8, 0xd7, 0x6e, 0x3c, 0x04, 0x68, + 0x04, 0x3e, 0x8f, 0x66, 0x3f, 0x48, 0x60, 0xee, 0x12, 0xbf, 0x2d, 0x5b, 0x0b, 0x74, 0x74, 0xd6, + 0xe6, 0x94, 0xf9, 0x1e, 0x6d, 0xcc, 0x40, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_modp18_p[1024] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, + 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, + 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, + 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, + 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, + 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, + 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, + 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, + 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, + 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, + 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, + 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x21, 0x08, 0x01, 0x1a, 0x72, 0x3c, 0x12, 0xa7, 0x87, 0xe6, 0xd7, + 0x88, 0x71, 0x9a, 0x10, 0xbd, 0xba, 0x5b, 0x26, 0x99, 0xc3, 0x27, 0x18, 0x6a, 0xf4, 0xe2, 0x3c, + 0x1a, 0x94, 0x68, 0x34, 0xb6, 0x15, 0x0b, 0xda, 0x25, 0x83, 0xe9, 0xca, 0x2a, 0xd4, 0x4c, 0xe8, + 0xdb, 0xbb, 0xc2, 0xdb, 0x04, 0xde, 0x8e, 0xf9, 0x2e, 0x8e, 0xfc, 0x14, 0x1f, 0xbe, 0xca, 0xa6, + 0x28, 0x7c, 0x59, 0x47, 0x4e, 0x6b, 0xc0, 0x5d, 0x99, 0xb2, 0x96, 0x4f, 0xa0, 0x90, 0xc3, 0xa2, + 0x23, 0x3b, 0xa1, 0x86, 0x51, 0x5b, 0xe7, 0xed, 0x1f, 0x61, 0x29, 0x70, 0xce, 0xe2, 0xd7, 0xaf, + 0xb8, 0x1b, 0xdd, 0x76, 0x21, 0x70, 0x48, 0x1c, 0xd0, 0x06, 0x91, 0x27, 0xd5, 0xb0, 0x5a, 0xa9, + 0x93, 0xb4, 0xea, 0x98, 0x8d, 0x8f, 0xdd, 0xc1, 0x86, 0xff, 0xb7, 0xdc, 0x90, 0xa6, 0xc0, 0x8f, + 0x4d, 0xf4, 0x35, 0xc9, 0x34, 0x02, 0x84, 0x92, 0x36, 0xc3, 0xfa, 0xb4, 0xd2, 0x7c, 0x70, 0x26, + 0xc1, 0xd4, 0xdc, 0xb2, 0x60, 0x26, 0x46, 0xde, 0xc9, 0x75, 0x1e, 0x76, 0x3d, 0xba, 0x37, 0xbd, + 0xf8, 0xff, 0x94, 0x06, 0xad, 0x9e, 0x53, 0x0e, 0xe5, 0xdb, 0x38, 0x2f, 0x41, 0x30, 0x01, 0xae, + 0xb0, 0x6a, 0x53, 0xed, 0x90, 0x27, 0xd8, 0x31, 0x17, 0x97, 0x27, 0xb0, 0x86, 0x5a, 0x89, 0x18, + 0xda, 0x3e, 0xdb, 0xeb, 0xcf, 0x9b, 0x14, 0xed, 0x44, 0xce, 0x6c, 0xba, 0xce, 0xd4, 0xbb, 0x1b, + 0xdb, 0x7f, 0x14, 0x47, 0xe6, 0xcc, 0x25, 0x4b, 0x33, 0x20, 0x51, 0x51, 0x2b, 0xd7, 0xaf, 0x42, + 0x6f, 0xb8, 0xf4, 0x01, 0x37, 0x8c, 0xd2, 0xbf, 0x59, 0x83, 0xca, 0x01, 0xc6, 0x4b, 0x92, 0xec, + 0xf0, 0x32, 0xea, 0x15, 0xd1, 0x72, 0x1d, 0x03, 0xf4, 0x82, 0xd7, 0xce, 0x6e, 0x74, 0xfe, 0xf6, + 0xd5, 0x5e, 0x70, 0x2f, 0x46, 0x98, 0x0c, 0x82, 0xb5, 0xa8, 0x40, 0x31, 0x90, 0x0b, 0x1c, 0x9e, + 0x59, 0xe7, 0xc9, 0x7f, 0xbe, 0xc7, 0xe8, 0xf3, 0x23, 0xa9, 0x7a, 0x7e, 0x36, 0xcc, 0x88, 0xbe, + 0x0f, 0x1d, 0x45, 0xb7, 0xff, 0x58, 0x5a, 0xc5, 0x4b, 0xd4, 0x07, 0xb2, 0x2b, 0x41, 0x54, 0xaa, + 0xcc, 0x8f, 0x6d, 0x7e, 0xbf, 0x48, 0xe1, 0xd8, 0x14, 0xcc, 0x5e, 0xd2, 0x0f, 0x80, 0x37, 0xe0, + 0xa7, 0x97, 0x15, 0xee, 0xf2, 0x9b, 0xe3, 0x28, 0x06, 0xa1, 0xd5, 0x8b, 0xb7, 0xc5, 0xda, 0x76, + 0xf5, 0x50, 0xaa, 0x3d, 0x8a, 0x1f, 0xbf, 0xf0, 0xeb, 0x19, 0xcc, 0xb1, 0xa3, 0x13, 0xd5, 0x5c, + 0xda, 0x56, 0xc9, 0xec, 0x2e, 0xf2, 0x96, 0x32, 0x38, 0x7f, 0xe8, 0xd7, 0x6e, 0x3c, 0x04, 0x68, + 0x04, 0x3e, 0x8f, 0x66, 0x3f, 0x48, 0x60, 0xee, 0x12, 0xbf, 0x2d, 0x5b, 0x0b, 0x74, 0x74, 0xd6, + 0xe6, 0x94, 0xf9, 0x1e, 0x6d, 0xbe, 0x11, 0x59, 0x74, 0xa3, 0x92, 0x6f, 0x12, 0xfe, 0xe5, 0xe4, + 0x38, 0x77, 0x7c, 0xb6, 0xa9, 0x32, 0xdf, 0x8c, 0xd8, 0xbe, 0xc4, 0xd0, 0x73, 0xb9, 0x31, 0xba, + 0x3b, 0xc8, 0x32, 0xb6, 0x8d, 0x9d, 0xd3, 0x00, 0x74, 0x1f, 0xa7, 0xbf, 0x8a, 0xfc, 0x47, 0xed, + 0x25, 0x76, 0xf6, 0x93, 0x6b, 0xa4, 0x24, 0x66, 0x3a, 0xab, 0x63, 0x9c, 0x5a, 0xe4, 0xf5, 0x68, + 0x34, 0x23, 0xb4, 0x74, 0x2b, 0xf1, 0xc9, 0x78, 0x23, 0x8f, 0x16, 0xcb, 0xe3, 0x9d, 0x65, 0x2d, + 0xe3, 0xfd, 0xb8, 0xbe, 0xfc, 0x84, 0x8a, 0xd9, 0x22, 0x22, 0x2e, 0x04, 0xa4, 0x03, 0x7c, 0x07, + 0x13, 0xeb, 0x57, 0xa8, 0x1a, 0x23, 0xf0, 0xc7, 0x34, 0x73, 0xfc, 0x64, 0x6c, 0xea, 0x30, 0x6b, + 0x4b, 0xcb, 0xc8, 0x86, 0x2f, 0x83, 0x85, 0xdd, 0xfa, 0x9d, 0x4b, 0x7f, 0xa2, 0xc0, 0x87, 0xe8, + 0x79, 0x68, 0x33, 0x03, 0xed, 0x5b, 0xdd, 0x3a, 0x06, 0x2b, 0x3c, 0xf5, 0xb3, 0xa2, 0x78, 0xa6, + 0x6d, 0x2a, 0x13, 0xf8, 0x3f, 0x44, 0xf8, 0x2d, 0xdf, 0x31, 0x0e, 0xe0, 0x74, 0xab, 0x6a, 0x36, + 0x45, 0x97, 0xe8, 0x99, 0xa0, 0x25, 0x5d, 0xc1, 0x64, 0xf3, 0x1c, 0xc5, 0x08, 0x46, 0x85, 0x1d, + 0xf9, 0xab, 0x48, 0x19, 0x5d, 0xed, 0x7e, 0xa1, 0xb1, 0xd5, 0x10, 0xbd, 0x7e, 0xe7, 0x4d, 0x73, + 0xfa, 0xf3, 0x6b, 0xc3, 0x1e, 0xcf, 0xa2, 0x68, 0x35, 0x90, 0x46, 0xf4, 0xeb, 0x87, 0x9f, 0x92, + 0x40, 0x09, 0x43, 0x8b, 0x48, 0x1c, 0x6c, 0xd7, 0x88, 0x9a, 0x00, 0x2e, 0xd5, 0xee, 0x38, 0x2b, + 0xc9, 0x19, 0x0d, 0xa6, 0xfc, 0x02, 0x6e, 0x47, 0x95, 0x58, 0xe4, 0x47, 0x56, 0x77, 0xe9, 0xaa, + 0x9e, 0x30, 0x50, 0xe2, 0x76, 0x56, 0x94, 0xdf, 0xc8, 0x1f, 0x56, 0xe8, 0x80, 0xb9, 0x6e, 0x71, + 0x60, 0xc9, 0x80, 0xdd, 0x98, 0xed, 0xd3, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_ffdhe2048_p[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, + 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, + 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, + 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, + 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, + 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, + 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, + 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, + 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, + 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, + 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, + 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, + 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, + 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, + 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x28, 0x5c, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_ffdhe3072_p[384] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, + 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, + 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, + 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, + 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, + 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, + 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, + 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, + 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, + 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, + 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, + 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, + 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, + 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, + 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, + 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, + 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, + 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, + 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, + 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, + 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, + 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, + 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0xc6, 0x2e, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_ffdhe4096_p[512] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, + 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, + 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, + 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, + 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, + 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, + 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, + 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, + 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, + 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, + 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, + 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, + 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, + 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, + 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, + 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, + 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, + 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, + 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, + 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, + 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, + 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, + 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, + 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, + 0x77, 0x63, 0xcf, 0x1d, 0x55, 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, + 0x71, 0x35, 0xc8, 0x86, 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, + 0xa9, 0x07, 0x60, 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, + 0x09, 0x29, 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, + 0x2a, 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, + 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, 0xe1, + 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x65, 0x5f, 0x6a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_ffdhe6144_p[768] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, + 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, + 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, + 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, + 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, + 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, + 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, + 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, + 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, + 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, + 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, + 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, + 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, + 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, + 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, + 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, + 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, + 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, + 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, + 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, + 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, + 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, + 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, + 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, + 0x77, 0x63, 0xcf, 0x1d, 0x55, 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, + 0x71, 0x35, 0xc8, 0x86, 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, + 0xa9, 0x07, 0x60, 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, + 0x09, 0x29, 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, + 0x2a, 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, + 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, 0xe1, + 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x0d, 0xd9, 0x02, 0x0b, 0xfd, 0x64, 0xb6, 0x45, 0x03, 0x6c, 0x7a, + 0x4e, 0x67, 0x7d, 0x2c, 0x38, 0x53, 0x2a, 0x3a, 0x23, 0xba, 0x44, 0x42, 0xca, 0xf5, 0x3e, 0xa6, + 0x3b, 0xb4, 0x54, 0x32, 0x9b, 0x76, 0x24, 0xc8, 0x91, 0x7b, 0xdd, 0x64, 0xb1, 0xc0, 0xfd, 0x4c, + 0xb3, 0x8e, 0x8c, 0x33, 0x4c, 0x70, 0x1c, 0x3a, 0xcd, 0xad, 0x06, 0x57, 0xfc, 0xcf, 0xec, 0x71, + 0x9b, 0x1f, 0x5c, 0x3e, 0x4e, 0x46, 0x04, 0x1f, 0x38, 0x81, 0x47, 0xfb, 0x4c, 0xfd, 0xb4, 0x77, + 0xa5, 0x24, 0x71, 0xf7, 0xa9, 0xa9, 0x69, 0x10, 0xb8, 0x55, 0x32, 0x2e, 0xdb, 0x63, 0x40, 0xd8, + 0xa0, 0x0e, 0xf0, 0x92, 0x35, 0x05, 0x11, 0xe3, 0x0a, 0xbe, 0xc1, 0xff, 0xf9, 0xe3, 0xa2, 0x6e, + 0x7f, 0xb2, 0x9f, 0x8c, 0x18, 0x30, 0x23, 0xc3, 0x58, 0x7e, 0x38, 0xda, 0x00, 0x77, 0xd9, 0xb4, + 0x76, 0x3e, 0x4e, 0x4b, 0x94, 0xb2, 0xbb, 0xc1, 0x94, 0xc6, 0x65, 0x1e, 0x77, 0xca, 0xf9, 0x92, + 0xee, 0xaa, 0xc0, 0x23, 0x2a, 0x28, 0x1b, 0xf6, 0xb3, 0xa7, 0x39, 0xc1, 0x22, 0x61, 0x16, 0x82, + 0x0a, 0xe8, 0xdb, 0x58, 0x47, 0xa6, 0x7c, 0xbe, 0xf9, 0xc9, 0x09, 0x1b, 0x46, 0x2d, 0x53, 0x8c, + 0xd7, 0x2b, 0x03, 0x74, 0x6a, 0xe7, 0x7f, 0x5e, 0x62, 0x29, 0x2c, 0x31, 0x15, 0x62, 0xa8, 0x46, + 0x50, 0x5d, 0xc8, 0x2d, 0xb8, 0x54, 0x33, 0x8a, 0xe4, 0x9f, 0x52, 0x35, 0xc9, 0x5b, 0x91, 0x17, + 0x8c, 0xcf, 0x2d, 0xd5, 0xca, 0xce, 0xf4, 0x03, 0xec, 0x9d, 0x18, 0x10, 0xc6, 0x27, 0x2b, 0x04, + 0x5b, 0x3b, 0x71, 0xf9, 0xdc, 0x6b, 0x80, 0xd6, 0x3f, 0xdd, 0x4a, 0x8e, 0x9a, 0xdb, 0x1e, 0x69, + 0x62, 0xa6, 0x95, 0x26, 0xd4, 0x31, 0x61, 0xc1, 0xa4, 0x1d, 0x57, 0x0d, 0x79, 0x38, 0xda, 0xd4, + 0xa4, 0x0e, 0x32, 0x9c, 0xd0, 0xe4, 0x0e, 0x65, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_ffdhe8192_p[1024] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, 0x9a, + 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, 0x36, 0x95, + 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, 0x9b, 0x3e, 0xf9, + 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, 0xae, 0xc4, 0x61, 0x7a, + 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, 0x1f, 0x5f, 0x06, 0x6e, 0xd0, + 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, + 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, + 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, + 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, + 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, + 0xae, 0x56, 0xed, 0xe7, 0x63, 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, + 0x9e, 0x02, 0xfc, 0xe1, 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, + 0x91, 0x72, 0xfe, 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, + 0xc3, 0xfe, 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, + 0xc5, 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, 0x5b, + 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, 0xc9, 0x07, + 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, 0x86, 0xd9, 0x1c, + 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, 0xbc, 0x43, 0x79, 0x44, + 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, 0x1e, 0x71, 0xf5, 0x4b, 0xff, + 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, + 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, + 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, + 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, + 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, + 0x77, 0x63, 0xcf, 0x1d, 0x55, 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, + 0x71, 0x35, 0xc8, 0x86, 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, + 0xa9, 0x07, 0x60, 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, + 0x09, 0x29, 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, + 0x2a, 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, + 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, 0xe1, + 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x0d, 0xd9, 0x02, 0x0b, 0xfd, 0x64, 0xb6, 0x45, 0x03, 0x6c, 0x7a, + 0x4e, 0x67, 0x7d, 0x2c, 0x38, 0x53, 0x2a, 0x3a, 0x23, 0xba, 0x44, 0x42, 0xca, 0xf5, 0x3e, 0xa6, + 0x3b, 0xb4, 0x54, 0x32, 0x9b, 0x76, 0x24, 0xc8, 0x91, 0x7b, 0xdd, 0x64, 0xb1, 0xc0, 0xfd, 0x4c, + 0xb3, 0x8e, 0x8c, 0x33, 0x4c, 0x70, 0x1c, 0x3a, 0xcd, 0xad, 0x06, 0x57, 0xfc, 0xcf, 0xec, 0x71, + 0x9b, 0x1f, 0x5c, 0x3e, 0x4e, 0x46, 0x04, 0x1f, 0x38, 0x81, 0x47, 0xfb, 0x4c, 0xfd, 0xb4, 0x77, + 0xa5, 0x24, 0x71, 0xf7, 0xa9, 0xa9, 0x69, 0x10, 0xb8, 0x55, 0x32, 0x2e, 0xdb, 0x63, 0x40, 0xd8, + 0xa0, 0x0e, 0xf0, 0x92, 0x35, 0x05, 0x11, 0xe3, 0x0a, 0xbe, 0xc1, 0xff, 0xf9, 0xe3, 0xa2, 0x6e, + 0x7f, 0xb2, 0x9f, 0x8c, 0x18, 0x30, 0x23, 0xc3, 0x58, 0x7e, 0x38, 0xda, 0x00, 0x77, 0xd9, 0xb4, + 0x76, 0x3e, 0x4e, 0x4b, 0x94, 0xb2, 0xbb, 0xc1, 0x94, 0xc6, 0x65, 0x1e, 0x77, 0xca, 0xf9, 0x92, + 0xee, 0xaa, 0xc0, 0x23, 0x2a, 0x28, 0x1b, 0xf6, 0xb3, 0xa7, 0x39, 0xc1, 0x22, 0x61, 0x16, 0x82, + 0x0a, 0xe8, 0xdb, 0x58, 0x47, 0xa6, 0x7c, 0xbe, 0xf9, 0xc9, 0x09, 0x1b, 0x46, 0x2d, 0x53, 0x8c, + 0xd7, 0x2b, 0x03, 0x74, 0x6a, 0xe7, 0x7f, 0x5e, 0x62, 0x29, 0x2c, 0x31, 0x15, 0x62, 0xa8, 0x46, + 0x50, 0x5d, 0xc8, 0x2d, 0xb8, 0x54, 0x33, 0x8a, 0xe4, 0x9f, 0x52, 0x35, 0xc9, 0x5b, 0x91, 0x17, + 0x8c, 0xcf, 0x2d, 0xd5, 0xca, 0xce, 0xf4, 0x03, 0xec, 0x9d, 0x18, 0x10, 0xc6, 0x27, 0x2b, 0x04, + 0x5b, 0x3b, 0x71, 0xf9, 0xdc, 0x6b, 0x80, 0xd6, 0x3f, 0xdd, 0x4a, 0x8e, 0x9a, 0xdb, 0x1e, 0x69, + 0x62, 0xa6, 0x95, 0x26, 0xd4, 0x31, 0x61, 0xc1, 0xa4, 0x1d, 0x57, 0x0d, 0x79, 0x38, 0xda, 0xd4, + 0xa4, 0x0e, 0x32, 0x9c, 0xcf, 0xf4, 0x6a, 0xaa, 0x36, 0xad, 0x00, 0x4c, 0xf6, 0x00, 0xc8, 0x38, + 0x1e, 0x42, 0x5a, 0x31, 0xd9, 0x51, 0xae, 0x64, 0xfd, 0xb2, 0x3f, 0xce, 0xc9, 0x50, 0x9d, 0x43, + 0x68, 0x7f, 0xeb, 0x69, 0xed, 0xd1, 0xcc, 0x5e, 0x0b, 0x8c, 0xc3, 0xbd, 0xf6, 0x4b, 0x10, 0xef, + 0x86, 0xb6, 0x31, 0x42, 0xa3, 0xab, 0x88, 0x29, 0x55, 0x5b, 0x2f, 0x74, 0x7c, 0x93, 0x26, 0x65, + 0xcb, 0x2c, 0x0f, 0x1c, 0xc0, 0x1b, 0xd7, 0x02, 0x29, 0x38, 0x88, 0x39, 0xd2, 0xaf, 0x05, 0xe4, + 0x54, 0x50, 0x4a, 0xc7, 0x8b, 0x75, 0x82, 0x82, 0x28, 0x46, 0xc0, 0xba, 0x35, 0xc3, 0x5f, 0x5c, + 0x59, 0x16, 0x0c, 0xc0, 0x46, 0xfd, 0x82, 0x51, 0x54, 0x1f, 0xc6, 0x8c, 0x9c, 0x86, 0xb0, 0x22, + 0xbb, 0x70, 0x99, 0x87, 0x6a, 0x46, 0x0e, 0x74, 0x51, 0xa8, 0xa9, 0x31, 0x09, 0x70, 0x3f, 0xee, + 0x1c, 0x21, 0x7e, 0x6c, 0x38, 0x26, 0xe5, 0x2c, 0x51, 0xaa, 0x69, 0x1e, 0x0e, 0x42, 0x3c, 0xfc, + 0x99, 0xe9, 0xe3, 0x16, 0x50, 0xc1, 0x21, 0x7b, 0x62, 0x48, 0x16, 0xcd, 0xad, 0x9a, 0x95, 0xf9, + 0xd5, 0xb8, 0x01, 0x94, 0x88, 0xd9, 0xc0, 0xa0, 0xa1, 0xfe, 0x30, 0x75, 0xa5, 0x77, 0xe2, 0x31, + 0x83, 0xf8, 0x1d, 0x4a, 0x3f, 0x2f, 0xa4, 0x57, 0x1e, 0xfc, 0x8c, 0xe0, 0xba, 0x8a, 0x4f, 0xe8, + 0xb6, 0x85, 0x5d, 0xfe, 0x72, 0xb0, 0xa6, 0x6e, 0xde, 0xd2, 0xfb, 0xab, 0xfb, 0xe5, 0x8a, 0x30, + 0xfa, 0xfa, 0xbe, 0x1c, 0x5d, 0x71, 0xa8, 0x7e, 0x2f, 0x74, 0x1e, 0xf8, 0xc1, 0xfe, 0x86, 0xfe, + 0xa6, 0xbb, 0xfd, 0xe5, 0x30, 0x67, 0x7f, 0x0d, 0x97, 0xd1, 0x1d, 0x49, 0xf7, 0xa8, 0x44, 0x3d, + 0x08, 0x22, 0xe5, 0x06, 0xa9, 0xf4, 0x61, 0x4e, 0x01, 0x1e, 0x2a, 0x94, 0x83, 0x8f, 0xf8, 0x8c, + 0xd6, 0x8c, 0x8b, 0xb7, 0xc5, 0xc6, 0x42, 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char scr_dh_dh_1024_160_p[128] = { + 0xb1, 0x0b, 0x8f, 0x96, 0xa0, 0x80, 0xe0, 0x1d, 0xde, 0x92, 0xde, 0x5e, 0xae, 0x5d, 0x54, 0xec, + 0x52, 0xc9, 0x9f, 0xbc, 0xfb, 0x06, 0xa3, 0xc6, 0x9a, 0x6a, 0x9d, 0xca, 0x52, 0xd2, 0x3b, 0x61, + 0x60, 0x73, 0xe2, 0x86, 0x75, 0xa2, 0x3d, 0x18, 0x98, 0x38, 0xef, 0x1e, 0x2e, 0xe6, 0x52, 0xc0, + 0x13, 0xec, 0xb4, 0xae, 0xa9, 0x06, 0x11, 0x23, 0x24, 0x97, 0x5c, 0x3c, 0xd4, 0x9b, 0x83, 0xbf, + 0xac, 0xcb, 0xdd, 0x7d, 0x90, 0xc4, 0xbd, 0x70, 0x98, 0x48, 0x8e, 0x9c, 0x21, 0x9a, 0x73, 0x72, + 0x4e, 0xff, 0xd6, 0xfa, 0xe5, 0x64, 0x47, 0x38, 0xfa, 0xa3, 0x1a, 0x4f, 0xf5, 0x5b, 0xcc, 0xc0, + 0xa1, 0x51, 0xaf, 0x5f, 0x0d, 0xc8, 0xb4, 0xbd, 0x45, 0xbf, 0x37, 0xdf, 0x36, 0x5c, 0x1a, 0x65, + 0xe6, 0x8c, 0xfd, 0xa7, 0x6d, 0x4d, 0xa7, 0x08, 0xdf, 0x1f, 0xb2, 0xbc, 0x2e, 0x4a, 0x43, 0x71, +}; + +static const unsigned char scr_dh_dh_1024_160_g[128] = { + 0xa4, 0xd1, 0xcb, 0xd5, 0xc3, 0xfd, 0x34, 0x12, 0x67, 0x65, 0xa4, 0x42, 0xef, 0xb9, 0x99, 0x05, + 0xf8, 0x10, 0x4d, 0xd2, 0x58, 0xac, 0x50, 0x7f, 0xd6, 0x40, 0x6c, 0xff, 0x14, 0x26, 0x6d, 0x31, + 0x26, 0x6f, 0xea, 0x1e, 0x5c, 0x41, 0x56, 0x4b, 0x77, 0x7e, 0x69, 0x0f, 0x55, 0x04, 0xf2, 0x13, + 0x16, 0x02, 0x17, 0xb4, 0xb0, 0x1b, 0x88, 0x6a, 0x5e, 0x91, 0x54, 0x7f, 0x9e, 0x27, 0x49, 0xf4, + 0xd7, 0xfb, 0xd7, 0xd3, 0xb9, 0xa9, 0x2e, 0xe1, 0x90, 0x9d, 0x0d, 0x22, 0x63, 0xf8, 0x0a, 0x76, + 0xa6, 0xa2, 0x4c, 0x08, 0x7a, 0x09, 0x1f, 0x53, 0x1d, 0xbf, 0x0a, 0x01, 0x69, 0xb6, 0xa2, 0x8a, + 0xd6, 0x62, 0xa4, 0xd1, 0x8e, 0x73, 0xaf, 0xa3, 0x2d, 0x77, 0x9d, 0x59, 0x18, 0xd0, 0x8b, 0xc8, + 0x85, 0x8f, 0x4d, 0xce, 0xf9, 0x7c, 0x2a, 0x24, 0x85, 0x5e, 0x6e, 0xeb, 0x22, 0xb3, 0xb2, 0xe5, +}; + +static const unsigned char scr_dh_dh_1024_160_q[20] = { + 0xf5, 0x18, 0xaa, 0x87, 0x81, 0xa8, 0xdf, 0x27, 0x8a, 0xba, 0x4e, 0x7d, 0x64, 0xb7, 0xcb, 0x9d, + 0x49, 0x46, 0x23, 0x53, +}; + +static const unsigned char scr_dh_dh_2048_224_p[256] = { + 0xad, 0x10, 0x7e, 0x1e, 0x91, 0x23, 0xa9, 0xd0, 0xd6, 0x60, 0xfa, 0xa7, 0x95, 0x59, 0xc5, 0x1f, + 0xa2, 0x0d, 0x64, 0xe5, 0x68, 0x3b, 0x9f, 0xd1, 0xb5, 0x4b, 0x15, 0x97, 0xb6, 0x1d, 0x0a, 0x75, + 0xe6, 0xfa, 0x14, 0x1d, 0xf9, 0x5a, 0x56, 0xdb, 0xaf, 0x9a, 0x3c, 0x40, 0x7b, 0xa1, 0xdf, 0x15, + 0xeb, 0x3d, 0x68, 0x8a, 0x30, 0x9c, 0x18, 0x0e, 0x1d, 0xe6, 0xb8, 0x5a, 0x12, 0x74, 0xa0, 0xa6, + 0x6d, 0x3f, 0x81, 0x52, 0xad, 0x6a, 0xc2, 0x12, 0x90, 0x37, 0xc9, 0xed, 0xef, 0xda, 0x4d, 0xf8, + 0xd9, 0x1e, 0x8f, 0xef, 0x55, 0xb7, 0x39, 0x4b, 0x7a, 0xd5, 0xb7, 0xd0, 0xb6, 0xc1, 0x22, 0x07, + 0xc9, 0xf9, 0x8d, 0x11, 0xed, 0x34, 0xdb, 0xf6, 0xc6, 0xba, 0x0b, 0x2c, 0x8b, 0xbc, 0x27, 0xbe, + 0x6a, 0x00, 0xe0, 0xa0, 0xb9, 0xc4, 0x97, 0x08, 0xb3, 0xbf, 0x8a, 0x31, 0x70, 0x91, 0x88, 0x36, + 0x81, 0x28, 0x61, 0x30, 0xbc, 0x89, 0x85, 0xdb, 0x16, 0x02, 0xe7, 0x14, 0x41, 0x5d, 0x93, 0x30, + 0x27, 0x82, 0x73, 0xc7, 0xde, 0x31, 0xef, 0xdc, 0x73, 0x10, 0xf7, 0x12, 0x1f, 0xd5, 0xa0, 0x74, + 0x15, 0x98, 0x7d, 0x9a, 0xdc, 0x0a, 0x48, 0x6d, 0xcd, 0xf9, 0x3a, 0xcc, 0x44, 0x32, 0x83, 0x87, + 0x31, 0x5d, 0x75, 0xe1, 0x98, 0xc6, 0x41, 0xa4, 0x80, 0xcd, 0x86, 0xa1, 0xb9, 0xe5, 0x87, 0xe8, + 0xbe, 0x60, 0xe6, 0x9c, 0xc9, 0x28, 0xb2, 0xb9, 0xc5, 0x21, 0x72, 0xe4, 0x13, 0x04, 0x2e, 0x9b, + 0x23, 0xf1, 0x0b, 0x0e, 0x16, 0xe7, 0x97, 0x63, 0xc9, 0xb5, 0x3d, 0xcf, 0x4b, 0xa8, 0x0a, 0x29, + 0xe3, 0xfb, 0x73, 0xc1, 0x6b, 0x8e, 0x75, 0xb9, 0x7e, 0xf3, 0x63, 0xe2, 0xff, 0xa3, 0x1f, 0x71, + 0xcf, 0x9d, 0xe5, 0x38, 0x4e, 0x71, 0xb8, 0x1c, 0x0a, 0xc4, 0xdf, 0xfe, 0x0c, 0x10, 0xe6, 0x4f, +}; + +static const unsigned char scr_dh_dh_2048_224_g[256] = { + 0xac, 0x40, 0x32, 0xef, 0x4f, 0x2d, 0x9a, 0xe3, 0x9d, 0xf3, 0x0b, 0x5c, 0x8f, 0xfd, 0xac, 0x50, + 0x6c, 0xde, 0xbe, 0x7b, 0x89, 0x99, 0x8c, 0xaf, 0x74, 0x86, 0x6a, 0x08, 0xcf, 0xe4, 0xff, 0xe3, + 0xa6, 0x82, 0x4a, 0x4e, 0x10, 0xb9, 0xa6, 0xf0, 0xdd, 0x92, 0x1f, 0x01, 0xa7, 0x0c, 0x4a, 0xfa, + 0xab, 0x73, 0x9d, 0x77, 0x00, 0xc2, 0x9f, 0x52, 0xc5, 0x7d, 0xb1, 0x7c, 0x62, 0x0a, 0x86, 0x52, + 0xbe, 0x5e, 0x90, 0x01, 0xa8, 0xd6, 0x6a, 0xd7, 0xc1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4a, + 0xf4, 0xd0, 0x27, 0x27, 0x5a, 0xc1, 0x34, 0x8b, 0xb8, 0xa7, 0x62, 0xd0, 0x52, 0x1b, 0xc9, 0x8a, + 0xe2, 0x47, 0x15, 0x04, 0x22, 0xea, 0x1e, 0xd4, 0x09, 0x93, 0x9d, 0x54, 0xda, 0x74, 0x60, 0xcd, + 0xb5, 0xf6, 0xc6, 0xb2, 0x50, 0x71, 0x7c, 0xbe, 0xf1, 0x80, 0xeb, 0x34, 0x11, 0x8e, 0x98, 0xd1, + 0x19, 0x52, 0x9a, 0x45, 0xd6, 0xf8, 0x34, 0x56, 0x6e, 0x30, 0x25, 0xe3, 0x16, 0xa3, 0x30, 0xef, + 0xbb, 0x77, 0xa8, 0x6f, 0x0c, 0x1a, 0xb1, 0x5b, 0x05, 0x1a, 0xe3, 0xd4, 0x28, 0xc8, 0xf8, 0xac, + 0xb7, 0x0a, 0x81, 0x37, 0x15, 0x0b, 0x8e, 0xeb, 0x10, 0xe1, 0x83, 0xed, 0xd1, 0x99, 0x63, 0xdd, + 0xd9, 0xe2, 0x63, 0xe4, 0x77, 0x05, 0x89, 0xef, 0x6a, 0xa2, 0x1e, 0x7f, 0x5f, 0x2f, 0xf3, 0x81, + 0xb5, 0x39, 0xcc, 0xe3, 0x40, 0x9d, 0x13, 0xcd, 0x56, 0x6a, 0xfb, 0xb4, 0x8d, 0x6c, 0x01, 0x91, + 0x81, 0xe1, 0xbc, 0xfe, 0x94, 0xb3, 0x02, 0x69, 0xed, 0xfe, 0x72, 0xfe, 0x9b, 0x6a, 0xa4, 0xbd, + 0x7b, 0x5a, 0x0f, 0x1c, 0x71, 0xcf, 0xff, 0x4c, 0x19, 0xc4, 0x18, 0xe1, 0xf6, 0xec, 0x01, 0x79, + 0x81, 0xbc, 0x08, 0x7f, 0x2a, 0x70, 0x65, 0xb3, 0x84, 0xb8, 0x90, 0xd3, 0x19, 0x1f, 0x2b, 0xfa, +}; + +static const unsigned char scr_dh_dh_2048_224_q[28] = { + 0x80, 0x1c, 0x0d, 0x34, 0xc5, 0x8d, 0x93, 0xfe, 0x99, 0x71, 0x77, 0x10, 0x1f, 0x80, 0x53, 0x5a, + 0x47, 0x38, 0xce, 0xbc, 0xbf, 0x38, 0x9a, 0x99, 0xb3, 0x63, 0x71, 0xeb, +}; + +static const unsigned char scr_dh_dh_2048_256_p[256] = { + 0x87, 0xa8, 0xe6, 0x1d, 0xb4, 0xb6, 0x66, 0x3c, 0xff, 0xbb, 0xd1, 0x9c, 0x65, 0x19, 0x59, 0x99, + 0x8c, 0xee, 0xf6, 0x08, 0x66, 0x0d, 0xd0, 0xf2, 0x5d, 0x2c, 0xee, 0xd4, 0x43, 0x5e, 0x3b, 0x00, + 0xe0, 0x0d, 0xf8, 0xf1, 0xd6, 0x19, 0x57, 0xd4, 0xfa, 0xf7, 0xdf, 0x45, 0x61, 0xb2, 0xaa, 0x30, + 0x16, 0xc3, 0xd9, 0x11, 0x34, 0x09, 0x6f, 0xaa, 0x3b, 0xf4, 0x29, 0x6d, 0x83, 0x0e, 0x9a, 0x7c, + 0x20, 0x9e, 0x0c, 0x64, 0x97, 0x51, 0x7a, 0xbd, 0x5a, 0x8a, 0x9d, 0x30, 0x6b, 0xcf, 0x67, 0xed, + 0x91, 0xf9, 0xe6, 0x72, 0x5b, 0x47, 0x58, 0xc0, 0x22, 0xe0, 0xb1, 0xef, 0x42, 0x75, 0xbf, 0x7b, + 0x6c, 0x5b, 0xfc, 0x11, 0xd4, 0x5f, 0x90, 0x88, 0xb9, 0x41, 0xf5, 0x4e, 0xb1, 0xe5, 0x9b, 0xb8, + 0xbc, 0x39, 0xa0, 0xbf, 0x12, 0x30, 0x7f, 0x5c, 0x4f, 0xdb, 0x70, 0xc5, 0x81, 0xb2, 0x3f, 0x76, + 0xb6, 0x3a, 0xca, 0xe1, 0xca, 0xa6, 0xb7, 0x90, 0x2d, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8a, 0x0e, + 0xf1, 0x3c, 0x6d, 0x9a, 0x51, 0xbf, 0xa4, 0xab, 0x3a, 0xd8, 0x34, 0x77, 0x96, 0x52, 0x4d, 0x8e, + 0xf6, 0xa1, 0x67, 0xb5, 0xa4, 0x18, 0x25, 0xd9, 0x67, 0xe1, 0x44, 0xe5, 0x14, 0x05, 0x64, 0x25, + 0x1c, 0xca, 0xcb, 0x83, 0xe6, 0xb4, 0x86, 0xf6, 0xb3, 0xca, 0x3f, 0x79, 0x71, 0x50, 0x60, 0x26, + 0xc0, 0xb8, 0x57, 0xf6, 0x89, 0x96, 0x28, 0x56, 0xde, 0xd4, 0x01, 0x0a, 0xbd, 0x0b, 0xe6, 0x21, + 0xc3, 0xa3, 0x96, 0x0a, 0x54, 0xe7, 0x10, 0xc3, 0x75, 0xf2, 0x63, 0x75, 0xd7, 0x01, 0x41, 0x03, + 0xa4, 0xb5, 0x43, 0x30, 0xc1, 0x98, 0xaf, 0x12, 0x61, 0x16, 0xd2, 0x27, 0x6e, 0x11, 0x71, 0x5f, + 0x69, 0x38, 0x77, 0xfa, 0xd7, 0xef, 0x09, 0xca, 0xdb, 0x09, 0x4a, 0xe9, 0x1e, 0x1a, 0x15, 0x97, +}; + +static const unsigned char scr_dh_dh_2048_256_g[256] = { + 0x3f, 0xb3, 0x2c, 0x9b, 0x73, 0x13, 0x4d, 0x0b, 0x2e, 0x77, 0x50, 0x66, 0x60, 0xed, 0xbd, 0x48, + 0x4c, 0xa7, 0xb1, 0x8f, 0x21, 0xef, 0x20, 0x54, 0x07, 0xf4, 0x79, 0x3a, 0x1a, 0x0b, 0xa1, 0x25, + 0x10, 0xdb, 0xc1, 0x50, 0x77, 0xbe, 0x46, 0x3f, 0xff, 0x4f, 0xed, 0x4a, 0xac, 0x0b, 0xb5, 0x55, + 0xbe, 0x3a, 0x6c, 0x1b, 0x0c, 0x6b, 0x47, 0xb1, 0xbc, 0x37, 0x73, 0xbf, 0x7e, 0x8c, 0x6f, 0x62, + 0x90, 0x12, 0x28, 0xf8, 0xc2, 0x8c, 0xbb, 0x18, 0xa5, 0x5a, 0xe3, 0x13, 0x41, 0x00, 0x0a, 0x65, + 0x01, 0x96, 0xf9, 0x31, 0xc7, 0x7a, 0x57, 0xf2, 0xdd, 0xf4, 0x63, 0xe5, 0xe9, 0xec, 0x14, 0x4b, + 0x77, 0x7d, 0xe6, 0x2a, 0xaa, 0xb8, 0xa8, 0x62, 0x8a, 0xc3, 0x76, 0xd2, 0x82, 0xd6, 0xed, 0x38, + 0x64, 0xe6, 0x79, 0x82, 0x42, 0x8e, 0xbc, 0x83, 0x1d, 0x14, 0x34, 0x8f, 0x6f, 0x2f, 0x91, 0x93, + 0xb5, 0x04, 0x5a, 0xf2, 0x76, 0x71, 0x64, 0xe1, 0xdf, 0xc9, 0x67, 0xc1, 0xfb, 0x3f, 0x2e, 0x55, + 0xa4, 0xbd, 0x1b, 0xff, 0xe8, 0x3b, 0x9c, 0x80, 0xd0, 0x52, 0xb9, 0x85, 0xd1, 0x82, 0xea, 0x0a, + 0xdb, 0x2a, 0x3b, 0x73, 0x13, 0xd3, 0xfe, 0x14, 0xc8, 0x48, 0x4b, 0x1e, 0x05, 0x25, 0x88, 0xb9, + 0xb7, 0xd2, 0xbb, 0xd2, 0xdf, 0x01, 0x61, 0x99, 0xec, 0xd0, 0x6e, 0x15, 0x57, 0xcd, 0x09, 0x15, + 0xb3, 0x35, 0x3b, 0xbb, 0x64, 0xe0, 0xec, 0x37, 0x7f, 0xd0, 0x28, 0x37, 0x0d, 0xf9, 0x2b, 0x52, + 0xc7, 0x89, 0x14, 0x28, 0xcd, 0xc6, 0x7e, 0xb6, 0x18, 0x4b, 0x52, 0x3d, 0x1d, 0xb2, 0x46, 0xc3, + 0x2f, 0x63, 0x07, 0x84, 0x90, 0xf0, 0x0e, 0xf8, 0xd6, 0x47, 0xd1, 0x48, 0xd4, 0x79, 0x54, 0x51, + 0x5e, 0x23, 0x27, 0xcf, 0xef, 0x98, 0xc5, 0x82, 0x66, 0x4b, 0x4c, 0x0f, 0x6c, 0xc4, 0x16, 0x59, +}; + +static const unsigned char scr_dh_dh_2048_256_q[32] = { + 0x8c, 0xf8, 0x36, 0x42, 0xa7, 0x09, 0xa0, 0x97, 0xb4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9d, 0xa2, + 0x99, 0xb1, 0xa4, 0x7d, 0x1e, 0xb3, 0x75, 0x0b, 0xa3, 0x08, 0xb0, 0xfe, 0x64, 0xf5, 0xfb, 0xd3, +}; + +#endif diff --git a/packages/runtime/src/scr_dyn.h b/packages/runtime/src/scr_dyn.h index 37e9eacce..4b618a809 100644 --- a/packages/runtime/src/scr_dyn.h +++ b/packages/runtime/src/scr_dyn.h @@ -155,6 +155,8 @@ typedef enum { X(DATE, 30) /* JavaScript Date with stable native identity */ \ X(DECIPHER, 46) /* node:crypto Decipheriv (scr_cipher.c) */ \ X(DGRAM_SOCKET, 62) /* dgram.Socket (scr_dgram.c) */ \ + X(DIFFIE_HELLMAN, 65) /* node:crypto DiffieHellman (scr_dh.c) */ \ + X(DIFFIE_HELLMAN_GROUP, 66) /* node:crypto DiffieHellmanGroup (scr_dh.c) */ \ X(DIR, 44) /* fs.Dir (scr_lib.c) */ \ X(DISPOSABLE_STACK, 49) /* DisposableStack (scr_symbol.c) */ \ X(ECDH, 64) /* node:crypto ECDH (scr_ecdh.c) */ \ diff --git a/packages/runtime/src/scr_ecdh.c b/packages/runtime/src/scr_ecdh.c index fc8797571..5edbadba6 100644 --- a/packages/runtime/src/scr_ecdh.c +++ b/packages/runtime/src/scr_ecdh.c @@ -21,6 +21,7 @@ #include "mbedtls/ecp.h" #include "mbedtls/platform_util.h" +#include "scr_crypto_args.h" #include "scr_ecdh_names.h" typedef struct { @@ -73,6 +74,9 @@ static int se_random(void *state, unsigned char *out, size_t len) { return 0; } +/* The value the SC2020 error for an unlowered encoding or format names. */ +#define SE_SUBJECT "An ECDH encoding or format" + /* ── errors ───────────────────────────────────────────────────────────── */ static void se_throw(int kind, const char *code, const char *message) { @@ -83,51 +87,6 @@ static void se_operation_failed(const char *message) { se_throw(SCR_ERR_ERROR, "ERR_CRYPTO_OPERATION_FAILED", message); } -/* An object's toString the program defined, as an own or inherited - * function property. */ -static bool se_custom_to_string(ScrDyn *value) { - if (value->kind != SCR_DYN_OBJ && value->kind != SCR_DYN_TYPED_REF) return false; - ScrDyn *member = scr_dyn_obj_read(value, "toString", 8); - bool custom = member && member->kind == SCR_DYN_FUNC; - scr_dyn_release(member); - return custom; -} - -/* Whether value is a class instance whose class defines toString. */ -static bool se_class_to_string(const ScrDyn *value) { - ScrStr *name = scr_str_new("toString", 8); - bool method = scr_dyn_typed_ref_class_has(value, name); - scr_str_release(name); - return method; -} - -/* `prefix` and the value as util.format's %s renders it: strings and - * objects with a program-defined toString as String(value), numbers as - * util.inspect does, other values inspected at depth 0. Functions and - * class instances that define toString have no lowering. */ -static void se_throw_formatted(const char *code, const char *prefix, const ScrDyn *value) { - ScrDyn *v = (ScrDyn *)value; - if (v->kind == SCR_DYN_FUNC || se_class_to_string(v)) { - static const char message[] = "An ECDH encoding or format that is a function or an instance of a class " - "defining toString has no lowering; pass the name as a string"; - scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020"); - return; - } - ScrStr *text; - if (v->kind == SCR_DYN_STR || se_custom_to_string(v)) text = scr_dyn_string_coerce_js(v); - else if (v->kind == SCR_DYN_NUM) text = scr_insp_f64(v->v.num); - else text = scr_insp_dyn_s(v, 0); - if (!text) return; - ScrJsonBuf b; - scr_jb_init(&b); - scr_jb_puts(&b, prefix); - scr_jb_put_str(&b, text); - scr_str_release(text); - ScrStr *message = scr_jb_finish(&b); - scr_throw_error_msg_code(SCR_ERR_TYPE, message->data, message->len, code); - scr_str_release(message); -} - /* ── names and encodings ──────────────────────────────────────────────── */ static int se_name_cmp(const void *key, const void *entry) { @@ -198,129 +157,16 @@ static bool se_load_group(const ScrStr *name, mbedtls_ecp_group *grp, const char return false; } -static bool se_ascii_eq_ci(const char *a, size_t a_len, const char *b) { - size_t b_len = strlen(b); - if (a_len != b_len) return false; - for (size_t i = 0; i < a_len; i++) { - char x = a[i]; - if (x >= 'A' && x <= 'Z') x = (char)(x - 'A' + 'a'); - if (x != b[i]) return false; - } - return true; -} - -static bool se_str_is(const ScrStr *text, const char *literal) { - size_t len = strlen(literal); - return text->len == len && memcmp(text->data, literal, len) == 0; -} - -/* Node's normalizeEncoding: the canonical name, or NULL. */ -static const char *se_normalize_encoding(const ScrStr *name) { - static const struct { const char *from; const char *to; } map[] = { - {"utf8", "utf8"}, {"utf-8", "utf8"}, {"hex", "hex"}, - {"base64", "base64"}, {"base64url", "base64url"}, {"latin1", "latin1"}, - {"binary", "latin1"}, {"ascii", "ascii"}, {"utf16le", "utf16le"}, - {"utf-16le", "utf16le"}, {"ucs2", "utf16le"}, {"ucs-2", "utf16le"}, - }; - for (size_t i = 0; i < sizeof map / sizeof map[0]; i++) - if (se_ascii_eq_ci(name->data, name->len, map[i].from)) return map[i].to; - return NULL; -} - -/* The value a typed-ref box holds when it is a string, view, ArrayBuffer - * or native handle (+1). Other values are retained as passed, so argument - * errors describe the original value. NULL after throwing. */ -static ScrDyn *se_unbox(ScrDyn *value) { - if (value->kind != SCR_DYN_TYPED_REF) return scr_dyn_retain(value); - ScrDyn *view = scr_dyn_typed_ref_materialize(value); - if (!view) return NULL; - if (view->kind == SCR_DYN_STR || view->kind == SCR_DYN_BYTES || view->kind == SCR_DYN_HANDLE || - scr_buffer_storage_is(view)) - return view; - scr_dyn_release(view); - return scr_dyn_retain(value); -} - -/* getArrayBufferOrView(key, 'key', encoding): a string is Buffer.from(key, - * encoding) with 'buffer' read as utf8; views and ArrayBuffers are their - * bytes. +1 or NULL after throwing. */ -static ScrBytes *se_key_bytes(const ScrDyn *key, const ScrDyn *encoding) { - if (key->kind == SCR_DYN_STR) { - /* Buffer.from(string, encoding): an encoding that is not a non-empty - * string reads as utf8. */ - const char *name = "utf8"; - if (encoding->kind == SCR_DYN_STR && encoding->v.str->len > 0 && !se_str_is(encoding->v.str, "buffer")) { - name = se_normalize_encoding(encoding->v.str); - if (!name) { - se_throw_formatted("ERR_UNKNOWN_ENCODING", "Unknown encoding: ", encoding); - return NULL; - } - } - ScrStr *canonical = scr_str_new(name, strlen(name)); - ScrBytes *bytes = scr_bytes_from_str(key->v.str, canonical); - scr_str_release(canonical); - return bytes; - } - if (key->kind == SCR_DYN_BYTES || scr_buffer_storage_is(key)) return scr_bytes_buffer_source(key); - scr_dyn_arg_type_fail("key", "of type string or an instance of ArrayBuffer, Buffer, TypedArray, or DataView", key); - return NULL; -} - -/* encode(buffer, encoding): the Buffer, or buffer.toString(encoding) for - * a truthy encoding other than 'buffer'. Consumes bytes. */ -static ScrDyn *se_encode(ScrBytes *bytes, const ScrDyn *encoding) { - if (!scr_dyn_truthy(encoding) || (encoding->kind == SCR_DYN_STR && se_str_is(encoding->v.str, "buffer"))) { - ScrDyn *result = scr_dyn_new_buffer(bytes); - scr_bytes_release(bytes); - return result; - } - /* toString answers "" for no bytes before it reads the encoding. */ - ScrStr *text = NULL; - if (bytes->len == 0) { - text = scr_str_new("", 0); - } else { - /* getEncodingOps reads `encoding + ''`. */ - ScrStr *name = encoding->kind == SCR_DYN_STR ? scr_str_retain(encoding->v.str) : NULL; - if (!name) { - if (encoding->kind == SCR_DYN_FUNC || se_class_to_string(encoding)) { - se_throw_formatted("ERR_UNKNOWN_ENCODING", "Unknown encoding: ", encoding); - scr_bytes_release(bytes); - return NULL; - } - name = scr_dyn_string_coerce_js(encoding); - if (!name) { - scr_bytes_release(bytes); - return NULL; - } - } - const char *canonical = se_normalize_encoding(name); - scr_str_release(name); - if (!canonical) { - se_throw_formatted("ERR_UNKNOWN_ENCODING", "Unknown encoding: ", encoding); - scr_bytes_release(bytes); - return NULL; - } - ScrStr *canonical_text = scr_str_new(canonical, strlen(canonical)); - text = scr_bytes_to_str(bytes, canonical_text); - scr_str_release(canonical_text); - } - scr_bytes_release(bytes); - if (!text) return NULL; - ScrDyn *result = scr_dyn_new_str(text); - scr_str_release(text); - return result; -} - /* getFormat: a falsy format is uncompressed; other values must be * 'compressed', 'hybrid' or 'uncompressed'. 0 after throwing. */ static int se_format(const ScrDyn *format) { if (!scr_dyn_truthy(format)) return SE_UNCOMPRESSED; if (format->kind == SCR_DYN_STR) { - if (se_str_is(format->v.str, "compressed")) return SE_COMPRESSED; - if (se_str_is(format->v.str, "hybrid")) return SE_HYBRID; - if (se_str_is(format->v.str, "uncompressed")) return SE_UNCOMPRESSED; + if (ca_str_is(format->v.str, "compressed")) return SE_COMPRESSED; + if (ca_str_is(format->v.str, "hybrid")) return SE_HYBRID; + if (ca_str_is(format->v.str, "uncompressed")) return SE_UNCOMPRESSED; } - se_throw_formatted("ERR_CRYPTO_ECDH_INVALID_FORMAT", "Invalid ECDH format: ", format); + ca_throw_formatted(SE_SUBJECT, "ERR_CRYPTO_ECDH_INVALID_FORMAT", "Invalid ECDH format: ", format); return 0; } @@ -516,7 +362,7 @@ static ScrDyn *se_new(const ScrDyn *curve) { } ScrDyn *scr_crypto_ecdh_new(ScrDyn *curve) { - ScrDyn *unboxed = se_unbox(curve); + ScrDyn *unboxed = ca_unbox(curve); if (!unboxed) return NULL; ScrDyn *result = se_new(unboxed); scr_dyn_release(unboxed); @@ -530,7 +376,7 @@ static ScrDyn *se_convert_key(const ScrDyn *key, const ScrDyn *curve, const ScrD scr_dyn_arg_type_fail("curve", "of type string", curve); return NULL; } - ScrBytes *bytes = se_key_bytes(key, in_enc); + ScrBytes *bytes = ca_key_bytes(SE_SUBJECT, key, in_enc); if (!bytes) return NULL; int form = se_format(format); if (!form) { @@ -557,7 +403,7 @@ static ScrDyn *se_convert_key(const ScrDyn *key, const ScrDyn *curve, const ScrD } else { ScrBytes *out = se_write_point(&grp, &pt, form); if (!out) se_operation_failed("Failed to convert EC_POINT to Buffer"); - else result = se_encode(out, out_enc); + else result = ca_encode(SE_SUBJECT, out, out_enc); } } mbedtls_ecp_point_free(&pt); @@ -571,7 +417,7 @@ ScrDyn *scr_crypto_ecdh_convert_key(ScrDyn *key, ScrDyn *curve, ScrDyn *in_enc, ScrDyn *unboxed[5] = {NULL, NULL, NULL, NULL, NULL}; ScrDyn *result = NULL; for (size_t i = 0; i < 5; i++) - if (!(unboxed[i] = se_unbox(args[i]))) goto done; + if (!(unboxed[i] = ca_unbox(args[i]))) goto done; result = se_convert_key(unboxed[0], unboxed[1], unboxed[2], unboxed[3], unboxed[4]); done: for (size_t i = 0; i < 5; i++) scr_dyn_release(unboxed[i]); @@ -597,7 +443,7 @@ static ScrDyn *se_get_public_key(ScEcdh *e, const ScrDyn *encoding, const ScrDyn se_operation_failed("Failed to get ECDH public key"); return NULL; } - return se_encode(out, encoding); + return ca_encode(SE_SUBJECT, out, encoding); } static ScrDyn *se_generate_keys(ScEcdh *e, const ScrDyn *encoding, const ScrDyn *format) { @@ -628,12 +474,12 @@ static ScrDyn *se_get_private_key(ScEcdh *e, const ScrDyn *encoding) { se_operation_failed("Failed to get ECDH private key"); return NULL; } - return se_encode(out, encoding); + return ca_encode(SE_SUBJECT, out, encoding); } /* setPrivateKey(key, encoding): 1 <= d < n, and the public key d·G. */ static ScrDyn *se_set_private_key(ScEcdh *e, ScrDyn *self, const ScrDyn *key, const ScrDyn *encoding) { - ScrBytes *bytes = se_key_bytes(key, encoding); + ScrBytes *bytes = ca_key_bytes(SE_SUBJECT, key, encoding); if (!bytes) return NULL; mbedtls_mpi d; mbedtls_ecp_point q; @@ -680,7 +526,7 @@ static void se_dep0031(void) { static ScrDyn *se_set_public_key(ScEcdh *e, ScrDyn *self, const ScrDyn *key, const ScrDyn *encoding) { se_dep0031(); - ScrBytes *bytes = se_key_bytes(key, encoding); + ScrBytes *bytes = ca_key_bytes(SE_SUBJECT, key, encoding); if (!bytes) return NULL; mbedtls_ecp_point q; mbedtls_ecp_point_init(&q); @@ -716,7 +562,7 @@ static bool se_pair_valid(ScEcdh *e) { /* computeSecret(key, inputEncoding, outputEncoding): the x coordinate of * d·P, padded to the field size as ECDH_compute_key writes it. */ static ScrDyn *se_compute_secret(ScEcdh *e, const ScrDyn *key, const ScrDyn *in_enc, const ScrDyn *out_enc) { - ScrBytes *bytes = se_key_bytes(key, in_enc); + ScrBytes *bytes = ca_key_bytes(SE_SUBJECT, key, in_enc); if (!bytes) return NULL; ScrDyn *result = NULL; mbedtls_ecp_point pub; @@ -737,7 +583,7 @@ static ScrDyn *se_compute_secret(ScEcdh *e, const ScrDyn *key, const ScrDyn *in_ scr_bytes_release(out); se_operation_failed("Failed to compute ECDH key"); } else { - result = se_encode(out, out_enc); + result = ca_encode(SE_SUBJECT, out, out_enc); } } mbedtls_mpi_free(&z); @@ -776,7 +622,7 @@ static ScrDyn *se_invoke(void *h, ScrDyn *self, const char *method, ScrDyn *cons size_t count = argc < 3 ? argc : 3; ScrDyn *result = NULL; for (size_t i = 0; i < count; i++) - if (!(unboxed[i] = se_unbox(args[i]))) goto done; + if (!(unboxed[i] = ca_unbox(args[i]))) goto done; result = se_dispatch(h, self, method, unboxed, count, what); done: for (size_t i = 0; i < count; i++) scr_dyn_release(unboxed[i]); diff --git a/packages/runtime/test/dh.test.ts b/packages/runtime/test/dh.test.ts new file mode 100644 index 000000000..d402d0465 --- /dev/null +++ b/packages/runtime/test/dh.test.ts @@ -0,0 +1,85 @@ +import { execFile } from "node:child_process"; +import { mkdir } from "node:fs/promises"; +import { join } from "node:path"; +import { promisify } from "node:util"; +import { beforeAll, expect, test } from "vitest"; + +const execFileAsync = promisify(execFile); +const testDir = import.meta.dirname; +const bin = join(testDir, "build", "test_dh"); +const mbedtls = join(testDir, "../vendor/mbedtls"); + +const runtimeSources = [ + "scr_shared.c", + "scr_bytes.c", + "scr_bytes.test.c", + "scr_bytes_io.c", + "scr_fs_cp.c", + "scr_zlib.c", + "scr_lib.c", + "scr_hash.c", + "scr_string.c", + "scr_array.c", + "scr_map.c", + "scr_exception.c", + "scr_error.c", + "scr_number.c", + "scr_console.c", + "scr_closure.c", + "scr_ffi.c", + "scr_object.c", + "scr_union.c", + "scr_cycle.c", + "scr_alloc.c", + "scr_json.c", + "scr_inspect.c", + "scr_bigint.c", + "scr_async.c", + "scr_async_dyn.c", + "scr_child.c", + "scr_path.c", + "scr_url.c", + "scr_url_params.c", +]; + +// The bignum modules scr_dh.c uses. +const mbedtlsSources = ["bignum.c", "bignum_core.c", "constant_time.c", "platform_util.c"]; + +// Compiled once with ASan, UBSan and the RC audit. test_dh.c includes +// scr_dh.c and checks the primality tests' Montgomery exponentiation +// against mbedTLS's, the primality test on primes, Carmichael numbers and +// strong pseudoprimes, exponents past mbedTLS's 8192-bit limit, DH_check +// results, and generated safe primes. +beforeAll(async () => { + await mkdir(join(testDir, "build"), { recursive: true }); + await execFileAsync("clang", [ + "-std=c11", + "-O1", + "-Wall", + "-Wextra", + "-Wno-missing-field-initializers", + "-fsanitize=address,undefined", + "-DSCR_RC_AUDIT", + "-I", + join(testDir, "../src"), + "-I", + join(mbedtls, "include"), + "-I", + join(mbedtls, "library"), + "-o", + bin, + join(testDir, "test_dh.c"), + ...runtimeSources.map((source) => join(testDir, "../src", source)), + ...mbedtlsSources.map((source) => join(mbedtls, "library", source)), + "-Wno-deprecated-declarations", + "-lz", + ...(process.platform === "linux" ? ["-D_GNU_SOURCE", "-lm"] : []), + ]); +}, 300_000); + +test("DiffieHellman runtime: exponentiation, primality and safe primes", async () => { + const { stderr } = await execFileAsync(bin, [], { + env: { ...process.env, UBSAN_OPTIONS: "halt_on_error=1" }, + }); + expect(stderr.trim().split("\n").pop()).toMatch(/^(\d+)\/\1 cases passed$/); +}, 300_000); diff --git a/packages/runtime/test/test_dh.c b/packages/runtime/test/test_dh.c new file mode 100644 index 000000000..aa8f175a3 --- /dev/null +++ b/packages/runtime/test/test_dh.c @@ -0,0 +1,228 @@ +/* Unit tests for the DiffieHellman runtime's arithmetic (scr_dh.c), built + * with ASan and UBSan by dh.test.ts. The unit is included directly so the + * tests can compare the Montgomery exponentiation the primality tests use + * with mbedTLS's, run the primality test on primes, Carmichael numbers and + * strong pseudoprimes to small bases, split exponents past mbedTLS's limit, + * and check generated safe primes and DH_check results. */ +#include "../src/scr_dh.c" + +#include +#include + +static long total = 0, failed = 0; + +static void check(bool ok, const char *what) { + total++; + if (!ok) { + failed++; + fprintf(stderr, "FAIL: %s\n", what); + } +} + +static void from_hex(mbedtls_mpi *x, const char *hex) { + if (mbedtls_mpi_read_string(x, 16, hex) != 0) scr_trap("bad hex\n"); +} + +/* The Montgomery exponentiation, with the base 2 and with general bases, + * against mbedtls_mpi_exp_mod for moduli of one to 64 limbs. */ +static void test_mont_pow(void) { +#if SD_MONT + static const size_t sizes[] = {3, 64, 65, 127, 128, 129, 512, 1000, 2048, 4096}; + mbedtls_mpi n, base, e, two, got, want; + mbedtls_mpi_init(&n); + mbedtls_mpi_init(&base); + mbedtls_mpi_init(&e); + mbedtls_mpi_init(&two); + mbedtls_mpi_init(&got); + mbedtls_mpi_init(&want); + mbedtls_mpi_lset(&two, 2); + for (size_t s = 0; s < sizeof sizes / sizeof sizes[0]; s++) { + for (int trial = 0; trial < 6; trial++) { + size_t bits = sizes[s]; + sd_random_bits(&n, bits, true); + if (mbedtls_mpi_cmp_int(&n, 3) <= 0) mbedtls_mpi_lset(&n, 7); + sd_random_bits(&base, bits > 2 ? bits - 1 : 1, false); + size_t ebits = trial == 0 ? 1 : trial == 1 ? bits + 70 : (bits * (size_t)trial) / 5 + 1; + sd_random_bits(&e, ebits, false); + if (trial == 0) mbedtls_mpi_lset(&e, 0); + ScDhMont m; + bool ready = sd_mont_init(&m, &n); + check(ready, "sd_mont_init"); + char what[96]; + snprintf(what, sizeof what, "base^e mod n for %zu bits, trial %d", bits, trial); + bool ok = ready && sd_mont_pow(&m, &got, &base, &e) && mbedtls_mpi_exp_mod(&want, &base, &e, &n, NULL) == 0; + check(ok && mbedtls_mpi_cmp_mpi(&got, &want) == 0, what); + snprintf(what, sizeof what, "2^e mod n for %zu bits, trial %d", bits, trial); + ok = ready && sd_mont_pow(&m, &got, NULL, &e) && mbedtls_mpi_exp_mod(&want, &two, &e, &n, NULL) == 0; + check(ok && mbedtls_mpi_cmp_mpi(&got, &want) == 0, what); + sd_mont_free(&m); + } + } + mbedtls_mpi_free(&n); + mbedtls_mpi_free(&base); + mbedtls_mpi_free(&e); + mbedtls_mpi_free(&two); + mbedtls_mpi_free(&got); + mbedtls_mpi_free(&want); +#endif +} + +/* BN_check_prime's answers for small values, Mersenne numbers, Carmichael + * numbers and strong pseudoprimes to small prime bases. */ +static void test_is_prime(void) { + static const struct { + const char *hex; + bool prime; + } cases[] = { + {"0", false}, + {"1", false}, + {"2", true}, + {"3", true}, + {"4", false}, + {"5", true}, + {"9", false}, + {"231", false}, /* 561, a Carmichael number */ + {"451", false}, /* 1105, a Carmichael number */ + {"7ff", false}, /* 2047, a strong pseudoprime to the base 2 */ + {"45c5", false}, /* 17861 = 53 · 337 */ + {"45c7", true}, /* 17863, the largest trial-division prime */ + {"bfa17dc7", false}, /* 3215031751, to the bases 2, 3, 5 and 7 */ + {"351591274f9af9fb", false}, /* 3825123056546413051, to the bases up to 23 */ + {"437ae92817f9fc85b7e5", false}, /* to the prime bases up to 37 */ + {"2be6951adc5b22410a5fd", false}, /* to the prime bases up to 41 */ + {"1fffffffffffffff", true}, /* 2^61 - 1 */ + {"1ffffffffffffffffffffff", true}, /* 2^89 - 1 */ + {"7fffffffffffffffffffffffffffffff", true}, /* 2^127 - 1 */ + {"100000000000000000000000000000001", false}, /* 2^128 + 1 */ + }; + ScDhSmallPrimes *small = sd_small_primes(); + mbedtls_mpi x; + mbedtls_mpi_init(&x); + for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++) { + from_hex(&x, cases[i].hex); + char what[96]; + snprintf(what, sizeof what, "is_prime(0x%s) == %d", cases[i].hex, cases[i].prime); + check(sd_is_prime(small, &x) == cases[i].prime, what); + } + /* The modp groups' primes are safe primes. */ + for (size_t i = 0; i < 4; i++) { + mbedtls_mpi_read_binary(&x, sd_groups[i].p, sd_groups[i].p_len); + check(sd_is_prime(small, &x), "a modp prime is prime"); + mbedtls_mpi_shift_r(&x, 1); + check(sd_is_prime(small, &x), "half a modp prime is prime"); + mbedtls_mpi_add_int(&x, &x, 2); + check(!sd_is_prime(small, &x), "half a modp prime plus 2 is composite"); + } + mbedtls_mpi_free(&x); + free(small); +} + +/* DH_check bits for small and composite primes, the Pocklington path for + * safe primes, and a generator at p - 1. */ +static void test_check_params(void) { + static const struct { + const char *p; + int g; + int codes; + } cases[] = { + {"0", 2, 137}, {"1", 2, 137}, {"2", 2, 139}, {"3", 2, 138}, {"5", 2, 128}, {"5", 5, 136}, + {"7", 2, 128}, {"9", 2, 129}, {"f", 2, 129}, {"11", 2, 130}, {"17", 2, 128}, {"17", 22, 136}, + {"18", 2, 129}, {"231", 2, 129}, {"7ff", 2, 129}, + }; + mbedtls_mpi p, g; + mbedtls_mpi_init(&p); + mbedtls_mpi_init(&g); + for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++) { + from_hex(&p, cases[i].p); + mbedtls_mpi_lset(&g, cases[i].g); + char what[96]; + int codes = sd_check_params(&p, &g); + snprintf(what, sizeof what, "check(0x%s, %d) == %d (got %d)", cases[i].p, cases[i].g, cases[i].codes, codes); + check(codes == cases[i].codes, what); + } + mbedtls_mpi_read_binary(&p, sd_groups[1].p, sd_groups[1].p_len); + mbedtls_mpi_lset(&g, 2); + check(sd_check_params(&p, &g) == 0, "check(modp2, 2) == 0"); + mbedtls_mpi_sub_int(&g, &p, 1); + check(sd_check_params(&p, &g) == SD_NOT_SUITABLE_GENERATOR, "check(modp2, p - 1) == 8"); + mbedtls_mpi_lset(&g, 2); + mbedtls_mpi_add_int(&p, &p, 2); + check(sd_check_params(&p, &g) == SD_P_NOT_PRIME, "check(modp2 + 2, 2) == 1"); + mbedtls_mpi_free(&p); + mbedtls_mpi_free(&g); +} + +/* Exponents past 8192 bits, split into digits, against the exponent + * reduced modulo p - 1 (Fermat) for a prime p. */ +static void test_long_exponent(void) { + mbedtls_mpi p, p1, base, e, reduced, got, want; + mbedtls_mpi_init(&p); + mbedtls_mpi_init(&p1); + mbedtls_mpi_init(&base); + mbedtls_mpi_init(&e); + mbedtls_mpi_init(&reduced); + mbedtls_mpi_init(&got); + mbedtls_mpi_init(&want); + mbedtls_mpi_read_binary(&p, sd_groups[0].p, sd_groups[0].p_len); + mbedtls_mpi_sub_int(&p1, &p, 1); + mbedtls_mpi_lset(&base, 5); + static const size_t sizes[] = {8192, 8193, 16382, 16383, 20000}; + for (size_t i = 0; i < sizeof sizes / sizeof sizes[0]; i++) { + sd_random_bits(&e, sizes[i], false); + mbedtls_mpi_mod_mpi(&reduced, &e, &p1); + bool ok = sd_exp(&got, &base, &e, &p) && mbedtls_mpi_exp_mod(&want, &base, &reduced, &p, NULL) == 0; + char what[64]; + snprintf(what, sizeof what, "5^e mod p for a %zu-bit e", sizes[i]); + check(ok && mbedtls_mpi_cmp_mpi(&got, &want) == 0, what); + } + mbedtls_mpi_lset(&p, 24); + check(!sd_exp(&got, &base, &e, &p), "an even modulus fails"); + mbedtls_mpi_lset(&p, 1); + check(!sd_exp(&got, &base, &e, &p), "the modulus 1 fails"); + mbedtls_mpi_free(&p); + mbedtls_mpi_free(&p1); + mbedtls_mpi_free(&base); + mbedtls_mpi_free(&e); + mbedtls_mpi_free(&reduced); + mbedtls_mpi_free(&got); + mbedtls_mpi_free(&want); +} + +/* Generated safe primes have the requested length and congruence, and + * mbedTLS's own primality test accepts p and (p - 1) / 2. */ +static void test_generate(void) { + static const unsigned adds[] = {24, 60, 12}; + mbedtls_mpi p, q, g; + mbedtls_mpi_init(&p); + mbedtls_mpi_init(&q); + mbedtls_mpi_init(&g); + mbedtls_mpi_lset(&g, 2); + for (size_t i = 0; i < sizeof adds / sizeof adds[0]; i++) { + for (int trial = 0; trial < 2; trial++) { + bool ok = sd_generate_safe_prime(&p, 512, adds[i], adds[i] - 1); + mbedtls_mpi_uint r = 0; + ok = ok && mbedtls_mpi_bitlen(&p) == 512 && mbedtls_mpi_mod_int(&r, &p, adds[i]) == 0 && r == adds[i] - 1; + mbedtls_mpi_copy(&q, &p); + mbedtls_mpi_shift_r(&q, 1); + ok = ok && mbedtls_mpi_is_prime_ext(&p, 40, sd_random, NULL) == 0 && + mbedtls_mpi_is_prime_ext(&q, 40, sd_random, NULL) == 0; + char what[64]; + snprintf(what, sizeof what, "a 512-bit safe prime ≡ %u (mod %u)", adds[i] - 1, adds[i]); + check(ok, what); + check(sd_check_params(&p, &g) == 0, "a generated prime has no DH_check findings"); + } + } + mbedtls_mpi_free(&p); + mbedtls_mpi_free(&q); + mbedtls_mpi_free(&g); +} + +int main(void) { + test_mont_pow(); + test_is_prime(); + test_check_params(); + test_long_exponent(); + test_generate(); + fprintf(stderr, "%ld/%ld cases passed\n", total - failed, total); + return failed ? 1 : 0; +} diff --git a/tests/corpus/6706-dh-groups.ts b/tests/corpus/6706-dh-groups.ts new file mode 100644 index 000000000..24c8b5773 --- /dev/null +++ b/tests/corpus/6706-dh-groups.ts @@ -0,0 +1,52 @@ +// crypto.getDiffieHellman and createDiffieHellmanGroup on every modp +// group: the RFC primes and generator, verifyError, case-insensitive names, +// private keys of the group's width, and key pairs that agree on one +// secret of the prime's length. +import { createDiffieHellmanGroup, getDiffieHellman, type DiffieHellmanGroup } from "node:crypto"; + +const groups = ["modp1", "modp2", "modp5", "modp14", "modp15", "modp16", "modp17", "modp18"]; + +function bitLength(bytes: Buffer): number { + if (bytes.length === 0) return 0; + return (bytes.length - 1) * 8 + bytes[0]!.toString(2).length; +} + +for (const name of groups) { + const group: DiffieHellmanGroup = getDiffieHellman(name); + const prime: Buffer = group.getPrime(); + console.log( + name, + group.verifyError, + bitLength(prime), + prime.toString("hex").slice(0, 24), + prime.toString("hex").slice(-16), + group.getGenerator("hex"), + ); +} + +// Key exchange on the groups whose exponentiations stay quick. +for (const name of ["modp1", "modp2", "modp5", "modp14"]) { + const alice = getDiffieHellman(name); + const bob = createDiffieHellmanGroup(name.toUpperCase()); + const alicePublic: Buffer = alice.generateKeys(); + const bobPublic: string = bob.generateKeys("base64"); + const fromAlice: Buffer = alice.computeSecret(bobPublic, "base64"); + const fromBob: string = bob.computeSecret(alicePublic, null, "hex"); + const width = bitLength(alice.getPrivateKey()); + console.log( + name, + fromAlice.length, + fromAlice.toString("hex") === fromBob, + alicePublic.length <= fromAlice.length, + name === "modp1" || name === "modp2" ? width <= bitLength(alice.getPrime()) : width, + ); + // generateKeys keeps the key pair it has. + console.log(alice.generateKeys("hex") === alicePublic.toString("hex"), alice.getPublicKey().equals(alicePublic)); +} + +// The prime and generator in every encoding. +const modp5 = getDiffieHellman("Modp5"); +for (const encoding of ["hex", "base64", "base64url", "latin1", "binary"] as const) { + const prime: string = modp5.getPrime(encoding as "hex"); + console.log(encoding, prime.length, JSON.stringify(modp5.getGenerator(encoding as "hex"))); +} diff --git a/tests/corpus/6707-dh-explicit-params.ts b/tests/corpus/6707-dh-explicit-params.ts new file mode 100644 index 000000000..57e239d73 --- /dev/null +++ b/tests/corpus/6707-dh-explicit-params.ts @@ -0,0 +1,169 @@ +// crypto.createDiffieHellman over explicit parameters: fixed private keys +// give Node's public keys and secrets, verifyError reports DH_check's +// findings, and parameters that equal an OpenSSL named group draw private +// keys of the group's length. +import { createDiffieHellman, getDiffieHellman, type DiffieHellman } from "node:crypto"; + +// A 512-bit safe prime with p ≡ 23 (mod 24). +const P512 = + "f29864297728009aab9d4220c05ca2d80a5e4ac2f016cd098543a2486bde8bcb" + + "deed9eab7029c8d2b7a681f9495bfc7b1c39c6db64457bb79beeb74a4b78f5ff"; + +function bitLength(bytes: Buffer): number { + if (bytes.length === 0) return 0; + return (bytes.length - 1) * 8 + bytes[0]!.toString(2).length; +} + +function fixed(prime: Buffer, generator: number | Buffer, key: string): DiffieHellman { + const dh = createDiffieHellman(prime, generator); + dh.setPrivateKey(key, "hex"); + return dh; +} + +const prime512 = Buffer.from(P512, "hex"); +for (const generator of [2, 3, 5, Buffer.from([0x0b])]) { + const alice = fixed(prime512, generator, "0123456789abcdef0123456789abcdef"); + const bob = fixed(prime512, generator, "fedcba9876543210fedcba9876543210ff"); + const alicePublic: string = alice.generateKeys("hex"); + const bobPublic: Buffer = bob.generateKeys(); + const secret: string = alice.computeSecret(bobPublic, null, "hex"); + console.log(alice.getGenerator("hex"), alice.verifyError, alicePublic.slice(0, 32), bobPublic.length); + console.log(secret, secret === bob.computeSecret(alicePublic, "hex", "hex")); +} + +// Secrets pad to the prime's length; public keys are minimal. +const modp1 = getDiffieHellman("modp1").getPrime(); +const small = fixed(modp1, 2, "01"); +console.log(small.generateKeys("hex"), small.getPrivateKey("hex")); +const big = fixed(modp1, 2, "ff".repeat(100)); +console.log(big.generateKeys().length, big.getPrivateKey().length); +const padded: Buffer = small.computeSecret(Buffer.from([4])); +console.log(padded.length, padded.subarray(0, 4).toString("hex"), padded.subarray(-2).toString("hex")); +console.log(big.computeSecret(Buffer.from([2]), null, "base64url")); + +// A private key of 0 still yields a public key; reading it back and +// deriving a secret from it fail. A private key of 1 answers the peer's +// key. +function report(run: () => unknown): void { + try { + const result = run(); + console.log(Buffer.isBuffer(result) ? result.toString("hex") : result); + } catch (error) { + const e = error as Error & { code?: string }; + console.log(e.name, e.code, e.message); + } +} +const zero = fixed(prime512, 2, "00"); +console.log(zero.generateKeys("hex")); +report(() => zero.getPrivateKey()); +report(() => zero.computeSecret(Buffer.from([5]))); +report(() => fixed(prime512, 2, "01").computeSecret(Buffer.from([5]))); + +// A private key longer than the prime: 8800 bits. +const long = fixed(modp1, 2, "ff".repeat(1100)); +console.log(long.generateKeys("hex").slice(0, 32), long.computeSecret(Buffer.from([7]), null, "hex").slice(-32)); + +// setPublicKey and setPrivateKey answer the object and replace the keys. +const keys = createDiffieHellman(prime512); +keys.setPublicKey(Buffer.from([0, 0, 7])); +keys.setPrivateKey("0005", "hex"); +console.log(keys.getPublicKey("hex"), keys.getPrivateKey("hex")); +console.log(keys.generateKeys("hex").length, keys.getPublicKey("hex") !== "07"); + +// verifyError for small, even, composite and non-safe primes and for +// unsuitable generators. +const cases: [string, number | string][] = [ + ["01", 2], + ["02", 2], + ["03", 2], + ["0d", 2], + ["15", 2], + ["17", 2], + ["17", 5], + ["17", "16"], + ["17", "1e"], + ["18", 2], + ["000017", 2], + ["05", 2], + ["05", 5], + ["07", 2], + ["09", 2], + ["0b", 5], + ["0f", 2], + ["11", 2], + ["2f", 2], + ["3b", 5], + ["0231", 2], + ["0451", 2], + ["07ff", 2], +]; +for (const [prime, generator] of cases) { + const g = typeof generator === "number" ? generator : Buffer.from(generator, "hex"); + console.log(prime, generator, createDiffieHellman(Buffer.from(prime, "hex"), g).verifyError); +} +const almost = Buffer.from(P512, "hex"); +almost[almost.length - 1] = 0xfe; +console.log(createDiffieHellman(almost).verifyError); +const composite = Buffer.from(P512, "hex"); +composite[composite.length - 1] = 0xfd; +console.log(createDiffieHellman(composite).verifyError); +const generatorPMinus1 = Buffer.from(P512, "hex"); +generatorPMinus1[generatorPMinus1.length - 1] = 0xfe; +console.log(createDiffieHellman(prime512, generatorPMinus1).verifyError); + +// Generated keys: bits(p) - 2 bits for other primes, at most the named +// group's key length for RFC 3526 and RFC 5114 parameters. +const generated = createDiffieHellman(prime512); +generated.generateKeys(); +console.log(bitLength(generated.getPrivateKey())); +const modp14 = getDiffieHellman("modp14").getPrime(); +const named = createDiffieHellman(modp14, Buffer.from([0, 2])); +named.generateKeys(); +console.log(named.verifyError, bitLength(named.getPrivateKey()) <= 225); +const notNamed = createDiffieHellman(modp1); +notNamed.generateKeys(); +console.log(bitLength(notNamed.getPrivateKey())); +const rfc5114 = createDiffieHellman( + Buffer.from( + "b10b8f96a080e01dde92de5eae5d54ec52c99fbcfb06a3c69a6a9dca52d23b616073e28675a23d189838ef1e2ee652c0" + + "13ecb4aea906112324975c3cd49b83bfaccbdd7d90c4bd7098488e9c219a73724effd6fae5644738faa31a4ff55bccc0" + + "a151af5f0dc8b4bd45bf37df365c1a65e68cfda76d4da708df1fb2bc2e4a4371", + "hex", + ), + Buffer.from( + "a4d1cbd5c3fd34126765a442efb99905f8104dd258ac507fd6406cff14266d31266fea1e5c41564b777e690f5504f213" + + "160217b4b01b886a5e91547f9e2749f4d7fbd7d3b9a92ee1909d0d2263f80a76a6a24c087a091f531dbf0a0169b6a28a" + + "d662a4d18e73afa32d779d5918d08bc8858f4dcef97c2a24855e6eeb22b3b2e5", + "hex", + ), +); +rfc5114.generateKeys(); +console.log(rfc5114.verifyError, bitLength(rfc5114.getPrivateKey()) <= 160); +// RFC 5114's 2048-bit group with a 256-bit subgroup draws 224-bit keys. +const rfc5114q256 = createDiffieHellman( + Buffer.from( + "87a8e61db4b6663cffbbd19c651959998ceef608660dd0f25d2ceed4435e3b00e00df8f1d61957d4faf7df4561b2aa30" + + "16c3d91134096faa3bf4296d830e9a7c209e0c6497517abd5a8a9d306bcf67ed91f9e6725b4758c022e0b1ef4275bf7b" + + "6c5bfc11d45f9088b941f54eb1e59bb8bc39a0bf12307f5c4fdb70c581b23f76b63acae1caa6b7902d52526735488a0e" + + "f13c6d9a51bfa4ab3ad8347796524d8ef6a167b5a41825d967e144e5140564251ccacb83e6b486f6b3ca3f7971506026" + + "c0b857f689962856ded4010abd0be621c3a3960a54e710c375f26375d7014103a4b54330c198af126116d2276e11715f" + + "693877fad7ef09cadb094ae91e1a1597", + "hex", + ), + Buffer.from( + "3fb32c9b73134d0b2e77506660edbd484ca7b18f21ef205407f4793a1a0ba12510dbc15077be463fff4fed4aac0bb555" + + "be3a6c1b0c6b47b1bc3773bf7e8c6f62901228f8c28cbb18a55ae31341000a650196f931c77a57f2ddf463e5e9ec144b" + + "777de62aaab8a8628ac376d282d6ed3864e67982428ebc831d14348f6f2f9193b5045af2767164e1dfc967c1fb3f2e55" + + "a4bd1bffe83b9c80d052b985d182ea0adb2a3b7313d3fe14c8484b1e052588b9b7d2bbd2df016199ecd06e1557cd0915" + + "b3353bbb64e0ec377fd028370df92b52c7891428cdc67eb6184b523d1db246c32f63078490f00ef8d647d148d4795451" + + "5e2327cfef98c582664b4c0f6cc41659", + "hex", + ), +); +let widest = 0; +for (let i = 0; i < 8; i++) { + const keys = createDiffieHellman(rfc5114q256.getPrime(), rfc5114q256.getGenerator()); + keys.generateKeys(); + widest = Math.max(widest, bitLength(keys.getPrivateKey())); +} +console.log(rfc5114q256.verifyError, widest <= 224, widest > 200); diff --git a/tests/corpus/6708-dh-errors.js b/tests/corpus/6708-dh-errors.js new file mode 100644 index 000000000..e092da727 --- /dev/null +++ b/tests/corpus/6708-dh-errors.js @@ -0,0 +1,147 @@ +// createDiffieHellman, getDiffieHellman and DiffieHellman errors from +// JavaScript: argument types and ranges, the keyEncoding and generator +// forms, OpenSSL's generator and modulus errors with their library and +// reason properties, parameter sizes, group names, encodings, keys read +// before they exist, public keys outside (1, p - 1), key generation and +// secrets on primes outside 512 to 10000 bits, and members a +// DiffieHellmanGroup does not have. +import crypto from "node:crypto"; + +// Values the declared overloads reject reach the calls through an +// untyped function, as they would from other untyped code. +const loose = (value) => value; + +function attempt(label, run) { + try { + const result = run(); + if (Buffer.isBuffer(result)) console.log(label, "ok", result.toString("hex")); + else if (typeof result === "object" && result !== null) + console.log(label, "ok", result.getGenerator("hex"), result.getPrime().length); + else console.log(label, "ok", JSON.stringify(result)); + } catch (e) { + const extra = e.library === undefined ? "" : ` [${Object.keys(e).join(",")}] ${e.library} / ${e.reason}`; + console.log(label, e.name, e.code, e.message + extra); + } +} + +const prime = crypto.getDiffieHellman("modp1").getPrime(); + +attempt("size missing", () => crypto.createDiffieHellman(loose(undefined))); +attempt("size null", () => crypto.createDiffieHellman(loose(null))); +attempt("size boolean", () => crypto.createDiffieHellman(loose(true))); +attempt("size object", () => crypto.createDiffieHellman(loose({}))); +attempt("size bigint", () => crypto.createDiffieHellman(loose(5n))); +attempt("size fraction", () => crypto.createDiffieHellman(1.5)); +attempt("size NaN", () => crypto.createDiffieHellman(NaN)); +attempt("size 2^31", () => crypto.createDiffieHellman(2 ** 31)); +attempt("size 2^40", () => crypto.createDiffieHellman(2 ** 40)); +attempt("size -1", () => crypto.createDiffieHellman(-1)); +attempt("size -0", () => crypto.createDiffieHellman(-0)); +attempt("size 1", () => crypto.createDiffieHellman(1)); +attempt("size 1 bad generator", () => crypto.createDiffieHellman(1, 1)); +attempt("size 2 generator 1", () => crypto.createDiffieHellman(2, 1)); +attempt("size 2", () => crypto.createDiffieHellman(2)); +attempt("size 511", () => crypto.createDiffieHellman(511)); +attempt("size 10001", () => crypto.createDiffieHellman(10001)); +attempt("size generator string", () => crypto.createDiffieHellman(64, loose("02"))); +attempt("size generator view", () => crypto.createDiffieHellman(64, loose(Buffer.from([2])))); + +attempt("generator 0", () => crypto.createDiffieHellman(prime, 0)); +attempt("generator 1", () => crypto.createDiffieHellman(prime, 1)); +attempt("generator -1", () => crypto.createDiffieHellman(prime, -1)); +attempt("generator fraction", () => crypto.createDiffieHellman(prime, 2.5)); +attempt("generator 2^31", () => crypto.createDiffieHellman(prime, 2 ** 31)); +attempt("generator NaN", () => crypto.createDiffieHellman(prime, NaN)); +attempt("generator null", () => crypto.createDiffieHellman(prime, loose(null))); +attempt("generator empty string", () => crypto.createDiffieHellman(prime, loose(""))); +attempt("generator true", () => crypto.createDiffieHellman(prime, loose(true))); +attempt("generator object", () => crypto.createDiffieHellman(prime, loose({}))); +attempt("generator view 1", () => crypto.createDiffieHellman(prime, Buffer.from([1]))); +attempt("generator view 0", () => crypto.createDiffieHellman(prime, Buffer.from([0]))); +attempt("generator view empty", () => crypto.createDiffieHellman(prime, Buffer.alloc(0))); +attempt("generator view 0002", () => crypto.createDiffieHellman(prime, Buffer.from([0, 2]))); +attempt("generator = p", () => crypto.createDiffieHellman(prime, prime)); +attempt("generator > p", () => crypto.createDiffieHellman(prime, Buffer.concat([Buffer.from([1]), prime]))); +attempt("generator string", () => crypto.createDiffieHellman(prime, loose("abc"))); +attempt("generator string hex", () => crypto.createDiffieHellman(prime, loose("05"), loose("hex"))); +attempt("generator bad encoding", () => crypto.createDiffieHellman(prime, loose("05"), loose("nope"))); +attempt("generator uint16", () => crypto.createDiffieHellman(prime, new Uint16Array([0x0300]))); + +attempt("prime hex", () => crypto.createDiffieHellman(prime.toString("hex"), "hex")); +attempt("prime base64 generator", () => crypto.createDiffieHellman(prime.toString("base64"), "base64", 5)); +attempt("prime utf8", () => crypto.createDiffieHellman("abc").verifyError); +attempt("prime buffer encoding", () => crypto.createDiffieHellman("abc", "buffer")); +attempt("keyEncoding as generator", () => crypto.createDiffieHellman("abc", loose("nope")).verifyError); +attempt("keyEncoding as generator with encoding", () => crypto.createDiffieHellman("abc", loose("nope"), loose("hex"))); +attempt("keyEncoding number", () => crypto.createDiffieHellman(prime, loose(5))); +attempt("keyEncoding on a view", () => crypto.createDiffieHellman(prime, loose("hex"), 5)); +attempt("prime ArrayBuffer", () => crypto.createDiffieHellman(new Uint8Array(prime).buffer)); +attempt("prime DataView", () => crypto.createDiffieHellman(new DataView(new ArrayBuffer(3))).verifyError); +attempt("prime zero", () => crypto.createDiffieHellman(Buffer.alloc(4)).getPrime()); + +attempt("group unknown", () => crypto.getDiffieHellman("modp3")); +attempt("group empty", () => crypto.getDiffieHellman("")); +attempt("group ffdhe", () => crypto.createDiffieHellmanGroup("ffdhe2048")); +attempt("group case", () => crypto.getDiffieHellman("MoDp2")); +attempt("group missing", () => crypto.getDiffieHellman(loose(undefined))); +attempt("group number", () => crypto.getDiffieHellman(loose(14))); + +const group = crypto.getDiffieHellman("modp2"); +attempt("group public before keys", () => group.getPublicKey()); +attempt("group private before keys", () => group.getPrivateKey()); +attempt("group secret before keys", () => group.computeSecret(Buffer.from([5]))); +const untypedGroup = loose(group); +attempt("group setPublicKey", () => untypedGroup.setPublicKey(Buffer.from([5]))); +attempt("group setPrivateKey", () => untypedGroup.setPrivateKey(Buffer.from([5]))); +group.generateKeys(); +attempt("secret 0", () => group.computeSecret(Buffer.from([0]))); +attempt("secret empty", () => group.computeSecret(Buffer.alloc(0))); +attempt("secret 1", () => group.computeSecret(Buffer.from([0, 1]))); +attempt("secret p - 1", () => { + const key = Buffer.from(group.getPrime()); + key[key.length - 1]--; + return group.computeSecret(key); +}); +attempt("secret p", () => group.computeSecret(group.getPrime())); +attempt("secret 2", () => group.computeSecret(Buffer.from([2])).length); +attempt("secret number", () => group.computeSecret(loose(5))); +attempt("secret bad input encoding", () => group.computeSecret("ab", loose("nope"))); +attempt("secret bad output encoding", () => untypedGroup.computeSecret(Buffer.from([2]), null, "nope")); +attempt("getPrime bad encoding", () => untypedGroup.getPrime("nope")); +attempt("getPrime HEX", () => untypedGroup.getPrime("HEX").slice(0, 8)); +attempt("getGenerator utf16le", () => untypedGroup.getGenerator("utf16le")); +attempt("verifyError call", () => untypedGroup.verifyError()); +attempt("unknown member", () => untypedGroup.bogus()); + +const fresh = crypto.createDiffieHellman(prime); +attempt("public before keys", () => fresh.getPublicKey()); +attempt("private before keys", () => fresh.getPrivateKey()); +attempt("secret before keys", () => fresh.computeSecret(Buffer.from([5]))); +attempt("setPublicKey number", () => fresh.setPublicKey(loose(5))); +attempt("setPrivateKey bad encoding", () => fresh.setPrivateKey("ab", loose("nope"))); +attempt("setPublicKey 0", () => loose(fresh.setPublicKey(Buffer.from([0]))).getPublicKey()); +attempt("setPrivateKey utf8", () => loose(fresh.setPrivateKey("ab")).getPrivateKey("hex")); + +// Primes outside 512 to 10000 bits: key generation and secrets fail, but a +// private key the program sets still yields a public key. +for (const value of [23, 1, 0, 24]) { + const label = `p ${value}`; + const dh = crypto.createDiffieHellman(Buffer.from([value])); + attempt(`${label} generateKeys`, () => dh.generateKeys()); + attempt(`${label} secret 2`, () => dh.computeSecret(Buffer.from([2]))); + attempt(`${label} secret 5`, () => dh.computeSecret(Buffer.from([5]))); + dh.setPrivateKey(Buffer.from([3])); + attempt(`${label} generateKeys with key`, () => dh.generateKeys()); + attempt(`${label} public with key`, () => dh.getPublicKey()); + attempt(`${label} secret with key`, () => dh.computeSecret(Buffer.from([5]))); +} +const even = Buffer.from(prime); +even[even.length - 1] = 0xfe; +const evenDh = crypto.createDiffieHellman(even); +attempt("even generateKeys", () => evenDh.generateKeys()); +evenDh.setPrivateKey(Buffer.from([3])); +attempt("even generateKeys with key", () => evenDh.generateKeys()); +attempt("p 23 generator 30", () => + loose(crypto.createDiffieHellman(Buffer.from([23]), 30).setPrivateKey(Buffer.from([2]))).generateKeys(), +); +attempt("p 9", () => loose(crypto.createDiffieHellman(Buffer.from([9])).setPrivateKey(Buffer.from([3]))).generateKeys()); diff --git a/tests/corpus/6709-dh-generate.ts b/tests/corpus/6709-dh-generate.ts new file mode 100644 index 000000000..b041605c2 --- /dev/null +++ b/tests/corpus/6709-dh-generate.ts @@ -0,0 +1,47 @@ +// crypto.createDiffieHellman(primeLength, generator) generates a safe +// prime of the requested length: p ≡ 23 (mod 24) for the generator 2, +// p ≡ 59 (mod 60) for 5 and p ≡ 11 (mod 12) otherwise. A second party built +// from the first one's prime and generator agrees on the secret. +import { createDiffieHellman } from "node:crypto"; + +function bitLength(bytes: Buffer): number { + if (bytes.length === 0) return 0; + return (bytes.length - 1) * 8 + bytes[0]!.toString(2).length; +} + +function mod(bytes: Buffer, m: number): number { + let r = 0; + for (const byte of bytes) r = (r * 256 + byte) % m; + return r; +} + +for (const generator of [undefined, 2, 5, 3, 7]) { + const alice = generator === undefined ? createDiffieHellman(512) : createDiffieHellman(512, generator); + const prime = alice.getPrime(); + console.log( + generator, + bitLength(prime), + alice.getGenerator("hex"), + alice.verifyError, + generator === undefined || generator === 2 + ? mod(prime, 24) + : generator === 5 + ? mod(prime, 60) + : mod(prime, 12), + ); + const bob = createDiffieHellman(alice.getPrime(), alice.getGenerator()); + const alicePublic = alice.generateKeys(); + const bobPublic = bob.generateKeys("hex"); + const secret = alice.computeSecret(bobPublic, "hex"); + console.log( + bob.verifyError, + bitLength(alice.getPrivateKey()), + secret.length, + secret.equals(bob.computeSecret(alicePublic)), + ); +} + +// A string prime with its encoding, and the generator as a string. +const shared = createDiffieHellman(512); +const copy = createDiffieHellman(shared.getPrime("base64"), "base64", shared.getGenerator("hex"), "hex"); +console.log(copy.getPrime().equals(shared.getPrime()), copy.getGenerator("hex"), copy.verifyError); diff --git a/tests/corpus/6710-dh-encodings.cjs b/tests/corpus/6710-dh-encodings.cjs new file mode 100644 index 000000000..100ee95e2 --- /dev/null +++ b/tests/corpus/6710-dh-encodings.cjs @@ -0,0 +1,73 @@ +// DiffieHellman string inputs and outputs from CommonJS: primes and +// generators as strings in every binary encoding, the keyEncoding slot +// read as the generator, keys set in every Buffer encoding and read in the +// declared ones, and computeSecret's input and output encodings. +const crypto = require("node:crypto"); + +// Values the declared overloads do not cover reach the calls through an +// untyped function, as they would from other untyped code. +const loose = (value) => value; + +const prime = crypto.getDiffieHellman("modp2").getPrime(); +const encodings = ["hex", "base64", "base64url", "latin1", "binary", "HEX", "Base64"]; + +for (const encoding of encodings) { + const text = prime.toString(encoding.toLowerCase()); + const dh = crypto.createDiffieHellman(text, encoding, "05", "hex"); + console.log(encoding, dh.getPrime().equals(prime), dh.getGenerator("hex")); +} +console.log(crypto.createDiffieHellman(prime.toString("hex"), "hex", "05", "hex").verifyError); + +// A truthy keyEncoding that names no encoding is the generator, and the +// generator slot its encoding. +console.log(crypto.createDiffieHellman(prime, 7).getGenerator("hex")); +console.log(crypto.createDiffieHellman(prime, "\x05").getGenerator("hex")); +console.log(crypto.createDiffieHellman(prime, Buffer.from([3])).getGenerator("hex")); +console.log(crypto.createDiffieHellman(prime, "buffer", 11).getGenerator("hex")); +console.log(crypto.createDiffieHellman(prime, "0d", "hex").getGenerator("hex")); +console.log(crypto.createDiffieHellman(prime, "base64", "Ew==", "base64").getGenerator("hex")); +console.log(crypto.createDiffieHellman(prime, "", 0, "").getGenerator("hex")); + +// Keys in and out of every encoding. +const alice = crypto.createDiffieHellman(prime); +const bob = crypto.createDiffieHellman(prime); +alice.setPrivateKey("q83vEjRWeJA=", "base64"); +bob.setPrivateKey(Buffer.from("10325476", "hex")); +alice.generateKeys(); +bob.generateKeys(); +function report(encoding, key, secret) { + const bytes = Buffer.isBuffer(secret) ? secret : Buffer.from(secret, encoding); + console.log(encoding, typeof key, key.length, bytes.toString("hex").slice(0, 24)); +} +const hex = alice.getPublicKey("hex"); +report("hex", hex, bob.computeSecret(hex, "hex", "hex")); +const base64 = alice.getPublicKey("base64"); +report("base64", base64, bob.computeSecret(base64, "base64", "base64")); +const base64url = alice.getPublicKey("base64url"); +report("base64url", base64url, bob.computeSecret(base64url, "base64url", "base64url")); +const binary = alice.getPublicKey("binary"); +report("binary", binary, bob.computeSecret(binary, "binary", "binary")); +const view = alice.getPublicKey(); +report("undefined", view, bob.computeSecret(view, undefined, undefined)); +report("null", view, bob.computeSecret(view, null, null)); +// Encodings @types/node does not declare for these methods reach them +// through an untyped receiver. +const untypedBob = loose(bob); +console.log(alice.getPrivateKey("hex"), alice.getPrivateKey("base64url"), untypedBob.getPrivateKey("latin1").length); +console.log(untypedBob.getPublicKey("utf16le").length, untypedBob.getPrime("ucs2").length, untypedBob.getGenerator("buffer")); +console.log(untypedBob.computeSecret(loose(alice).getPublicKey("latin1"), "latin1", "base64").slice(0, 16)); + +// setPublicKey and setPrivateKey read strings through their encoding; +// 'buffer' and a missing encoding read utf8. +const keys = crypto.createDiffieHellman(prime); +keys.setPublicKey("ffee", "hex"); +loose(keys).setPrivateKey("zz", "buffer"); +console.log(keys.getPublicKey("hex"), keys.getPrivateKey("hex")); +keys.setPublicKey("é"); +console.log(keys.getPublicKey("hex")); +keys.setPublicKey("é", "latin1"); +console.log(keys.getPublicKey("hex")); +keys.setPrivateKey(new Uint16Array([0x0102, 0x0304])); +console.log(keys.getPrivateKey("hex")); +keys.setPrivateKey(new DataView(new Uint8Array([9, 8, 7]).buffer)); +console.log(keys.getPrivateKey("hex")); diff --git a/tests/corpus/6711-dh-handles.ts b/tests/corpus/6711-dh-handles.ts new file mode 100644 index 000000000..c0c5522be --- /dev/null +++ b/tests/corpus/6711-dh-handles.ts @@ -0,0 +1,56 @@ +// DiffieHellman and DiffieHellmanGroup values as ordinary values: class +// fields, optional fields, arrays, Map values, parameters and return +// values, with verifyError read through each. +import * as crypto from "node:crypto"; +import { type DiffieHellman, type DiffieHellmanGroup } from "node:crypto"; + +const prime = crypto.getDiffieHellman("modp1").getPrime(); + +class Party { + readonly dh: DiffieHellman; + peer: DiffieHellman | null = null; + group?: DiffieHellmanGroup; + + constructor(seed: number) { + this.dh = crypto.createDiffieHellman(prime, 2); + this.dh.setPrivateKey(Buffer.from([seed, seed + 1, seed + 2])); + this.dh.generateKeys(); + } + + secretWith(other: Party): string { + return this.dh.computeSecret(other.dh.getPublicKey(), null, "hex"); + } +} + +const alice = new Party(1); +const bob = new Party(7); +alice.peer = bob.dh; +console.log(alice.secretWith(bob) === bob.secretWith(alice), alice.secretWith(bob).slice(0, 32)); +console.log(alice.peer !== null && alice.peer.getPublicKey().equals(bob.dh.getPublicKey())); +console.log(alice.group === undefined); +alice.group = crypto.getDiffieHellman("modp2"); +console.log(alice.group.verifyError, alice.group.getPrime().length); + +function verify(dh: DiffieHellman | DiffieHellmanGroup): number { + return dh.verifyError; +} + +function make(bytes: number[]): DiffieHellman { + return crypto.createDiffieHellman(Buffer.from(bytes)); +} + +const all: (DiffieHellman | DiffieHellmanGroup)[] = [alice.dh, make([23]), make([24]), crypto.getDiffieHellman("modp5")]; +console.log(all.map(verify).join(",")); +for (const dh of all) console.log(dh.getGenerator("hex"), dh.getPrime().length); + +const byName = new Map(); +for (const name of ["modp1", "modp2"]) byName.set(name, crypto.createDiffieHellmanGroup(name)); +for (const [name, group] of byName) { + group.generateKeys(); + console.log(name, group.getGenerator("hex"), group.computeSecret(alice.dh.getPublicKey()).length); +} + +let maybe: DiffieHellman | undefined; +console.log(maybe?.verifyError); +maybe = make([0x0d]); +console.log(maybe?.verifyError, maybe.getPrime("hex")); diff --git a/tests/fixtures/node-types/dh.ts b/tests/fixtures/node-types/dh.ts new file mode 100644 index 000000000..0b20b1056 --- /dev/null +++ b/tests/fixtures/node-types/dh.ts @@ -0,0 +1,22 @@ +import { + type DiffieHellman, + type DiffieHellmanGroup, + createDiffieHellman, + createDiffieHellmanGroup, + getDiffieHellman, +} from "node:crypto"; + +const group: DiffieHellmanGroup = getDiffieHellman("modp2"); +const alice: DiffieHellman = createDiffieHellman(group.getPrime(), group.getGenerator()); +const bob: DiffieHellman = createDiffieHellman(group.getPrime("hex"), "hex", 2); +alice.setPrivateKey(Buffer.from("0a0b0c", "hex")); +bob.setPrivateKey("0d0e0f", "hex"); +const alicePublic: Buffer = alice.generateKeys(); +const bobPublic: string = bob.generateKeys("base64"); +const fromAlice: string = alice.computeSecret(bobPublic, "base64", "hex"); +const fromBob: Buffer = bob.computeSecret(alicePublic); +console.log(fromAlice === fromBob.toString("hex"), fromAlice.slice(0, 16), alice.verifyError); +const named: DiffieHellmanGroup = createDiffieHellmanGroup("modp5"); +console.log(named.getPrime().length, named.getGenerator("hex"), bob.getPrivateKey("hex")); +const generated: string = named.generateKeys("hex"); +console.log(generated.length <= 384, named.getPublicKey("hex") === generated); diff --git a/tests/fixtures/node-types/fenced.ts b/tests/fixtures/node-types/fenced.ts index 16a74081f..c6a8e90b7 100644 --- a/tests/fixtures/node-types/fenced.ts +++ b/tests/fixtures/node-types/fenced.ts @@ -103,6 +103,12 @@ cipherStream.on("data", () => {}); import { createECDH } from "node:crypto"; createECDH("sect163k1"); createECDH("prime256v1").computeSecret(Buffer.alloc(65), "hex"); +/* DiffieHellman's Object.prototype members, and crypto.diffieHellman over + * asymmetric KeyObjects. */ +import { createDiffieHellman, diffieHellman, type KeyObject } from "node:crypto"; +createDiffieHellman(Buffer.from([23])).toString(); +declare const dhKey: KeyObject; +diffieHellman({ privateKey: dhKey, publicKey: dhKey }); /* WebCrypto beyond the HMAC slice: @types/node declares the whole * SubtleCrypto surface, and the methods outside importKey, sign, verify * and digest fence by name. */ diff --git a/tests/harness/__snapshots__/node-types-fenced.txt b/tests/harness/__snapshots__/node-types-fenced.txt index 0bd13963d..6ac45473f 100644 --- a/tests/harness/__snapshots__/node-types-fenced.txt +++ b/tests/harness/__snapshots__/node-types-fenced.txt @@ -228,24 +228,42 @@ fenced.ts:105:1 - error SC2020: 'ECDH.computeSecret(key, encoding) with a Buffer 104 | createECDH("sect163k1"); 105 | createECDH("prime256v1").computeSecret(Buffer.alloc(65), "hex"); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 106 | /* WebCrypto beyond the HMAC slice: @types/node declares the whole + 106 | /* DiffieHellman's Object.prototype members, and crypto.diffieHellman over hint: Node reads the second argument as the key's input encoding and answers a Buffer; pass the output encoding third: computeSecret(key, undefined, encoding) -fenced.ts:110:9 - error SC2020: 'crypto.subtle.encrypt' is part of the standard library types but has no scriptc lowering yet +fenced.ts:109:1 - error SC2020: 'DiffieHellman.toString' is part of the standard library types but has no scriptc lowering yet - 109 | async function encryptWithWebCrypto(key: import("node:crypto").webcrypto.CryptoKey): Promise { - 110 | await crypto.subtle.encrypt({ name: "AES-GCM", iv: new Uint8Array(12) }, key, new Uint8Array(1)); + 108 | import { createDiffieHellman, diffieHellman, type KeyObject } from "node:crypto"; + 109 | createDiffieHellman(Buffer.from([23])).toString(); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 110 | declare const dhKey: KeyObject; + + hint: generateKeys(), computeSecret(), getPrime(), getGenerator(), getPublicKey(), getPrivateKey(), setPublicKey(), setPrivateKey() and verifyError are supported + +fenced.ts:111:1 - error SC2020: 'crypto.diffieHellman' is typed by @types/node but has no scriptc lowering yet + + 110 | declare const dhKey: KeyObject; + 111 | diffieHellman({ privateKey: dhKey, publicKey: dhKey }); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 112 | /* WebCrypto beyond the HMAC slice: @types/node declares the whole + + hint: crypto.diffieHellman takes asymmetric KeyObjects, which the static runtime does not have; the objects of createDiffieHellman, getDiffieHellman and createECDH compute shared secrets + +fenced.ts:116:9 - error SC2020: 'crypto.subtle.encrypt' is part of the standard library types but has no scriptc lowering yet + + 115 | async function encryptWithWebCrypto(key: import("node:crypto").webcrypto.CryptoKey): Promise { + 116 | await crypto.subtle.encrypt({ name: "AES-GCM", iv: new Uint8Array(12) }, key, new Uint8Array(1)); | ^~~~~~~~~~~~~~~~~~~~~ - 111 | } + 117 | } hint: the native WebCrypto surface is crypto.subtle.importKey('raw', ...) for HMAC keys, sign and verify with HMAC, and digest with SHA-1, SHA-256, SHA-384 or SHA-512, each called directly on crypto.subtle -fenced.ts:115:15 - error SC2020: 'navigator.hardwareConcurrency' is typed by @types/node but has no scriptc lowering yet +fenced.ts:121:15 - error SC2020: 'navigator.hardwareConcurrency' is typed by @types/node but has no scriptc lowering yet - 114 | * compile; the other members fence by name. */ - 115 | const cores = navigator.hardwareConcurrency; + 120 | * compile; the other members fence by name. */ + 121 | const cores = navigator.hardwareConcurrency; | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 116 | void cores; + 122 | void cores; hint: of Node's navigator global only typeof navigator and navigator.userAgent compile \ No newline at end of file diff --git a/tests/harness/corpus-costs/6706-dh-groups.ts.json b/tests/harness/corpus-costs/6706-dh-groups.ts.json new file mode 100644 index 000000000..b051df274 --- /dev/null +++ b/tests/harness/corpus-costs/6706-dh-groups.ts.json @@ -0,0 +1 @@ +{ "plain": 4855, "san": 8228 } diff --git a/tests/harness/corpus-costs/6707-dh-explicit-params.ts.json b/tests/harness/corpus-costs/6707-dh-explicit-params.ts.json new file mode 100644 index 000000000..95ab0f1b2 --- /dev/null +++ b/tests/harness/corpus-costs/6707-dh-explicit-params.ts.json @@ -0,0 +1 @@ +{ "plain": 6328, "san": 10420 } diff --git a/tests/harness/corpus-costs/6708-dh-errors.js.json b/tests/harness/corpus-costs/6708-dh-errors.js.json new file mode 100644 index 000000000..743216cb9 --- /dev/null +++ b/tests/harness/corpus-costs/6708-dh-errors.js.json @@ -0,0 +1 @@ +{ "plain": 8044, "san": 10499 } diff --git a/tests/harness/corpus-costs/6709-dh-generate.ts.json b/tests/harness/corpus-costs/6709-dh-generate.ts.json new file mode 100644 index 000000000..1e00ad64d --- /dev/null +++ b/tests/harness/corpus-costs/6709-dh-generate.ts.json @@ -0,0 +1 @@ +{ "plain": 6999, "san": 9388 } diff --git a/tests/harness/corpus-costs/6710-dh-encodings.cjs.json b/tests/harness/corpus-costs/6710-dh-encodings.cjs.json new file mode 100644 index 000000000..15e5385e0 --- /dev/null +++ b/tests/harness/corpus-costs/6710-dh-encodings.cjs.json @@ -0,0 +1 @@ +{ "plain": 6048, "san": 8117 } diff --git a/tests/harness/corpus-costs/6711-dh-handles.ts.json b/tests/harness/corpus-costs/6711-dh-handles.ts.json new file mode 100644 index 000000000..ecf09ecb1 --- /dev/null +++ b/tests/harness/corpus-costs/6711-dh-handles.ts.json @@ -0,0 +1 @@ +{ "plain": 5664, "san": 7161 } diff --git a/tests/harness/errors.test.ts b/tests/harness/errors.test.ts index 8af6998ca..87ca7b59f 100644 --- a/tests/harness/errors.test.ts +++ b/tests/harness/errors.test.ts @@ -918,6 +918,53 @@ for (const run of [() => ecdh.getPublicKey.name, () => { ecdh.custom = 1; }]) { }); }); +// DiffieHellman primes past mbedTLS's 8192-bit limit throw a coded SC2020 +// error, as do property reads other than verifyError, property writes, and +// Object.prototype method calls on an untyped DiffieHellman. +describe("DiffieHellman runtime refusals", () => { + test("primes longer than 8192 bits throw SC2020", async () => { + const r = await compileAndRun( + "dh-long-prime", + `import { createDiffieHellman } from "node:crypto"; +const prime = Buffer.alloc(1025, 0xff); +for (const run of [() => createDiffieHellman(prime), () => createDiffieHellman(8193)]) { + try { + run(); + } catch (error) { + const e = error as Error & { code?: string }; + console.log(e.name, e.code, e.message); + } +} +console.log(createDiffieHellman(Buffer.alloc(1024, 0xff)).verifyError); +`, + ); + expect(r.exitCode).toBe(0); + const message = "DiffieHellman primes longer than 8192 bits have no native implementation"; + expect(r.stdout).toBe(`Error SC2020 ${message}\nError SC2020 ${message}\n1\n`); + expect(r.stderr).toBe(""); + }); + + test("DiffieHellman property reads, writes and Object.prototype calls throw SC2020", async () => { + const r = await compileAndRun( + "dh-properties", + `const crypto = require("node:crypto"); +const dh = crypto.getDiffieHellman("modp1"); +console.log(dh.verifyError); +for (const run of [() => dh.getPrime.name, () => { dh.verifyError = 1; }, () => dh.hasOwnProperty("verifyError")]) { + try { + run(); + } catch (e) { + console.log(e.name, e.code); + } +} +`, + "cjs", + ); + expect(r.exitCode).toBe(0); + expect(r.stdout).toBe("0\nError SC2020\nError SC2020\nError SC2020\n"); + }); +}); + // Node formats an unknown output encoding with %s, which prints a // function's source text and calls a toString the encoding's class // defines. The cipher runtime has neither, so both throw a coded SC2020 diff --git a/tests/harness/project-config.test.ts b/tests/harness/project-config.test.ts index dabb9b0bf..2f336a1cd 100644 --- a/tests/harness/project-config.test.ts +++ b/tests/harness/project-config.test.ts @@ -1114,6 +1114,20 @@ test("node-types: ECDH matches Node under @types/node", async () => { expect(comparableStderr(native.stderr)).toBe(node.stderr); }); +test("node-types: DiffieHellman matches Node under @types/node", async () => { + const outDir = outDirFor("node-dh"); + const entry = join(nodeTypesDir, "dh.ts"); + const result = await compile(entry, { outPath: join(outDir, "dh"), outDir, sanitize }); + expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics, null, 2) : "").toBe(true); + if (!result.ok) return; + const [native, node] = await Promise.all([ + execFileAsync(result.binaryPath), + execFileAsync(process.execPath, ["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", entry]), + ]); + expect(native.stdout).toBe(node.stdout); + expect(comparableStderr(native.stderr)).toBe(node.stderr); +}); + test("node-types: WebCrypto HMAC matches Node under @types/node", async () => { const outDir = outDirFor("node-webcrypto-hmac"); const entry = join(nodeTypesDir, "webcrypto-hmac.ts"); diff --git a/tests/harness/self-hosting-native-toolchain.test.ts b/tests/harness/self-hosting-native-toolchain.test.ts index 6d028d597..cbab9f6c9 100644 --- a/tests/harness/self-hosting-native-toolchain.test.ts +++ b/tests/harness/self-hosting-native-toolchain.test.ts @@ -48,6 +48,7 @@ const features: NativeLinkFeatures = { tlsCa: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, }; diff --git a/tests/harness/self-hosting-runtime-pack.test.ts b/tests/harness/self-hosting-runtime-pack.test.ts index f4fde795c..f170f96d1 100644 --- a/tests/harness/self-hosting-runtime-pack.test.ts +++ b/tests/harness/self-hosting-runtime-pack.test.ts @@ -49,6 +49,7 @@ const base: NativeLinkFeatures = { tlsCa: false, cipher: false, ecdh: false, + dh: false, webcrypto: false, perfLoop: false, };