Skip to content

Commit c4681d5

Browse files
committedSep 23, 2024
src: move evp stuff to ncrypto
PR-URL: #54911 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 6a6c957 commit c4681d5

11 files changed

+492
-262
lines changed
 

‎deps/ncrypto/ncrypto.cc

+209
Original file line numberDiff line numberDiff line change
@@ -1425,4 +1425,213 @@ DataPointer pbkdf2(const EVP_MD* md,
14251425
return {};
14261426
}
14271427

1428+
// ============================================================================
1429+
1430+
EVPKeyPointer EVPKeyPointer::New() {
1431+
return EVPKeyPointer(EVP_PKEY_new());
1432+
}
1433+
1434+
EVPKeyPointer EVPKeyPointer::NewRawPublic(int id, const Buffer<const unsigned char>& data) {
1435+
if (id == 0) return {};
1436+
return EVPKeyPointer(EVP_PKEY_new_raw_public_key(id, nullptr, data.data, data.len));
1437+
}
1438+
1439+
EVPKeyPointer EVPKeyPointer::NewRawPrivate(int id, const Buffer<const unsigned char>& data) {
1440+
if (id == 0) return {};
1441+
return EVPKeyPointer(EVP_PKEY_new_raw_private_key(id, nullptr, data.data, data.len));
1442+
}
1443+
1444+
EVPKeyPointer::EVPKeyPointer(EVP_PKEY* pkey) : pkey_(pkey) {}
1445+
1446+
EVPKeyPointer::EVPKeyPointer(EVPKeyPointer&& other) noexcept
1447+
: pkey_(other.release()) {}
1448+
1449+
EVPKeyPointer& EVPKeyPointer::operator=(EVPKeyPointer&& other) noexcept {
1450+
if (this == &other) return *this;
1451+
this->~EVPKeyPointer();
1452+
return *new (this) EVPKeyPointer(std::move(other));
1453+
}
1454+
1455+
EVPKeyPointer::~EVPKeyPointer() { reset(); }
1456+
1457+
void EVPKeyPointer::reset(EVP_PKEY* pkey) {
1458+
pkey_.reset(pkey);
1459+
}
1460+
1461+
EVP_PKEY* EVPKeyPointer::release() {
1462+
return pkey_.release();
1463+
}
1464+
1465+
int EVPKeyPointer::id(const EVP_PKEY* key) {
1466+
if (key == nullptr) return 0;
1467+
return EVP_PKEY_id(key);
1468+
}
1469+
1470+
int EVPKeyPointer::base_id(const EVP_PKEY* key) {
1471+
if (key == nullptr) return 0;
1472+
return EVP_PKEY_base_id(key);
1473+
}
1474+
1475+
int EVPKeyPointer::id() const {
1476+
return id(get());
1477+
}
1478+
1479+
int EVPKeyPointer::base_id() const {
1480+
return base_id(get());
1481+
}
1482+
1483+
int EVPKeyPointer::bits() const {
1484+
if (get() == nullptr) return 0;
1485+
return EVP_PKEY_bits(get());
1486+
}
1487+
1488+
size_t EVPKeyPointer::size() const {
1489+
if (get() == nullptr) return 0;
1490+
return EVP_PKEY_size(get());
1491+
}
1492+
1493+
EVPKeyCtxPointer EVPKeyPointer::newCtx() const {
1494+
if (!pkey_) return {};
1495+
return EVPKeyCtxPointer(EVP_PKEY_CTX_new(get(), nullptr));
1496+
}
1497+
1498+
size_t EVPKeyPointer::rawPublicKeySize() const {
1499+
if (!pkey_) return 0;
1500+
size_t len = 0;
1501+
if (EVP_PKEY_get_raw_public_key(get(), nullptr, &len) == 1) return len;
1502+
return 0;
1503+
}
1504+
1505+
size_t EVPKeyPointer::rawPrivateKeySize() const {
1506+
if (!pkey_) return 0;
1507+
size_t len = 0;
1508+
if (EVP_PKEY_get_raw_private_key(get(), nullptr, &len) == 1) return len;
1509+
return 0;
1510+
}
1511+
1512+
DataPointer EVPKeyPointer::rawPublicKey() const {
1513+
if (!pkey_) return {};
1514+
if (auto data = DataPointer::Alloc(rawPublicKeySize())) {
1515+
const Buffer<unsigned char> buf = data;
1516+
size_t len = data.size();
1517+
if (EVP_PKEY_get_raw_public_key(get(),
1518+
buf.data,
1519+
&len) != 1) return {};
1520+
return data;
1521+
}
1522+
return {};
1523+
}
1524+
1525+
DataPointer EVPKeyPointer::rawPrivateKey() const {
1526+
if (!pkey_) return {};
1527+
if (auto data = DataPointer::Alloc(rawPrivateKeySize())) {
1528+
const Buffer<unsigned char> buf = data;
1529+
size_t len = data.size();
1530+
if (EVP_PKEY_get_raw_private_key(get(),
1531+
buf.data,
1532+
&len) != 1) return {};
1533+
return data;
1534+
}
1535+
return {};
1536+
}
1537+
1538+
BIOPointer EVPKeyPointer::derPublicKey() const {
1539+
if (!pkey_) return {};
1540+
auto bio = BIOPointer::NewMem();
1541+
if (!bio) return {};
1542+
if (!i2d_PUBKEY_bio(bio.get(), get())) return {};
1543+
return bio;
1544+
}
1545+
1546+
namespace {
1547+
EVPKeyPointer::ParseKeyResult TryParsePublicKeyInner(
1548+
const BIOPointer& bp,
1549+
const char* name,
1550+
auto&& parse) {
1551+
if (!bp.resetBio()) {
1552+
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::FAILED);
1553+
}
1554+
unsigned char* der_data;
1555+
long der_len;
1556+
1557+
// This skips surrounding data and decodes PEM to DER.
1558+
{
1559+
MarkPopErrorOnReturn mark_pop_error_on_return;
1560+
if (PEM_bytes_read_bio(&der_data, &der_len, nullptr, name,
1561+
bp.get(), nullptr, nullptr) != 1)
1562+
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::NOT_RECOGNIZED);
1563+
}
1564+
DataPointer data(der_data, der_len);
1565+
1566+
// OpenSSL might modify the pointer, so we need to make a copy before parsing.
1567+
const unsigned char* p = der_data;
1568+
EVPKeyPointer pkey(parse(&p, der_len));
1569+
if (!pkey) return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::FAILED);
1570+
return EVPKeyPointer::ParseKeyResult(std::move(pkey));
1571+
}
1572+
1573+
EVPKeyPointer::ParseKeyResult TryParsePublicKeyPEM(
1574+
const Buffer<const unsigned char>& buffer) {
1575+
auto bp = BIOPointer::New(buffer.data, buffer.len);
1576+
if (!bp)
1577+
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::FAILED);
1578+
1579+
// Try parsing as SubjectPublicKeyInfo (SPKI) first.
1580+
if (auto ret = TryParsePublicKeyInner(bp, "PUBLIC KEY",
1581+
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
1582+
return d2i_PUBKEY(nullptr, p, l);
1583+
})) {
1584+
return ret;
1585+
}
1586+
1587+
// Maybe it is PKCS#1.
1588+
if (auto ret = TryParsePublicKeyInner(bp, "RSA PUBLIC KEY",
1589+
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
1590+
return d2i_PublicKey(EVP_PKEY_RSA, nullptr, p, l);
1591+
})) {
1592+
return ret;
1593+
}
1594+
1595+
// X.509 fallback.
1596+
if (auto ret = TryParsePublicKeyInner(bp, "CERTIFICATE",
1597+
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
1598+
X509Pointer x509(d2i_X509(nullptr, p, l));
1599+
return x509 ? X509_get_pubkey(x509.get()) : nullptr;
1600+
})) {
1601+
return ret;
1602+
};
1603+
1604+
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::NOT_RECOGNIZED);
1605+
}
1606+
} // namespace
1607+
1608+
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
1609+
PKFormatType format,
1610+
PKEncodingType encoding,
1611+
const Buffer<const unsigned char>& buffer) {
1612+
if (format == PKFormatType::PEM) {
1613+
return TryParsePublicKeyPEM(buffer);
1614+
}
1615+
1616+
if (format != PKFormatType::DER) {
1617+
return ParseKeyResult(PKParseError::FAILED);
1618+
}
1619+
1620+
const unsigned char* start = buffer.data;
1621+
1622+
EVP_PKEY* key = nullptr;
1623+
1624+
if (encoding == PKEncodingType::PKCS1 &&
1625+
(key = d2i_PublicKey(EVP_PKEY_RSA, nullptr, &start, buffer.len))) {
1626+
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
1627+
}
1628+
1629+
if (encoding == PKEncodingType::SPKI &&
1630+
(key = d2i_PUBKEY(nullptr, &start, buffer.len))) {
1631+
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
1632+
}
1633+
1634+
return ParseKeyResult(PKParseError::FAILED);
1635+
}
1636+
14281637
} // namespace ncrypto

