Skip to content

Commit 56c43c7

Browse files
panvaaduh95
authored andcommitted
crypto: fix multi-prime RSA JWKs
Preserve additional RSA prime information when exporting and importing private JWKs across supported OpenSSL versions. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65649 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent f02dcb1 commit 56c43c7

7 files changed

Lines changed: 344 additions & 10 deletions

File tree

deps/ncrypto/ncrypto.cc

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,31 @@ struct OpenSSLBufferDeleter {
131131
};
132132
using OpenSSLBufferPointer =
133133
std::unique_ptr<unsigned char, OpenSSLBufferDeleter>;
134+
135+
struct RsaOtherPrimeParamNames {
136+
const char* factor;
137+
const char* exponent;
138+
const char* coefficient;
139+
};
140+
141+
#define RSA_OTHER_PRIME_PARAM_NAMES(prime, coefficient) \
142+
{ \
143+
OSSL_PKEY_PARAM_RSA_FACTOR #prime, OSSL_PKEY_PARAM_RSA_EXPONENT #prime, \
144+
OSSL_PKEY_PARAM_RSA_COEFFICIENT #coefficient \
145+
}
146+
147+
constexpr std::array<RsaOtherPrimeParamNames, 8> kRsaOtherPrimeParamNames = {{
148+
RSA_OTHER_PRIME_PARAM_NAMES(3, 2),
149+
RSA_OTHER_PRIME_PARAM_NAMES(4, 3),
150+
RSA_OTHER_PRIME_PARAM_NAMES(5, 4),
151+
RSA_OTHER_PRIME_PARAM_NAMES(6, 5),
152+
RSA_OTHER_PRIME_PARAM_NAMES(7, 6),
153+
RSA_OTHER_PRIME_PARAM_NAMES(8, 7),
154+
RSA_OTHER_PRIME_PARAM_NAMES(9, 8),
155+
RSA_OTHER_PRIME_PARAM_NAMES(10, 9),
156+
}};
157+
158+
#undef RSA_OTHER_PRIME_PARAM_NAMES
134159
#endif
135160

136161
static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON =
@@ -3082,6 +3107,19 @@ EVPKeyPointer EVPKeyPointer::NewRSA(const Rsa& rsa) {
30823107
bld.get(), OSSL_PKEY_PARAM_RSA_COEFFICIENT1, private_key.qi) != 1) {
30833108
return {};
30843109
}
3110+
3111+
const auto other_prime_infos = rsa.getOtherPrimeInfos();
3112+
if (other_prime_infos.size() > kRsaOtherPrimeParamNames.size()) return {};
3113+
for (size_t i = 0; i < other_prime_infos.size(); i++) {
3114+
const auto& info = other_prime_infos[i];
3115+
const auto& names = kRsaOtherPrimeParamNames[i];
3116+
if (info.r == nullptr || info.d == nullptr || info.t == nullptr ||
3117+
OSSL_PARAM_BLD_push_BN(bld.get(), names.factor, info.r) != 1 ||
3118+
OSSL_PARAM_BLD_push_BN(bld.get(), names.exponent, info.d) != 1 ||
3119+
OSSL_PARAM_BLD_push_BN(bld.get(), names.coefficient, info.t) != 1) {
3120+
return {};
3121+
}
3122+
}
30853123
selection = EVP_PKEY_KEYPAIR;
30863124
}
30873125

@@ -6135,6 +6173,11 @@ DataPointer CipherImpl(const EVPKeyPointer& key,
61356173
}
61366174
} // namespace
61376175

