Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 122 additions & 1 deletion deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,31 @@ struct OpenSSLBufferDeleter {
};
using OpenSSLBufferPointer =
std::unique_ptr<unsigned char, OpenSSLBufferDeleter>;

struct RsaOtherPrimeParamNames {
const char* factor;
const char* exponent;
const char* coefficient;
};

#define RSA_OTHER_PRIME_PARAM_NAMES(prime, coefficient) \
{ \
OSSL_PKEY_PARAM_RSA_FACTOR #prime, OSSL_PKEY_PARAM_RSA_EXPONENT #prime, \
OSSL_PKEY_PARAM_RSA_COEFFICIENT #coefficient \
}

constexpr std::array<RsaOtherPrimeParamNames, 8> kRsaOtherPrimeParamNames = {{
RSA_OTHER_PRIME_PARAM_NAMES(3, 2),
RSA_OTHER_PRIME_PARAM_NAMES(4, 3),
RSA_OTHER_PRIME_PARAM_NAMES(5, 4),
RSA_OTHER_PRIME_PARAM_NAMES(6, 5),
RSA_OTHER_PRIME_PARAM_NAMES(7, 6),
RSA_OTHER_PRIME_PARAM_NAMES(8, 7),
RSA_OTHER_PRIME_PARAM_NAMES(9, 8),
RSA_OTHER_PRIME_PARAM_NAMES(10, 9),
}};

#undef RSA_OTHER_PRIME_PARAM_NAMES
#endif

static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON =
Expand Down Expand Up @@ -3081,6 +3106,19 @@ EVPKeyPointer EVPKeyPointer::NewRSA(const Rsa& rsa) {
bld.get(), OSSL_PKEY_PARAM_RSA_COEFFICIENT1, private_key.qi) != 1) {
return {};
}

const auto other_prime_infos = rsa.getOtherPrimeInfos();
if (other_prime_infos.size() > kRsaOtherPrimeParamNames.size()) return {};
for (size_t i = 0; i < other_prime_infos.size(); i++) {
const auto& info = other_prime_infos[i];
const auto& names = kRsaOtherPrimeParamNames[i];
if (info.r == nullptr || info.d == nullptr || info.t == nullptr ||
OSSL_PARAM_BLD_push_BN(bld.get(), names.factor, info.r) != 1 ||
OSSL_PARAM_BLD_push_BN(bld.get(), names.exponent, info.d) != 1 ||
OSSL_PARAM_BLD_push_BN(bld.get(), names.coefficient, info.t) != 1) {
return {};
}
}
selection = EVP_PKEY_KEYPAIR;
}

Expand Down Expand Up @@ -6134,6 +6172,11 @@ DataPointer CipherImpl(const EVPKeyPointer& key,
}
} // namespace

Rsa::OtherPrimeInfoPointer::OtherPrimeInfoPointer(BignumPointer&& r,
BignumPointer&& d,
BignumPointer&& t)
: r(r.release()), d(d.release()), t(t.release()) {}