‎deps/ncrypto/ncrypto.h

+78-5
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,16 @@ class MarkPopErrorOnReturn final {
172172
CryptoErrorList* errors_;
173173
};
174174

175+
// TODO(@jasnell): Eventually replace with std::expected when we are able to
176+
// bump up to c++23.
175177
template <typename T, typename E>
176178
struct Result final {
179+
const bool has_value;
177180
T value;
178181
std::optional<E> error;
179-
Result(T&& value) : value(std::move(value)) {}
180-
Result(E&& error) : error(std::move(error)) {}
182+
Result(T&& value) : has_value(true), value(std::move(value)) {}
183+
Result(E&& error) : has_value(false), error(std::move(error)) {}
184+
inline operator bool() const { return has_value; }
181185
};
182186

183187
// ============================================================================
@@ -202,7 +206,6 @@ using ECGroupPointer = DeleteFnPtr<EC_GROUP, EC_GROUP_free>;
202206
using ECKeyPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
203207
using ECPointPointer = DeleteFnPtr<EC_POINT, EC_POINT_free>;
204208
using EVPKeyCtxPointer = DeleteFnPtr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
205-
using EVPKeyPointer = DeleteFnPtr<EVP_PKEY, EVP_PKEY_free>;
206209
using EVPMDCtxPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
207210
using HMACCtxPointer = DeleteFnPtr<HMAC_CTX, HMAC_CTX_free>;
208211
using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI, NETSCAPE_SPKI_free>;
@@ -252,9 +255,10 @@ class DataPointer final {
252255
Buffer<void> release();
253256

254257
// Returns a Buffer struct that is a view of the underlying data.
255-
inline operator const Buffer<void>() const {
258+
template <typename T = void>
259+
inline operator const Buffer<T>() const {
256260
return {
257-
.data = data_,
261+
.data = static_cast<T*>(data_),
258262
.len = len_,
259263
};
260264
}
@@ -359,6 +363,75 @@ class BignumPointer final {
359363
DeleteFnPtr<BIGNUM, BN_clear_free> bn_;
360364
};
361365

366+
class EVPKeyPointer final {
367+
public:
368+
static EVPKeyPointer New();
369+
static EVPKeyPointer NewRawPublic(int id, const Buffer<const unsigned char>& data);
370+
static EVPKeyPointer NewRawPrivate(int id, const Buffer<const unsigned char>& data);
371+
372+
enum class PKEncodingType {
373+
// RSAPublicKey / RSAPrivateKey according to PKCS#1.
374+
PKCS1,
375+
// PrivateKeyInfo or EncryptedPrivateKeyInfo according to PKCS#8.
376+
PKCS8,
377+
// SubjectPublicKeyInfo according to X.509.
378+
SPKI,
379+
// ECPrivateKey according to SEC1.
380+
SEC1
381+
};
382+
383+
enum class PKFormatType {
384+
DER,
385+
PEM,
386+
JWK
387+
};
388+
389+
enum class PKParseError {
390+
NOT_RECOGNIZED,
391+
NEED_PASSPHRASE,
392+
FAILED
393+
};
394+
using ParseKeyResult = Result<EVPKeyPointer, PKParseError>;
395+
396+
static ParseKeyResult TryParsePublicKey(
397+
PKFormatType format,
398+
PKEncodingType encoding,
399+
const Buffer<const unsigned char>& buffer);
400+
401+
EVPKeyPointer() = default;
402+
explicit EVPKeyPointer(EVP_PKEY* pkey);
403+
EVPKeyPointer(EVPKeyPointer&& other) noexcept;
404+
EVPKeyPointer& operator=(EVPKeyPointer&& other) noexcept;
405+
NCRYPTO_DISALLOW_COPY(EVPKeyPointer)
406+
~EVPKeyPointer();
407+
408+
inline bool operator==(std::nullptr_t) const noexcept { return pkey_ == nullptr; }
409+
inline operator bool() const { return pkey_ != nullptr; }
410+
inline EVP_PKEY* get() const { return pkey_.get(); }
411+
void reset(EVP_PKEY* pkey = nullptr);
412+
EVP_PKEY* release();
413+
414+
static int id(const EVP_PKEY* key);
415+
static int base_id(const EVP_PKEY* key);
416+
417+
int id() const;
418+
int base_id() const;
419+
int bits() const;
420+
size_t size() const;
421+
422+
size_t rawPublicKeySize() const;
423+
size_t rawPrivateKeySize() const;
424+
DataPointer rawPublicKey() const;
425+
DataPointer rawPrivateKey() const;
426+
427+
BIOPointer derPublicKey() const;
428+
429+
EVPKeyCtxPointer newCtx() const;
430+
431+
private:
432+
DeleteFnPtr<EVP_PKEY, EVP_PKEY_free> pkey_;
433+
};
434+
362435
class DHPointer final {
363436
public:
364437

‎src/crypto/crypto_cipher.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ bool PublicKeyCipher::Cipher(
995995
const ArrayBufferOrViewContents<unsigned char>& oaep_label,
996996
const ArrayBufferOrViewContents<unsigned char>& data,
997997
std::unique_ptr<BackingStore>* out) {
998-
EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
998+
EVPKeyCtxPointer ctx = pkey.newCtx();
999999
if (!ctx)
10001000
return false;
10011001
if (EVP_PKEY_cipher_init(ctx.get()) <= 0)
@@ -1071,7 +1071,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
10711071

10721072
if (EVP_PKEY_cipher == EVP_PKEY_decrypt &&
10731073
operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING) {
1074-
EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
1074+
EVPKeyCtxPointer ctx = pkey.newCtx();
10751075
CHECK(ctx);
10761076

10771077
if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) {

‎src/crypto/crypto_common.cc

+12-15
Original file line numberDiff line numberDiff line change
@@ -449,15 +449,14 @@ MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) {
449449
Local<Context> context = env->context();
450450
crypto::EVPKeyPointer key(raw_key);
451451

452-
int kid = EVP_PKEY_id(key.get());
453-
int bits = EVP_PKEY_bits(key.get());
452+
int kid = key.id();
454453
switch (kid) {
455454
case EVP_PKEY_DH:
456455
if (!Set<String>(context, info, env->type_string(), env->dh_string()) ||
457456
!Set<Integer>(context,
458-
info,
459-
env->size_string(),
460-
Integer::New(env->isolate(), bits))) {
457+
info,
458+
env->size_string(),
459+
Integer::New(env->isolate(), key.bits()))) {
461460
return MaybeLocal<Object>();
462461
}
463462
break;
@@ -473,18 +472,16 @@ MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) {
473472
} else {
474473
curve_name = OBJ_nid2sn(kid);
475474
}
476-
if (!Set<String>(context,
477-
info,
478-
env->type_string(),
479-
env->ecdh_string()) ||
475+
if (!Set<String>(
476+
context, info, env->type_string(), env->ecdh_string()) ||
480477
!Set<String>(context,
481-
info,
482-
env->name_string(),
483-
OneByteString(env->isolate(), curve_name)) ||
478+
info,
479+
env->name_string(),
480+
OneByteString(env->isolate(), curve_name)) ||
484481
!Set<Integer>(context,
485-
info,
486-
env->size_string(),
487-
Integer::New(env->isolate(), bits))) {
482+
info,
483+
env->size_string(),
484+
Integer::New(env->isolate(), key.bits()))) {
488485
return MaybeLocal<Object>();
489486
}
490487
}

0 commit comments

Comments
 (0)