Skip to content

Commit 77ebbdf

Browse files
committed
crypto: discover ciphers from OpenSSL providers
Enumerate fetchable ciphers from activated OpenSSL 3 providers instead of maintaining lists of provider-only ciphers. This enables SM4-GCM, SM4-CCM, SM4-XTS, CBC-CTS, and other provider-only ciphers. Exclude NULL and TLS-only composite or multiblock implementations that cannot be used through Node.js Cipher APIs. Add test vectors for SM4-GCM, SM4-CCM, SM4-XTS, CBC-CTS, and AES key wrap variants. Share one-shot payload state across CBC-CTS, SIV, and CCM decryption so finalization rejects missing updates and CCM authentication failures are reported consistently. Fixes: #43040 Fixes: #64866 Signed-off-by: Filip Skokan <panva.ip@gmail.com>
1 parent f4bcf75 commit 77ebbdf

12 files changed

Lines changed: 534 additions & 114 deletions

‎deps/ncrypto/ncrypto.cc‎

Lines changed: 87 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4406,13 +4406,38 @@ bool SSLCtxPointer::setCipherSuites(const char* ciphers) {
44064406

44074407
// ============================================================================
44084408

4409-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
4409+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4410+
namespace {
4411+
bool IsSupportedCipher(const EVP_CIPHER* cipher) {
4412+
if (cipher == nullptr || EVP_CIPHER_is_a(cipher, "NULL")) return false;
4413+
4414+
constexpr auto kUnsupportedFlags =
4415+
EVP_CIPH_FLAG_CIPHER_WITH_MAC | EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK;
4416+
if ((EVP_CIPHER_get_flags(cipher) & kUnsupportedFlags) != 0) return false;
4417+
4418+
#ifdef OSSL_CIPHER_PARAM_ENCRYPT_THEN_MAC
4419+
int encrypt_then_mac = 0;
4420+
OSSL_PARAM params[] = {
4421+
OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_ENCRYPT_THEN_MAC,
4422+
&encrypt_then_mac),
4423+
OSSL_PARAM_construct_end(),
4424+
};
4425+
if (EVP_CIPHER_get_params(const_cast<EVP_CIPHER*>(cipher), params) == 1 &&
4426+
encrypt_then_mac != 0) {
4427+
return false;
4428+
}
4429+
#endif
4430+
4431+
return true;
4432+
}
4433+
} // namespace
4434+
44104435
Cipher::Cipher(DeleteFnPtr<EVP_CIPHER, EVP_CIPHER_free> cipher)
44114436
: cipher_(cipher.get()), fetched_cipher_(std::move(cipher)) {}
44124437
#endif
44134438