#if NCRYPTO_USE_OPENSSL3_PROVIDER
namespace {
int DigestAlgorithmIdentifierToNid(const unsigned char* data, size_t size) {
Expand Down Expand Up @@ -6362,6 +6405,19 @@ Rsa::Rsa(const EVP_PKEY* pkey) : Rsa() {
return;
}

for (const auto& names : kRsaOtherPrimeParamNames) {
OtherPrimeInfoPointer info;
if (!GetOptionalPKeyBnParam(pkey, names.factor, &info.r) ||
!GetOptionalPKeyBnParam(pkey, names.exponent, &info.d) ||
!GetOptionalPKeyBnParam(pkey, names.coefficient, &info.t)) {
return;
}

if (!info.r && !info.d && !info.t) break;
if (!info.r || !info.d || !info.t) return;
other_prime_infos_.push_back(std::move(info));
}

if (type == EVP_PKEY_RSA_PSS) {
MarkPopErrorOnReturn pop_errors;
PssParams params;
Expand Down Expand Up @@ -6400,6 +6456,35 @@ const Rsa::PrivateKey Rsa::getPrivateKey() const {
#endif
}

const Rsa::OtherPrimeInfos Rsa::getOtherPrimeInfos() const {
OtherPrimeInfos infos;
#if NCRYPTO_USE_OPENSSL3_PROVIDER
infos.reserve(other_prime_infos_.size());
for (const auto& info : other_prime_infos_) {
infos.push_back({info.r.get(), info.d.get(), info.t.get()});
}
#elif NCRYPTO_USE_LEGACY_OPENSSL
if (rsa_ == nullptr) return infos;
const int count = RSA_get_multi_prime_extra_count(rsa_);
if (count <= 0) return infos;

std::vector<const BIGNUM*> factors(count);
std::vector<const BIGNUM*> exponents(count);
std::vector<const BIGNUM*> coefficients(count);
if (RSA_get0_multi_prime_factors(rsa_, factors.data()) != 1 ||
RSA_get0_multi_prime_crt_params(
rsa_, exponents.data(), coefficients.data()) != 1) {
return {};
}

infos.reserve(count);
for (int i = 0; i < count; i++) {
infos.push_back({factors[i], exponents[i], coefficients[i]});
}
#endif
return infos;
}

const std::optional<Rsa::PssParams> Rsa::getPssParams() const {
#if NCRYPTO_USE_OPENSSL3_PROVIDER
return pss_params_;
Expand Down Expand Up @@ -6501,15 +6586,20 @@ bool Rsa::setPrivateKey(BignumPointer&& d,
BignumPointer&& p,
BignumPointer&& dp,
BignumPointer&& dq,
BignumPointer&& qi) {
BignumPointer&& qi,
OtherPrimeInfoPointers&& other_prime_infos) {
#if NCRYPTO_USE_OPENSSL3_PROVIDER
if (!d || !q || !p || !dp || !dq || !qi) return false;
for (const auto& info : other_prime_infos) {
if (!info.r || !info.d || !info.t) return false;
}
d_.reset(d.release());
q_.reset(q.release());
p_.reset(p.release());
dp_.reset(dp.release());
dq_.reset(dq.release());
qi_.reset(qi.release());
other_prime_infos_ = std::move(other_prime_infos);
rsa_ = n_ != nullptr && e_ != nullptr;
return rsa_;
#else
Expand All @@ -6531,6 +6621,37 @@ bool Rsa::setPrivateKey(BignumPointer&& d,
dp.release();
dq.release();
qi.release();

#if NCRYPTO_USE_LEGACY_OPENSSL
if (!other_prime_infos.empty()) {
std::vector<BIGNUM*> factors;
std::vector<BIGNUM*> exponents;
std::vector<BIGNUM*> coefficients;
factors.reserve(other_prime_infos.size());
exponents.reserve(other_prime_infos.size());
coefficients.reserve(other_prime_infos.size());
for (const auto& info : other_prime_infos) {
if (!info.r || !info.d || !info.t) return false;
factors.push_back(info.r.get());
exponents.push_back(info.d.get());
coefficients.push_back(info.t.get());
}
if (RSA_set0_multi_prime_params(const_cast<RSA*>(rsa_),
factors.data(),
exponents.data(),
coefficients.data(),
static_cast<int>(factors.size())) != 1) {
return false;
}
for (auto& info : other_prime_infos) {
info.r.release();
info.d.release();
info.t.release();
}
}
#else
if (!other_prime_infos.empty()) return false;
#endif
return true;
#endif
}
Expand Down
22 changes: 21 additions & 1 deletion deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,23 @@ class Rsa final {
const BIGNUM* dq;
const BIGNUM* qi;
};
struct OtherPrimeInfo {
const BIGNUM* r;
const BIGNUM* d;
const BIGNUM* t;
};
struct OtherPrimeInfoPointer {
OtherPrimeInfoPointer() = default;
OtherPrimeInfoPointer(BignumPointer&& r,
BignumPointer&& d,
BignumPointer&& t);

DeleteFnPtr<BIGNUM, BN_clear_free> r;
DeleteFnPtr<BIGNUM, BN_clear_free> d;
DeleteFnPtr<BIGNUM, BN_clear_free> t;
};
using OtherPrimeInfos = std::vector<OtherPrimeInfo>;
using OtherPrimeInfoPointers = std::vector<OtherPrimeInfoPointer>;
struct PssParams {
std::string_view digest = "sha1";
std::optional<std::string_view> mgf1_digest = "sha1";
Expand All @@ -732,6 +749,7 @@ class Rsa final {

const PublicKey getPublicKey() const;
const PrivateKey getPrivateKey() const;
const OtherPrimeInfos getOtherPrimeInfos() const;
const std::optional<PssParams> getPssParams() const;

bool setPublicKey(BignumPointer&& n, BignumPointer&& e);
Expand All @@ -740,7 +758,8 @@ class Rsa final {
BignumPointer&& p,
BignumPointer&& dp,
BignumPointer&& dq,
BignumPointer&& qi);
BignumPointer&& qi,
OtherPrimeInfoPointers&& other_prime_infos = {});

using CipherParams = Cipher::CipherParams;

Expand All @@ -765,6 +784,7 @@ class Rsa final {
DeleteFnPtr<BIGNUM, BN_clear_free> dp_;
DeleteFnPtr<BIGNUM, BN_clear_free> dq_;
DeleteFnPtr<BIGNUM, BN_clear_free> qi_;
OtherPrimeInfoPointers other_prime_infos_;
std::optional<PssParams> pss_params_;
#else
OSSL3_CONST RSA* rsa_;
Expand Down
107 changes: 99 additions & 8 deletions src/crypto/crypto_rsa.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ using ncrypto::EVPKeyPointer;
#if NCRYPTO_USE_LEGACY_KEY_TYPES
using ncrypto::RSAPointer;
#endif
using v8::Array;
using v8::ArrayBuffer;
using v8::BackingStoreInitializationMode;
using v8::FunctionCallbackInfo;
Expand All @@ -39,6 +40,8 @@ using v8::Value;

namespace crypto {
namespace {
constexpr uint32_t kMaxRsaOtherPrimeInfos = 8;

bool IsRsaPssDigestEncodable(const Digest& digest) {
#if NCRYPTO_USE_OPENSSL3_PROVIDER
const int nid = EVP_MD_type(digest.get());
Expand Down Expand Up @@ -339,6 +342,29 @@ bool ExportJWKRsaKey(Environment* env,
.IsNothing()) {
return false;
}

const auto other_prime_infos = rsa.getOtherPrimeInfos();
if (!other_prime_infos.empty()) {
const uint32_t count = static_cast<uint32_t>(other_prime_infos.size());
Local<Array> oth = Array::New(env->isolate(), count);
for (uint32_t i = 0; i < count; i++) {
const auto& info = other_prime_infos[i];
Local<Object> item = Object::New(env->isolate());
if (SetEncodedValue(env, item, env->jwk_r_string(), info.r)
.IsNothing() ||
SetEncodedValue(env, item, env->jwk_d_string(), info.d)
.IsNothing() ||
SetEncodedValue(env, item, env->jwk_t_string(), info.t)
.IsNothing() ||
!oth->Set(env->context(), i, item).FromMaybe(false)) {
return false;
}
}
if (!target->DefineOwnProperty(env->context(), env->jwk_oth_string(), oth)
.FromMaybe(false)) {
return false;
}
}
}

return true;
Expand All @@ -348,12 +374,13 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {
Local<Value> n_value;
Local<Value> e_value;
Local<Value> d_value;
Local<Value> oth_value;

if (!jwk->Get(env->context(), env->jwk_n_string()).ToLocal(&n_value) ||
!jwk->Get(env->context(), env->jwk_e_string()).ToLocal(&e_value) ||
!jwk->Get(env->context(), env->jwk_d_string()).ToLocal(&d_value) ||
!n_value->IsString() ||
!e_value->IsString()) {
!jwk->Get(env->context(), env->jwk_oth_string()).ToLocal(&oth_value) ||
!n_value->IsString() || !e_value->IsString()) {
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
return {};
}
Expand All @@ -364,6 +391,10 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Local<Object> jwk) {
}

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

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

if (!rsa_view.setPrivateKey(
d.ToBN(), q.ToBN(), p.ToBN(), dp.ToBN(), dq.ToBN(), qi.ToBN())) {
ncrypto::Rsa::OtherPrimeInfoPointers other_prime_infos;
if (!oth_value->IsUndefined()) {
if (!oth_value->IsArray()) {
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
return {};
}

Local<Array> oth = oth_value.As<Array>();
const uint32_t length = oth->Length();
if (length == 0 || length > kMaxRsaOtherPrimeInfos) {
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
return {};
}
other_prime_infos.reserve(length);
for (uint32_t i = 0; i < length; i++) {
Local<Value> item_value;
Local<Value> r_value;
Local<Value> other_d_value;
Local<Value> t_value;
if (!oth->Get(env->context(), i).ToLocal(&item_value) ||
!item_value->IsObject()) {
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
return {};
}

Local<Object> item = item_value.As<Object>();
if (!item->Get(env->context(), env->jwk_r_string()).ToLocal(&r_value) ||
!item->Get(env->context(), env->jwk_d_string())
.ToLocal(&other_d_value) ||
!item->Get(env->context(), env->jwk_t_string()).ToLocal(&t_value) ||
!r_value->IsString() || !other_d_value->IsString() ||
!t_value->IsString()) {
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
return {};
}

other_prime_infos.push_back({
ByteSource::FromEncodedString(env, r_value.As<String>()).ToBN(),
ByteSource::FromEncodedString(env, other_d_value.As<String>())
.ToBN(),
ByteSource::FromEncodedString(env, t_value.As<String>()).ToBN(),
});
}
}

if (!rsa_view.setPrivateKey(d.ToBN(),
q.ToBN(),
p.ToBN(),
dp.ToBN(),
dq.ToBN(),
qi.ToBN(),
std::move(other_prime_infos))) {
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
return {};
}

// Verify that n == p * q.
// Verify that n is the product of all prime factors.
const auto& pub = rsa_view.getPublicKey();
const auto& priv = rsa_view.getPrivateKey();
auto pq = BignumPointer::New();
auto product = BignumPointer::New();
BN_CTX* ctx = BN_CTX_new();
bool n_valid = ctx && pq && BN_mul(pq.get(), priv.p, priv.q, ctx) == 1 &&
BN_cmp(pq.get(), pub.n) == 0;
bool n_valid =
ctx && product && BN_mul(product.get(), priv.p, priv.q, ctx) == 1;
for (const auto& info : rsa_view.getOtherPrimeInfos()) {
auto next = BignumPointer::New();
if (!n_valid || !next ||
BN_mul(next.get(), product.get(), info.r, ctx) != 1) {
n_valid = false;
break;
}
product = std::move(next);
}
n_valid = n_valid && BN_cmp(product.get(), pub.n) == 0;
BN_CTX_free(ctx);
if (!n_valid) {
THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key");
Expand Down
Loading
Loading