diff --git a/.github/sync-node-ncrypto.json b/.github/sync-node-ncrypto.json index 95d8a97..74aa9b5 100644 --- a/.github/sync-node-ncrypto.json +++ b/.github/sync-node-ncrypto.json @@ -1,3 +1,3 @@ { - "node_commit": "b4f23d3619c98bed09af93a21192f6080197a8c6" + "node_commit": "2161c944dc174f8b8960fd633b36a1f0c881d283" } diff --git a/include/ncrypto.h b/include/ncrypto.h index dc75cec..b5bbd26 100644 --- a/include/ncrypto.h +++ b/include/ncrypto.h @@ -115,6 +115,18 @@ #define OPENSSL_WITH_EVP_MAC 0 #endif +#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_PREREQ(3, 0) +#define OPENSSL_WITH_AES_SIV 1 +#else +#define OPENSSL_WITH_AES_SIV 0 +#endif + +#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_PREREQ(3, 2) +#define OPENSSL_WITH_AES_GCM_SIV 1 +#else +#define OPENSSL_WITH_AES_GCM_SIV 0 +#endif + #if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_PREREQ(3, 2) #define OPENSSL_WITH_SIGNATURE_CONTEXT_STRING 1 #else @@ -460,9 +472,12 @@ class Cipher final { Cipher() = default; Cipher(const EVP_CIPHER* cipher) : cipher_(cipher) {} - Cipher(const Cipher&) = default; - Cipher& operator=(const Cipher&) = default; + Cipher(const Cipher& other); + Cipher& operator=(const Cipher& other); inline Cipher& operator=(const EVP_CIPHER* cipher) { +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + fetched_cipher_.reset(); +#endif cipher_ = cipher; return *this; } @@ -485,6 +500,8 @@ class Cipher final { bool isCtrMode() const; bool isCcmMode() const; bool isOcbMode() const; + bool isSivMode() const; + bool isGcmSivMode() const; bool isStreamMode() const; bool isChaCha20Poly1305() const; @@ -531,6 +548,7 @@ class Cipher final { struct CipherParams { int padding; Digest digest; + Digest mgf1_digest; const Buffer label; }; @@ -555,6 +573,10 @@ class Cipher final { private: const EVP_CIPHER* cipher_ = nullptr; +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + explicit Cipher(DeleteFnPtr cipher); + DeleteFnPtr fetched_cipher_; +#endif }; // ============================================================================ @@ -965,6 +987,8 @@ class CipherCtxPointer final { bool isOcbMode() const; bool isCcmMode() const; bool isWrapMode() const; + bool isSivMode() const; + bool isGcmSivMode() const; bool isChaCha20Poly1305() const; bool update(const Buffer& in, @@ -1260,9 +1284,9 @@ class DHPointer final { UNABLE_TO_CHECK_GENERATOR = 0x04, NOT_SUITABLE_GENERATOR = 0x08, Q_NOT_PRIME = 0x10, -#ifndef OPENSSL_IS_BORINGSSL - // Boringssl does not define the DH_CHECK_INVALID_[Q or J]_VALUE INVALID_Q = 0x20, +#ifndef OPENSSL_IS_BORINGSSL + // BoringSSL does not define DH_CHECK_INVALID_J_VALUE. INVALID_J = 0x40, MODULUS_TOO_SMALL = 0x80, MODULUS_TOO_LARGE = 0x100, @@ -1273,14 +1297,9 @@ class DHPointer final { enum class CheckPublicKeyResult { NONE, -#ifndef OPENSSL_IS_BORINGSSL - // Boringssl does not define DH_R_CHECK_PUBKEY_TOO_SMALL or TOO_LARGE - TOO_SMALL = DH_R_CHECK_PUBKEY_TOO_SMALL, - TOO_LARGE = DH_R_CHECK_PUBKEY_TOO_LARGE, - INVALID = DH_R_CHECK_PUBKEY_INVALID, -#else - INVALID = DH_R_INVALID_PUBKEY, -#endif + TOO_SMALL, + TOO_LARGE, + INVALID, CHECK_FAILED = 512, }; // Check to see if the given public key is suitable for this DH instance. @@ -1375,9 +1394,6 @@ class SSLPointer final { bool setSession(const SSLSessionPointer& session); bool setSniContext(const SSLCtxPointer& ctx) const; - const char* getClientHelloAlpn() const; - const char* getClientHelloServerName() const; - std::optional getServerName() const; X509View getCertificate() const; EVPKeyPointer getPeerTempKey() const; diff --git a/src/ncrypto.cpp b/src/ncrypto.cpp index 55a933f..6e711af 100644 --- a/src/ncrypto.cpp +++ b/src/ncrypto.cpp @@ -405,15 +405,7 @@ std::optional CryptoErrorList::pop_front() { // ============================================================================ DataPointer DataPointer::Alloc(size_t len) { -#ifdef OPENSSL_IS_BORINGSSL - // Boringssl does not implement OPENSSL_zalloc - auto ptr = OPENSSL_malloc(len); - if (ptr == nullptr) return {}; - memset(ptr, 0, len); - return DataPointer(ptr, len); -#else return DataPointer(OPENSSL_zalloc(len), len); -#endif } DataPointer DataPointer::SecureAlloc(size_t len) { @@ -436,18 +428,11 @@ DataPointer DataPointer::SecureAlloc(size_t len) { } size_t DataPointer::GetSecureHeapUsed() { -#ifndef OPENSSL_IS_BORINGSSL return CRYPTO_secure_malloc_initialized() ? CRYPTO_secure_used() : 0; -#else - // BoringSSL does not have the secure heap and therefore - // will always return 0. - return 0; -#endif } DataPointer::InitSecureHeapResult DataPointer::TryInitSecureHeap(size_t amount, size_t min) { -#ifndef OPENSSL_IS_BORINGSSL switch (CRYPTO_secure_malloc_init(amount, min)) { case 0: return InitSecureHeapResult::FAILED; @@ -458,10 +443,6 @@ DataPointer::InitSecureHeapResult DataPointer::TryInitSecureHeap(size_t amount, default: return InitSecureHeapResult::FAILED; } -#else - // BoringSSL does not actually support the secure heap - return InitSecureHeapResult::FAILED; -#endif } DataPointer DataPointer::Copy(const Buffer& buffer) { @@ -557,8 +538,7 @@ bool setFipsEnabled(bool enable, CryptoErrorList* errors) { if (isFipsEnabled() == enable) return true; ClearErrorOnReturn clearErrorOnReturn(errors); #if OPENSSL_VERSION_MAJOR >= 3 - return EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1 && - EVP_default_properties_is_fips_enabled(nullptr); + return EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1; #else return FIPS_mode_set(enable ? 1 : 0) == 1; #endif @@ -599,12 +579,7 @@ BignumPointer BignumPointer::New() { } BignumPointer BignumPointer::NewSecure() { -#ifdef OPENSSL_IS_BORINGSSL - // Boringssl does not implement BN_secure_new. - return New(); -#else return BignumPointer(BN_secure_new()); -#endif } BignumPointer& BignumPointer::operator=(BignumPointer&& other) noexcept { @@ -2323,14 +2298,11 @@ DHPointer::CheckPublicKeyResult DHPointer::checkPublicKey( if (DH_check_pub_key(dh_.get(), pub_key.get(), &codes) != 1) { return DHPointer::CheckPublicKeyResult::CHECK_FAILED; } -#ifndef OPENSSL_IS_BORINGSSL - // Boringssl does not define DH_CHECK_PUBKEY_TOO_SMALL or TOO_LARGE if (codes & DH_CHECK_PUBKEY_TOO_SMALL) { return DHPointer::CheckPublicKeyResult::TOO_SMALL; } else if (codes & DH_CHECK_PUBKEY_TOO_LARGE) { return DHPointer::CheckPublicKeyResult::TOO_LARGE; } -#endif if (codes != 0) { return DHPointer::CheckPublicKeyResult::INVALID; } @@ -4433,59 +4405,6 @@ std::optional SSLPointer::verifyPeerCertificate() const { return std::nullopt; } -const char* SSLPointer::getClientHelloAlpn() const { - if (ssl_ == nullptr) return {}; -#ifndef OPENSSL_IS_BORINGSSL - const unsigned char* buf; - size_t len; - size_t rem; - - if (!SSL_client_hello_get0_ext( - get(), - TLSEXT_TYPE_application_layer_protocol_negotiation, - &buf, - &rem) || - rem < 2) { - return {}; - } - - len = (buf[0] << 8) | buf[1]; - if (len + 2 != rem) return {}; - return reinterpret_cast(buf + 3); -#else - // Boringssl doesn't have a public API for this. - return {}; -#endif -} - -const char* SSLPointer::getClientHelloServerName() const { - if (ssl_ == nullptr) return {}; -#ifndef OPENSSL_IS_BORINGSSL - const unsigned char* buf; - size_t len; - size_t rem; - - if (!SSL_client_hello_get0_ext(get(), TLSEXT_TYPE_server_name, &buf, &rem) || - rem <= 2) { - return {}; - } - - len = (*buf << 8) | *(buf + 1); - if (len + 2 != rem) return {}; - rem = len; - - if (rem == 0 || *(buf + 2) != TLSEXT_NAMETYPE_host_name) return {}; - rem--; - if (rem <= 2) return {}; - len = (*(buf + 3) << 8) | *(buf + 4); - if (len + 2 > rem) return {}; - return reinterpret_cast(buf + 5); -#else - // Boringssl doesn't have a public API for this. - return {}; -#endif -} - std::optional SSLPointer::GetServerName( const SSL* ssl) { if (ssl == nullptr) return std::nullopt; @@ -4531,6 +4450,13 @@ std::optional SSLPointer::getNegotiatedGroup() const { const char* group = SSL_get0_group_name(get()); if (group == nullptr) return std::nullopt; return group; +#elif defined(OPENSSL_IS_BORINGSSL) + if (!ssl_) return std::nullopt; + const int nid = SSL_get_negotiated_group(get()); + if (nid == NID_undef) return std::nullopt; + const char* group = OBJ_nid2sn(nid); + if (group == nullptr) return std::nullopt; + return group; #else return std::nullopt; #endif @@ -4555,19 +4481,17 @@ std::optional SSLPointer::getCipherVersion() const { } std::optional SSLPointer::getSecurityLevel() { -#ifndef OPENSSL_IS_BORINGSSL auto ctx = SSLCtxPointer::New(); if (!ctx) return std::nullopt; +#ifdef OPENSSL_IS_BORINGSSL + return SSL_CTX_get_security_level(ctx.get()); +#else auto ssl = SSLPointer::New(ctx); if (!ssl) return std::nullopt; return SSL_get_security_level(ssl); -#else - // OPENSSL_TLS_SECURITY_LEVEL is not defined in BoringSSL - // so assume it is the default OPENSSL_TLS_SECURITY_LEVEL value. - return 1; -#endif // OPENSSL_IS_BORINGSSL +#endif } SSLCtxPointer::SSLCtxPointer(SSL_CTX* ctx) : ctx_(ctx) {} @@ -4632,12 +4556,79 @@ bool SSLCtxPointer::setCipherSuites(const char* ciphers) { // ============================================================================ +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV +Cipher::Cipher(DeleteFnPtr cipher) + : cipher_(cipher.get()), fetched_cipher_(std::move(cipher)) {} +#endif + +Cipher::Cipher(const Cipher& other) : cipher_(other.cipher_) { +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + if (other.fetched_cipher_ != nullptr) { + if (EVP_CIPHER_up_ref(other.fetched_cipher_.get()) == 1) { + fetched_cipher_.reset(other.fetched_cipher_.get()); + } else { + cipher_ = nullptr; + } + } +#endif +} + +Cipher& Cipher::operator=(const Cipher& other) { + if (this == &other) return *this; +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + if (other.fetched_cipher_ != nullptr) { + if (EVP_CIPHER_up_ref(other.fetched_cipher_.get()) == 1) { + fetched_cipher_.reset(other.fetched_cipher_.get()); + } else { + fetched_cipher_.reset(); + cipher_ = nullptr; + return *this; + } + } else { + fetched_cipher_.reset(); + } +#endif + cipher_ = other.cipher_; + return *this; +} + const Cipher Cipher::FromName(const char* name) { - return Cipher(EVP_get_cipherbyname(name)); + const EVP_CIPHER* cipher = EVP_get_cipherbyname(name); + if (cipher != nullptr) return Cipher(cipher); + +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + MarkPopErrorOnReturn mark_pop_error_on_return; + DeleteFnPtr fetched( + EVP_CIPHER_fetch(nullptr, name, nullptr)); + if (fetched == nullptr) return Cipher(); + + const int mode = EVP_CIPHER_mode(fetched.get()); + const bool is_siv_mode = +#if OPENSSL_WITH_AES_SIV + mode == EVP_CIPH_SIV_MODE || +#endif +#if OPENSSL_WITH_AES_GCM_SIV + mode == EVP_CIPH_GCM_SIV_MODE || +#endif + false; + if (is_siv_mode) return Cipher(std::move(fetched)); + + return Cipher(); +#else + return Cipher(); +#endif } const Cipher Cipher::FromNid(int nid) { - return Cipher(EVP_get_cipherbynid(nid)); + const EVP_CIPHER* cipher = EVP_get_cipherbynid(nid); + if (cipher != nullptr) return Cipher(cipher); + +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + const char* name = OBJ_nid2sn(nid); + if (name != nullptr) return FromName(name); +#endif + + return Cipher(); } const Cipher Cipher::FromCtx(const CipherCtxPointer& ctx) { @@ -4691,6 +4682,24 @@ bool Cipher::isOcbMode() const { return getMode() == EVP_CIPH_OCB_MODE; } +bool Cipher::isSivMode() const { + if (!cipher_) return false; +#if OPENSSL_WITH_AES_SIV + return getMode() == EVP_CIPH_SIV_MODE; +#else + return false; +#endif +} + +bool Cipher::isGcmSivMode() const { + if (!cipher_) return false; +#if OPENSSL_WITH_AES_GCM_SIV + return getMode() == EVP_CIPH_GCM_SIV_MODE; +#else + return false; +#endif +} + bool Cipher::isStreamMode() const { if (!cipher_) return false; return getMode() == EVP_CIPH_STREAM_CIPHER; @@ -4745,6 +4754,14 @@ std::string_view Cipher::getModeLabel() const { return "ocb"; case EVP_CIPH_OFB_MODE: return "ofb"; +#if OPENSSL_WITH_AES_SIV + case EVP_CIPH_SIV_MODE: + return "siv"; +#endif +#if OPENSSL_WITH_AES_GCM_SIV + case EVP_CIPH_GCM_SIV_MODE: + return "gcm-siv"; +#endif case EVP_CIPH_WRAP_MODE: return "wrap"; case EVP_CIPH_XTS_MODE: @@ -4759,7 +4776,16 @@ const char* Cipher::getName() const { if (!cipher_) return {}; // OBJ_nid2sn(EVP_CIPHER_nid(cipher)) is used here instead of // EVP_CIPHER_name(cipher) for compatibility with BoringSSL. - return OBJ_nid2sn(getNid()); + const int nid = getNid(); + if (nid != NID_undef) { + const char* name = OBJ_nid2sn(nid); + if (name != nullptr) return name; + } +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + return EVP_CIPHER_get0_name(cipher_); +#else + return {}; +#endif } bool Cipher::isSupportedAuthenticatedMode() const { @@ -4768,6 +4794,12 @@ bool Cipher::isSupportedAuthenticatedMode() const { case EVP_CIPH_GCM_MODE: #ifndef OPENSSL_NO_OCB case EVP_CIPH_OCB_MODE: +#endif +#if OPENSSL_WITH_AES_SIV + case EVP_CIPH_SIV_MODE: +#endif +#if OPENSSL_WITH_AES_GCM_SIV + case EVP_CIPH_GCM_SIV_MODE: #endif return true; case EVP_CIPH_STREAM_CIPHER: @@ -4883,6 +4915,24 @@ bool CipherCtxPointer::isWrapMode() const { return getMode() == EVP_CIPH_WRAP_MODE; } +bool CipherCtxPointer::isSivMode() const { + if (!ctx_) return false; +#if OPENSSL_WITH_AES_SIV + return getMode() == EVP_CIPH_SIV_MODE; +#else + return false; +#endif +} + +bool CipherCtxPointer::isGcmSivMode() const { + if (!ctx_) return false; +#if OPENSSL_WITH_AES_GCM_SIV + return getMode() == EVP_CIPH_GCM_SIV_MODE; +#else + return false; +#endif +} + bool CipherCtxPointer::isChaCha20Poly1305() const { if (!ctx_) return false; return getNid() == NID_chacha20_poly1305; @@ -5808,9 +5858,11 @@ DataPointer RSA_Cipher(const EVPKeyPointer& key, if (!key) return {}; EVPKeyCtxPointer ctx = key.newCtx(); + const Digest& mgf1_digest = + params.mgf1_digest != nullptr ? params.mgf1_digest : params.digest; if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || - (params.digest != nullptr && (!ctx.setRsaOaepMd(params.digest) || - !ctx.setRsaMgf1Md(params.digest)))) { + (params.digest != nullptr && + (!ctx.setRsaOaepMd(params.digest) || !ctx.setRsaMgf1Md(mgf1_digest)))) { return {}; } @@ -5849,7 +5901,9 @@ DataPointer CipherImpl(const EVPKeyPointer& key, if (!key) return {}; EVPKeyCtxPointer ctx = key.newCtx(); if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || - (params.digest != nullptr && !ctx.setRsaOaepMd(params.digest))) { + (params.digest != nullptr && !ctx.setRsaOaepMd(params.digest)) || + (params.mgf1_digest != nullptr && + !ctx.setRsaMgf1Md(params.mgf1_digest))) { return {}; } @@ -6332,6 +6386,22 @@ struct CipherCallbackContext { void operator()(const char* name) { cb(name); } }; +#if OPENSSL_WITH_AES_SIV +constexpr const char* kProviderOnlyAesSivCiphers[] = { + "aes-128-siv", + "aes-192-siv", + "aes-256-siv", +}; +#endif + +#if OPENSSL_WITH_AES_GCM_SIV +constexpr const char* kProviderOnlyAesGcmSivCiphers[] = { + "aes-128-gcm-siv", + "aes-192-gcm-siv", + "aes-256-gcm-siv", +}; +#endif + #if OPENSSL_VERSION_MAJOR >= 3 template , #endif &context); +#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV + auto maybe_push_provider_only_cipher = [&](const char* name) { + EVP_CIPHER* cipher = EVP_CIPHER_fetch(nullptr, name, nullptr); + if (cipher == nullptr) return; + EVP_CIPHER_free(cipher); + context.cb(name); + }; +#endif +#if OPENSSL_WITH_AES_SIV + for (const char* name : kProviderOnlyAesSivCiphers) { + maybe_push_provider_only_cipher(name); + } +#endif +#if OPENSSL_WITH_AES_GCM_SIV + for (const char* name : kProviderOnlyAesGcmSivCiphers) { + maybe_push_provider_only_cipher(name); + } +#endif #endif } @@ -7173,6 +7261,9 @@ std::pair X509Name::Iterator::operator*() const { unsigned char* value_str; int value_str_size = ASN1_STRING_to_UTF8(&value_str, value); + if (value_str_size < 0) [[unlikely]] { + return {{}, {}}; + } std::string out(reinterpret_cast(value_str), value_str_size); OPENSSL_free(value_str); // free after copy