44144439
Cipher::Cipher(const Cipher& other) : cipher_(other.cipher_) {
4415-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
4440+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
44164441
if (other.fetched_cipher_ != nullptr) {
44174442
if (EVP_CIPHER_up_ref(other.fetched_cipher_.get()) == 1) {
44184443
fetched_cipher_.reset(other.fetched_cipher_.get());
@@ -4425,7 +4450,7 @@ Cipher::Cipher(const Cipher& other) : cipher_(other.cipher_) {
44254450

44264451
Cipher& Cipher::operator=(const Cipher& other) {
44274452
if (this == &other) return *this;
4428-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
4453+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
44294454
if (other.fetched_cipher_ != nullptr) {
44304455
if (EVP_CIPHER_up_ref(other.fetched_cipher_.get()) == 1) {
44314456
fetched_cipher_.reset(other.fetched_cipher_.get());
@@ -4444,36 +4469,34 @@ Cipher& Cipher::operator=(const Cipher& other) {
44444469

44454470
const Cipher Cipher::FromName(const char* name) {
44464471
const EVP_CIPHER* cipher = EVP_get_cipherbyname(name);
4447-
if (cipher != nullptr) return Cipher(cipher);
4472+
if (cipher != nullptr) {
4473+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4474+
if (!IsSupportedCipher(cipher)) return Cipher();
4475+
#endif
4476+
return Cipher(cipher);
4477+
}
44484478

4449-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
4479+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
44504480
MarkPopErrorOnReturn mark_pop_error_on_return;
44514481
DeleteFnPtr<EVP_CIPHER, EVP_CIPHER_free> fetched(
44524482
EVP_CIPHER_fetch(nullptr, name, nullptr));
4453-
if (fetched == nullptr) return Cipher();
4454-
4455-
const int mode = EVP_CIPHER_mode(fetched.get());
4456-
const bool is_siv_mode =
4457-
#if OPENSSL_WITH_AES_SIV
4458-
mode == EVP_CIPH_SIV_MODE ||
4459-
#endif
4460-
#if OPENSSL_WITH_AES_GCM_SIV
4461-
mode == EVP_CIPH_GCM_SIV_MODE ||
4462-
#endif
4463-
false;
4464-
if (is_siv_mode) return Cipher(std::move(fetched));
4465-
4466-
return Cipher();
4483+
if (!IsSupportedCipher(fetched.get())) return Cipher();
4484+
return Cipher(std::move(fetched));
44674485
#else
44684486
return Cipher();
44694487
#endif
44704488
}
44714489

44724490
const Cipher Cipher::FromNid(int nid) {
44734491
const EVP_CIPHER* cipher = EVP_get_cipherbynid(nid);
4474-
if (cipher != nullptr) return Cipher(cipher);
4492+
if (cipher != nullptr) {
4493+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4494+
if (!IsSupportedCipher(cipher)) return Cipher();
4495+
#endif
4496+
return Cipher(cipher);
4497+
}
44754498

4476-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
4499+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
44774500
const char* name = OBJ_nid2sn(nid);
44784501
if (name != nullptr) return FromName(name);
44794502
#endif
@@ -4527,6 +4550,15 @@ bool Cipher::isCcmMode() const {
45274550
return getMode() == EVP_CIPH_CCM_MODE;
45284551
}
45294552

4553+
bool Cipher::isCtsMode() const {
4554+
if (!cipher_) return false;
4555+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4556+
return (EVP_CIPHER_get_flags(cipher_) & EVP_CIPH_FLAG_CTS) != 0;
4557+
#else
4558+
return false;
4559+
#endif
4560+
}
4561+
45304562
bool Cipher::isOcbMode() const {
45314563
if (!cipher_) return false;
45324564
return getMode() == EVP_CIPH_OCB_MODE;
@@ -4631,7 +4663,7 @@ const char* Cipher::getName() const {
46314663
const char* name = OBJ_nid2sn(nid);
46324664
if (name != nullptr) return name;
46334665
}
4634-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
4666+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
46354667
return EVP_CIPHER_get0_name(cipher_);
46364668
#else
46374669
return {};
@@ -4758,6 +4790,11 @@ bool CipherCtxPointer::isCcmMode() const {
47584790
return getMode() == EVP_CIPH_CCM_MODE;
47594791
}
47604792

4793+
bool CipherCtxPointer::isCtsMode() const {
4794+
if (!ctx_) return false;
4795+
return Cipher::FromCtx(*this).isCtsMode();
4796+
}
4797+
47614798
bool CipherCtxPointer::isWrapMode() const {
47624799
if (!ctx_) return false;
47634800
return getMode() == EVP_CIPH_WRAP_MODE;
@@ -6229,23 +6266,7 @@ struct CipherCallbackContext {
62296266
void operator()(const char* name) { cb(name); }
62306267
};
62316268

6232-
#if OPENSSL_WITH_AES_SIV
6233-
constexpr const char* kProviderOnlyAesSivCiphers[] = {
6234-
"aes-128-siv",
6235-
"aes-192-siv",
6236-
"aes-256-siv",
6237-
};
6238-
#endif
6239-
6240-
#if OPENSSL_WITH_AES_GCM_SIV
6241-
constexpr const char* kProviderOnlyAesGcmSivCiphers[] = {
6242-
"aes-128-gcm-siv",
6243-
"aes-192-gcm-siv",
6244-
"aes-256-gcm-siv",
6245-
};
6246-
#endif
6247-
6248-
#if OPENSSL_VERSION_MAJOR >= 3
6269+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
62496270
template <class TypeName,
62506271
TypeName* fetch_type(OSSL_LIB_CTX*, const char*, const char*),
62516272
void free_type(TypeName*),
@@ -6269,13 +6290,34 @@ void array_push_back(const TypeName* evp_ref,
62696290
// instance if the algorithm is supported by the public OpenSSL APIs (some
62706291
// algorithms are used internally by OpenSSL and are also passed to this
62716292
// callback).
6272-
TypeName* fetched = fetch_type(nullptr, real_name, nullptr);
6273-
if (fetched == nullptr) return;
6293+
DeleteFnPtr<TypeName, free_type> fetched(
6294+
fetch_type(nullptr, real_name, nullptr));
6295+
if (!IsSupportedCipher(fetched.get())) return;
62746296

6275-
free_type(fetched);
62766297
auto& cb = *(static_cast<CipherCallbackContext*>(arg));
62776298
cb(from);
62786299
}
6300+
6301+
void array_push_back_provider(EVP_CIPHER* cipher, void* arg) {
6302+
const char* name = EVP_CIPHER_get0_name(cipher);
6303+
if (name == nullptr) return;
6304+
6305+
DeleteFnPtr<EVP_CIPHER, EVP_CIPHER_free> fetched(
6306+
EVP_CIPHER_fetch(nullptr, name, nullptr));
6307+
if (!IsSupportedCipher(fetched.get())) return;
6308+
6309+
name = EVP_CIPHER_get0_name(fetched.get());
6310+
if (name == nullptr) return;
6311+
6312+
std::string normalized_name(name);
6313+
std::transform(
6314+
normalized_name.begin(),
6315+
normalized_name.end(),
6316+
normalized_name.begin(),
6317+
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
6318+
auto& cb = *(static_cast<CipherCallbackContext*>(arg));
6319+
cb(normalized_name.c_str());
6320+
}
62796321
#else
62806322
template <class TypeName>
62816323
void array_push_back(const TypeName* evp_ref,
@@ -6301,7 +6343,7 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) {
63016343
}
63026344
#else
63036345
EVP_CIPHER_do_all_sorted(
6304-
#if OPENSSL_VERSION_MAJOR >= 3
6346+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
63056347
array_push_back<EVP_CIPHER,
63066348
EVP_CIPHER_fetch,
63076349
EVP_CIPHER_free,
@@ -6311,23 +6353,8 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) {
63116353
array_push_back<EVP_CIPHER>,
63126354
#endif
63136355
&context);
6314-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
6315-
auto maybe_push_provider_only_cipher = [&](const char* name) {
6316-
EVP_CIPHER* cipher = EVP_CIPHER_fetch(nullptr, name, nullptr);
6317-
if (cipher == nullptr) return;
6318-
EVP_CIPHER_free(cipher);
6319-
context.cb(name);
6320-
};
6321-
#endif
6322-
#if OPENSSL_WITH_AES_SIV
6323-
for (const char* name : kProviderOnlyAesSivCiphers) {
6324-
maybe_push_provider_only_cipher(name);
6325-
}
6326-
#endif
6327-
#if OPENSSL_WITH_AES_GCM_SIV
6328-
for (const char* name : kProviderOnlyAesGcmSivCiphers) {
6329-
maybe_push_provider_only_cipher(name);
6330-
}
6356+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
6357+
EVP_CIPHER_do_all_provided(nullptr, array_push_back_provider, &context);
63316358
#endif
63326359
#endif
63336360
}

‎deps/ncrypto/ncrypto.h‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ class Cipher final {
452452
Cipher(const Cipher& other);
453453
Cipher& operator=(const Cipher& other);
454454
inline Cipher& operator=(const EVP_CIPHER* cipher) {
455-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
455+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
456456
fetched_cipher_.reset();
457457
#endif
458458
cipher_ = cipher;
@@ -476,6 +476,7 @@ class Cipher final {
476476
bool isWrapMode() const;
477477
bool isCtrMode() const;
478478
bool isCcmMode() const;
479+
bool isCtsMode() const;
479480
bool isOcbMode() const;
480481
bool isSivMode() const;
481482
bool isGcmSivMode() const;
@@ -550,7 +551,7 @@ class Cipher final {
550551

551552
private:
552553
const EVP_CIPHER* cipher_ = nullptr;
553-
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
554+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
554555
explicit Cipher(DeleteFnPtr<EVP_CIPHER, EVP_CIPHER_free> cipher);
555556
DeleteFnPtr<EVP_CIPHER, EVP_CIPHER_free> fetched_cipher_;
556557
#endif
@@ -953,6 +954,7 @@ class CipherCtxPointer final {
953954
bool isGcmMode() const;
954955
bool isOcbMode() const;
955956
bool isCcmMode() const;
957+
bool isCtsMode() const;
956958
bool isWrapMode() const;
957959
bool isSivMode() const;
958960
bool isGcmSivMode() const;

0 commit comments

Comments
 (0)