diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 3a26cfbdcab52e..6baf3dfa7483b4 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -3783,6 +3783,35 @@ bool EVPKeyCtxPointer::setSignatureMd(const EVPMDCtxPointer& md) { 1; } +bool EVPKeyCtxPointer::setSignatureMd(const Digest& md) { + if (!ctx_ || !md) return false; + return EVP_PKEY_CTX_set_signature_md(ctx_.get(), md.get()) == 1; +} + +#if OPENSSL_VERSION_MAJOR >= 3 +int EVPKeyCtxPointer::initForSignEx(const OSSL_PARAM params[]) { + if (!ctx_) return 0; + return EVP_PKEY_sign_init_ex(ctx_.get(), params); +} + +int EVPKeyCtxPointer::initForVerifyEx(const OSSL_PARAM params[]) { + if (!ctx_) return 0; + return EVP_PKEY_verify_init_ex(ctx_.get(), params); +} +#endif + +#ifdef OSSL_SIGNATURE_PARAM_MU +int EVPKeyCtxPointer::initForSignMessage(const OSSL_PARAM params[]) { + if (!ctx_) return 0; + return EVP_PKEY_sign_message_init(ctx_.get(), nullptr, params); +} + +int EVPKeyCtxPointer::initForVerifyMessage(const OSSL_PARAM params[]) { + if (!ctx_) return 0; + return EVP_PKEY_verify_message_init(ctx_.get(), nullptr, params); +} +#endif + bool EVPKeyCtxPointer::initForEncrypt() { if (!ctx_) return false; return EVP_PKEY_encrypt_init(ctx_.get()) == 1; @@ -4321,6 +4350,27 @@ std::optional EVPMDCtxPointer::signInitWithContext( #ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING EVP_PKEY_CTX* ctx = nullptr; +#ifdef OSSL_SIGNATURE_PARAM_INSTANCE + // Ed25519ctx requires the INSTANCE param to enable context string support. + // Ed25519 pure mode ignores context strings without this. + if (key.id() == EVP_PKEY_ED25519) { + const OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string( + OSSL_SIGNATURE_PARAM_INSTANCE, const_cast("Ed25519ctx"), 0), + OSSL_PARAM_construct_octet_string( + OSSL_SIGNATURE_PARAM_CONTEXT_STRING, + const_cast(context_string.data), + context_string.len), + OSSL_PARAM_END}; + + if (!EVP_DigestSignInit_ex( + ctx_.get(), &ctx, nullptr, nullptr, nullptr, key.get(), params)) { + return std::nullopt; + } + return ctx; + } +#endif // OSSL_SIGNATURE_PARAM_INSTANCE + const OSSL_PARAM params[] = { OSSL_PARAM_construct_octet_string( OSSL_SIGNATURE_PARAM_CONTEXT_STRING, @@ -4345,6 +4395,27 @@ std::optional EVPMDCtxPointer::verifyInitWithContext( #ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING EVP_PKEY_CTX* ctx = nullptr; +#ifdef OSSL_SIGNATURE_PARAM_INSTANCE + // Ed25519ctx requires the INSTANCE param to enable context string support. + // Ed25519 pure mode ignores context strings without this. + if (key.id() == EVP_PKEY_ED25519) { + const OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string( + OSSL_SIGNATURE_PARAM_INSTANCE, const_cast("Ed25519ctx"), 0), + OSSL_PARAM_construct_octet_string( + OSSL_SIGNATURE_PARAM_CONTEXT_STRING, + const_cast(context_string.data), + context_string.len), + OSSL_PARAM_END}; + + if (!EVP_DigestVerifyInit_ex( + ctx_.get(), &ctx, nullptr, nullptr, nullptr, key.get(), params)) { + return std::nullopt; + } + return ctx; + } +#endif // OSSL_SIGNATURE_PARAM_INSTANCE + const OSSL_PARAM params[] = { OSSL_PARAM_construct_octet_string( OSSL_SIGNATURE_PARAM_CONTEXT_STRING, diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 5ffaee379e481a..a10cb7f6843616 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -798,6 +798,7 @@ class EVPKeyCtxPointer final { bool setRsaOaepLabel(DataPointer&& data); bool setSignatureMd(const EVPMDCtxPointer& md); + bool setSignatureMd(const Digest& md); bool publicCheck() const; bool privateCheck() const; @@ -821,6 +822,14 @@ class EVPKeyCtxPointer final { bool initForKeygen(); int initForVerify(); int initForSign(); +#if OPENSSL_VERSION_MAJOR >= 3 + int initForVerifyEx(const OSSL_PARAM params[]); + int initForSignEx(const OSSL_PARAM params[]); +#endif +#ifdef OSSL_SIGNATURE_PARAM_MU + int initForSignMessage(const OSSL_PARAM params[]); + int initForVerifyMessage(const OSSL_PARAM params[]); +#endif static EVPKeyCtxPointer New(const EVPKeyPointer& key); static EVPKeyCtxPointer NewFromID(int id); diff --git a/doc/api/crypto.md b/doc/api/crypto.md index acde45c346c84e..3e743615085fd6 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -5745,6 +5745,9 @@ Throws an error if FIPS mode is not available. + + + +* `algorithm` {string | null | undefined} +* `digest` {ArrayBuffer|Buffer|TypedArray|DataView} +* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `callback` {Function} + * `err` {Error} + * `signature` {Buffer} +* Returns: {Buffer} if the `callback` function is not provided. + + + +Calculates and returns the signature for `digest` using the given private key +and algorithm. Unlike [`crypto.sign()`][], this function does not hash the data +internally — `digest` is expected to be a pre-computed hash digest. + +The interpretation of `algorithm` and `digest` depends on the key type: + +* RSA, ECDSA, DSA: `algorithm` identifies the hash function used to create + `digest`. +* Ed25519, Ed448: `algorithm` must be `null` or `undefined`. `digest` must + be the output of the appropriate prehash function (SHA-512 for Ed25519ph, + SHAKE256 with 64-byte output for Ed448ph). +* ML-DSA: `algorithm` must be `null` or `undefined`. `digest` must be the + 64-byte external mu value per FIPS 204. + +If `key` is not a [`KeyObject`][], this function behaves as if `key` had been +passed to [`crypto.createPrivateKey()`][]. If it is an object, the following +additional properties can be passed: + +* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the + format of the generated signature. It can be one of the following: + * `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`. + * `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363. +* `padding` {integer} Optional padding value for RSA, one of the following: + + * `crypto.constants.RSA_PKCS1_PADDING` (default) + * `crypto.constants.RSA_PKCS1_PSS_PADDING` + + `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function + used to create the digest as specified in section 3.1 of [RFC 4055][]. +* `saltLength` {integer} Salt length for when padding is + `RSA_PKCS1_PSS_PADDING`. The special value + `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest + size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the + maximum permissible value. +* `context` {ArrayBuffer|Buffer|TypedArray|DataView} For Ed25519ph and Ed448ph, + this option specifies the optional context to differentiate signatures + generated for different purposes with the same key. Not supported for ML-DSA + keys because the context is already encoded into the mu value. + +If the `callback` function is provided this function uses libuv's threadpool. + ### `crypto.subtle` + + + +* `algorithm` {string|null|undefined} +* `digest` {ArrayBuffer|Buffer|TypedArray|DataView} +* `key` {Object|string|ArrayBuffer|Buffer|TypedArray|DataView|KeyObject|CryptoKey} +* `signature` {ArrayBuffer|Buffer|TypedArray|DataView} +* `callback` {Function} + * `err` {Error} + * `result` {boolean} +* Returns: {boolean} `true` or `false` depending on the validity of the + signature for the digest and public key if the `callback` function is not + provided. + + + +Verifies the given signature for `digest` using the given key and algorithm. +Unlike [`crypto.verify()`][], this function does not hash the data +internally — `digest` is expected to be a pre-computed hash digest. + +The interpretation of `algorithm` and `digest` depends on the key type: + +* RSA, ECDSA, DSA: `algorithm` identifies the hash function used to create + `digest`. +* Ed25519, Ed448: `algorithm` must be `null` or `undefined`. `digest` must + be the output of the appropriate prehash function (SHA-512 for Ed25519ph, + SHAKE256 with 64-byte output for Ed448ph). +* ML-DSA: `algorithm` must be `null` or `undefined`. `digest` must be the + 64-byte external mu value per FIPS 204. + +If `key` is not a [`KeyObject`][], this function behaves as if `key` had been +passed to [`crypto.createPublicKey()`][]. If it is an object, the following +additional properties can be passed: + +* `dsaEncoding` {string} For DSA and ECDSA, this option specifies the + format of the signature. It can be one of the following: + * `'der'` (default): DER-encoded ASN.1 signature structure encoding `(r, s)`. + * `'ieee-p1363'`: Signature format `r || s` as proposed in IEEE-P1363. +* `padding` {integer} Optional padding value for RSA, one of the following: + + * `crypto.constants.RSA_PKCS1_PADDING` (default) + * `crypto.constants.RSA_PKCS1_PSS_PADDING` + + `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function + used to create the digest as specified in section 3.1 of [RFC 4055][]. +* `saltLength` {integer} Salt length for when padding is + `RSA_PKCS1_PSS_PADDING`. The special value + `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest + size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the + maximum permissible value. +* `context` {ArrayBuffer|Buffer|TypedArray|DataView} For Ed25519ph and Ed448ph, + this option specifies the optional context to differentiate signatures + generated for different purposes with the same key. Not supported for ML-DSA + keys because the context is already encoded into the mu value. + +The `signature` argument is the previously calculated signature for the `digest`. + +Because public keys can be derived from private keys, a private key or a public +key may be passed for `key`. + +If the `callback` function is provided this function uses libuv's threadpool. + ### `crypto.webcrypto`