6176+
Rsa::OtherPrimeInfoPointer::OtherPrimeInfoPointer(BignumPointer&& r,
6177+
BignumPointer&& d,
6178+
BignumPointer&& t)
6179+
: r(r.release()), d(d.release()), t(t.release()) {}
6180+
61386181
#if NCRYPTO_USE_OPENSSL3_PROVIDER
61396182
namespace {
61406183
int DigestAlgorithmIdentifierToNid(const unsigned char* data, size_t size) {
@@ -6363,6 +6406,19 @@ Rsa::Rsa(const EVP_PKEY* pkey) : Rsa() {
63636406
return;
63646407
}
63656408

6409+
for (const auto& names : kRsaOtherPrimeParamNames) {
6410+
OtherPrimeInfoPointer info;
6411+
if (!GetOptionalPKeyBnParam(pkey, names.factor, &info.r) ||
6412+
!GetOptionalPKeyBnParam(pkey, names.exponent, &info.d) ||
6413+
!GetOptionalPKeyBnParam(pkey, names.coefficient, &info.t)) {
6414+
return;
6415+
}
6416+
6417+
if (!info.r && !info.d && !info.t) break;
6418+
if (!info.r || !info.d || !info.t) return;
6419+
other_prime_infos_.push_back(std::move(info));
6420+
}
6421+
63666422
if (type == EVP_PKEY_RSA_PSS) {
63676423
MarkPopErrorOnReturn pop_errors;
63686424
PssParams params;
@@ -6401,6 +6457,35 @@ const Rsa::PrivateKey Rsa::getPrivateKey() const {
64016457
#endif
64026458
}
64036459

6460+
const Rsa::OtherPrimeInfos Rsa::getOtherPrimeInfos() const {
6461+
OtherPrimeInfos infos;
6462+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
6463+
infos.reserve(other_prime_infos_.size());
6464+
for (const auto& info : other_prime_infos_) {
6465+
infos.push_back({info.r.get(), info.d.get(), info.t.get()});
6466+
}
6467+
#elif NCRYPTO_USE_LEGACY_OPENSSL
6468+
if (rsa_ == nullptr) return infos;
6469+
const int count = RSA_get_multi_prime_extra_count(rsa_);
6470+
if (count <= 0) return infos;
6471+
6472+
std::vector<const BIGNUM*> factors(count);
6473+
std::vector<const BIGNUM*> exponents(count);
6474+
std::vector<const BIGNUM*> coefficients(count);
6475+
if (RSA_get0_multi_prime_factors(rsa_, factors.data()) != 1 ||
6476+
RSA_get0_multi_prime_crt_params(
6477+
rsa_, exponents.data(), coefficients.data()) != 1) {
6478+
return {};
6479+
}
6480+
6481+
infos.reserve(count);
6482+
for (int i = 0; i < count; i++) {
6483+
infos.push_back({factors[i], exponents[i], coefficients[i]});
6484+
}
6485+
#endif
6486+
return infos;
6487+
}
6488+
64046489
const std::optional<Rsa::PssParams> Rsa::getPssParams() const {
64056490
#if NCRYPTO_USE_OPENSSL3_PROVIDER
64066491
return pss_params_;
@@ -6502,15 +6587,20 @@ bool Rsa::setPrivateKey(BignumPointer&& d,
65026587
BignumPointer&& p,
65036588
BignumPointer&& dp,
65046589
BignumPointer&& dq,
6505-
BignumPointer&& qi) {
6590+
BignumPointer&& qi,
6591+
OtherPrimeInfoPointers&& other_prime_infos) {
65066592
#if NCRYPTO_USE_OPENSSL3_PROVIDER
65076593
if (!d || !q || !p || !dp || !dq || !qi) return false;
6594+
for (const auto& info : other_prime_infos) {
6595+
if (!info.r || !info.d || !info.t) return false;
6596+
}
65086597
d_.reset(d.release());
65096598
q_.reset(q.release());
65106599
p_.reset(p.release());
65116600
dp_.reset(dp.release());
65126601
dq_.reset(dq.release());
65136602
qi_.reset(qi.release());
6603+
other_prime_infos_ = std::move(other_prime_infos);
65146604
rsa_ = n_ != nullptr && e_ != nullptr;
65156605
return rsa_;
65166606
#else
@@ -6532,6 +6622,37 @@ bool Rsa::setPrivateKey(BignumPointer&& d,
65326622
dp.release();
65336623
dq.release();
65346624
qi.release();
6625+
6626+
#if NCRYPTO_USE_LEGACY_OPENSSL
6627+
if (!other_prime_infos.empty()) {
6628+
std::vector<BIGNUM*> factors;
6629+
std::vector<BIGNUM*> exponents;
6630+
std::vector<BIGNUM*> coefficients;
6631+
factors.reserve(other_prime_infos.size());
6632+
exponents.reserve(other_prime_infos.size());
6633+
coefficients.reserve(other_prime_infos.size());
6634+
for (const auto& info : other_prime_infos) {
6635+
if (!info.r || !info.d || !info.t) return false;
6636+
factors.push_back(info.r.get());
6637+
exponents.push_back(info.d.get());
6638+
coefficients.push_back(info.t.get());
6639+
}
6640+
if (RSA_set0_multi_prime_params(const_cast<RSA*>(rsa_),
6641+
factors.data(),
6642+
exponents.data(),
6643+
coefficients.data(),
6644+
static_cast<int>(factors.size())) != 1) {
6645+
return false;
6646+
}
6647+
for (auto& info : other_prime_infos) {
6648+
info.r.release();
6649+
info.d.release();
6650+
info.t.release();
6651+
}
6652+
}
6653+
#else
6654+
if (!other_prime_infos.empty()) return false;
6655+
#endif
65356656
return true;
65366657
#endif
65376658
}

deps/ncrypto/ncrypto.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,23 @@ class Rsa final {
724724
const BIGNUM* dq;
725725
const BIGNUM* qi;
726726
};
727+
struct OtherPrimeInfo {
728+
const BIGNUM* r;
729+
const BIGNUM* d;
730+
const BIGNUM* t;
731+
};
732+
struct OtherPrimeInfoPointer {
733+
OtherPrimeInfoPointer() = default;
734+
OtherPrimeInfoPointer(BignumPointer&& r,
735+
BignumPointer&& d,
736+
BignumPointer&& t);
737+
738+
DeleteFnPtr<BIGNUM, BN_clear_free> r;
739+
DeleteFnPtr<BIGNUM, BN_clear_free> d;
740+
DeleteFnPtr<BIGNUM, BN_clear_free> t;
741+
};
742+
using OtherPrimeInfos = std::vector<OtherPrimeInfo>;
743+
using OtherPrimeInfoPointers = std::vector<OtherPrimeInfoPointer>;
727744
struct PssParams {
728745
std::string_view digest = "sha1";
729746
std::optional<std::string_view> mgf1_digest = "sha1";
@@ -732,6 +749,7 @@ class Rsa final {
732749

733750
const PublicKey getPublicKey() const;
734751
const PrivateKey getPrivateKey() const;
752+
const OtherPrimeInfos getOtherPrimeInfos() const;
735753
const std::optional<PssParams> getPssParams() const;
736754

737755
bool setPublicKey(BignumPointer&& n, BignumPointer&& e);
@@ -740,7 +758,8 @@ class Rsa final {
740758
BignumPointer&& p,
741759
BignumPointer&& dp,
742760
BignumPointer&& dq,
743-
BignumPointer&& qi);
761+
BignumPointer&& qi,
762+
OtherPrimeInfoPointers&& other_prime_infos = {});
744763

745764
using CipherParams = Cipher::CipherParams;
746765

@@ -765,6 +784,7 @@ class Rsa final {
765784
DeleteFnPtr<BIGNUM, BN_clear_free> dp_;
766785
DeleteFnPtr<BIGNUM, BN_clear_free> dq_;
767786
DeleteFnPtr<BIGNUM, BN_clear_free> qi_;
787+
OtherPrimeInfoPointers other_prime_infos_;
768788
std::optional<PssParams> pss_params_;
769789
#else
770790
OSSL3_CONST RSA* rsa_;

src/crypto/crypto_rsa.cc

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ using ncrypto::EVPKeyPointer;
2222
#if NCRYPTO_USE_LEGACY_KEY_TYPES
2323
using ncrypto::RSAPointer;
2424
#endif
25+
using v8::Array;
2526
using v8::ArrayBuffer;
2627
using v8::BackingStoreInitializationMode;
2728
using v8::FunctionCallbackInfo;
@@ -39,6 +40,8 @@ using v8::Value;
3940

4041
namespace crypto {
4142
namespace {
43+
constexpr uint32_t kMaxRsaOtherPrimeInfos = 8;
44+
4245
bool IsRsaPssDigestEncodable(const Digest& digest) {
4346
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4447
const int nid = EVP_MD_type(digest.get());
@@ -339,6 +342,29 @@ bool ExportJWKRsaKey(Environment* env,
339342
.IsNothing()) {
340343
return false;
341344
}
345+
346+
const auto other_prime_infos = rsa.getOtherPrimeInfos();
347+
if (!other_prime_infos.empty()) {
348+
const uint32_t count = static_cast<uint32_t>(other_prime_infos.size());
349+
Local<Array> oth = Array::New(env->isolate(), count);
350+
for (uint32_t i = 0; i < count; i++) {
351+
const auto& info = other_prime_infos[i];
352+
Local<Object> item = Object::New(env->isolate());
353+
if (SetEncodedValue(env, item, env->jwk_r_string(), info.r)
354+
.IsNothing() ||
355+
SetEncodedValue(env, item, env->jwk_d_string(), info.d)
356+
.IsNothing() ||
357+
SetEncodedValue(env, item, env->jwk_t_string(), info.t)
358+
.IsNothing() ||
359+
!oth->Set(env->context(), i, item).FromMaybe(false)) {
360+
return false;
361+
}
362+
}
363+
if (!target->DefineOwnProperty(env->context(), env->jwk_oth_string(), oth)
364+
.FromMaybe(false)) {
365+
return false;
366+
}
367+
}
342368
}
343369

344370
return true;
@@ -348,12 +374,13 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {
348374
Local<Value> n_value;
349375
Local<Value> e_value;
350376
Local<Value> d_value;
377+
Local<Value> oth_value;
351378

352379
if (!jwk->Get(env->context(), env->jwk_n_string()).ToLocal(&n_value) ||
353380
!jwk->Get(env->context(), env->jwk_e_string()).ToLocal(&e_value) ||
354381
!jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value) ||
355-
!n_value->IsString() ||
356-
!e_value->IsString()) {
382+
!jwk->Get(env->context(), env->jwk_oth_string()).ToLocal(&oth_value) ||
383+
!n_value->IsString() || !e_value->IsString()) {
357384
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
358385
return {};
359386
}
@@ -364,6 +391,10 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {
364391
}
365392

366393
KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic;
394+
if (type == kKeyTypePublic && !oth_value->IsUndefined()) {
395+
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
396+
return {};
397+
}
367398

368399
#if NCRYPTO_USE_OPENSSL3_PROVIDER
369400
ncrypto::Rsa rsa_view;
@@ -417,19 +448,79 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {
417448
ByteSource dq = ByteSource::FromEncodedString(env, dq_value.As<String>());
418449
ByteSource qi = ByteSource::FromEncodedString(env, qi_value.As<String>());
419450

420-
if (!rsa_view.setPrivateKey(
421-
d.ToBN(), q.ToBN(), p.ToBN(), dp.ToBN(), dq.ToBN(), qi.ToBN())) {
451+
ncrypto::Rsa::OtherPrimeInfoPointers other_prime_infos;
452+
if (!oth_value->IsUndefined()) {
453+
if (!oth_value->IsArray()) {
454+
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
455+
return {};
456+
}
457+
458+
Local<Array> oth = oth_value.As<Array>();
459+
const uint32_t length = oth->Length();
460+
if (length == 0 || length > kMaxRsaOtherPrimeInfos) {
461+
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
462+
return {};
463+
}
464+
other_prime_infos.reserve(length);
465+
for (uint32_t i = 0; i < length; i++) {
466+
Local<Value> item_value;
467+
Local<Value> r_value;
468+
Local<Value> other_d_value;
469+
Local<Value> t_value;
470+
if (!oth->Get(env->context(), i).ToLocal(&item_value) ||
471+
!item_value->IsObject()) {
472+
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
473+
return {};
474+
}
475+
476+
Local<Object> item = item_value.As<Object>();
477+
if (!item->Get(env->context(), env->jwk_r_string()).ToLocal(&r_value) ||
478+
!item->Get(env->context(), env->jwk_d_string())
479+
.ToLocal(&other_d_value) ||
480+
!item->Get(env->context(), env->jwk_t_string()).ToLocal(&t_value) ||
481+
!r_value->IsString() || !other_d_value->IsString() ||
482+
!t_value->IsString()) {
483+
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
484+
return {};
485+
}
486+
487+
other_prime_infos.push_back({
488+
ByteSource::FromEncodedString(env, r_value.As<String>()).ToBN(),
489+
ByteSource::FromEncodedString(env, other_d_value.As<String>())
490+
.ToBN(),
491+
ByteSource::FromEncodedString(env, t_value.As<String>()).ToBN(),
492+
});
493+
}
494+
}
495+
496+
if (!rsa_view.setPrivateKey(d.ToBN(),
497+
q.ToBN(),
498+
p.ToBN(),
499+
dp.ToBN(),
500+
dq.ToBN(),
501+
qi.ToBN(),
502+
std::move(other_prime_infos))) {
422503
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
423504
return {};
424505
}
425506

426-
// Verify that n == p * q.
507+
// Verify that n is the product of all prime factors.
427508
const auto& pub = rsa_view.getPublicKey();
428509
const auto& priv = rsa_view.getPrivateKey();
429-
auto pq = BignumPointer::New();
510+
auto product = BignumPointer::New();
430511
BN_CTX* ctx = BN_CTX_new();
431-
bool n_valid = ctx && pq && BN_mul(pq.get(), priv.p, priv.q, ctx) == 1 &&
432-
BN_cmp(pq.get(), pub.n) == 0;
512+
bool n_valid =
513+
ctx && product && BN_mul(product.get(), priv.p, priv.q, ctx) == 1;
514+
for (const auto& info : rsa_view.getOtherPrimeInfos()) {
515+
auto next = BignumPointer::New();
516+
if (!n_valid || !next ||
517+
BN_mul(next.get(), product.get(), info.r, ctx) != 1) {
518+
n_valid = false;
519+
break;
520+
}
521+
product = std::move(next);
522+
}
523+
n_valid = n_valid && BN_cmp(product.get(), pub.n) == 0;
433524
BN_CTX_free(ctx);
434525
if (!n_valid) {
435526
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");

0 commit comments

Comments
 (0)