Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: move evp stuff to ncrypto #54911

Closed
wants to merge 1 commit into from
Closed
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
209 changes: 209 additions & 0 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1425,4 +1425,213 @@ DataPointer pbkdf2(const EVP_MD* md,
return {};
}

// ============================================================================

EVPKeyPointer EVPKeyPointer::New() {
return EVPKeyPointer(EVP_PKEY_new());
}

EVPKeyPointer EVPKeyPointer::NewRawPublic(int id, const Buffer<const unsigned char>& data) {
if (id == 0) return {};
return EVPKeyPointer(EVP_PKEY_new_raw_public_key(id, nullptr, data.data, data.len));
}

EVPKeyPointer EVPKeyPointer::NewRawPrivate(int id, const Buffer<const unsigned char>& data) {
if (id == 0) return {};
return EVPKeyPointer(EVP_PKEY_new_raw_private_key(id, nullptr, data.data, data.len));
}

EVPKeyPointer::EVPKeyPointer(EVP_PKEY* pkey) : pkey_(pkey) {}

EVPKeyPointer::EVPKeyPointer(EVPKeyPointer&& other) noexcept
: pkey_(other.release()) {}

EVPKeyPointer& EVPKeyPointer::operator=(EVPKeyPointer&& other) noexcept {
if (this == &other) return *this;
this->~EVPKeyPointer();
return *new (this) EVPKeyPointer(std::move(other));
}

EVPKeyPointer::~EVPKeyPointer() { reset(); }

void EVPKeyPointer::reset(EVP_PKEY* pkey) {
pkey_.reset(pkey);
}

EVP_PKEY* EVPKeyPointer::release() {
return pkey_.release();
}

int EVPKeyPointer::id(const EVP_PKEY* key) {
if (key == nullptr) return 0;
return EVP_PKEY_id(key);
}

int EVPKeyPointer::base_id(const EVP_PKEY* key) {
if (key == nullptr) return 0;
return EVP_PKEY_base_id(key);
}

int EVPKeyPointer::id() const {
return id(get());
}

int EVPKeyPointer::base_id() const {
return base_id(get());
}

int EVPKeyPointer::bits() const {
if (get() == nullptr) return 0;
return EVP_PKEY_bits(get());
}

size_t EVPKeyPointer::size() const {
if (get() == nullptr) return 0;
return EVP_PKEY_size(get());
}

EVPKeyCtxPointer EVPKeyPointer::newCtx() const {
if (!pkey_) return {};
return EVPKeyCtxPointer(EVP_PKEY_CTX_new(get(), nullptr));
}

size_t EVPKeyPointer::rawPublicKeySize() const {
if (!pkey_) return 0;
size_t len = 0;
if (EVP_PKEY_get_raw_public_key(get(), nullptr, &len) == 1) return len;
return 0;
}

size_t EVPKeyPointer::rawPrivateKeySize() const {
if (!pkey_) return 0;
size_t len = 0;
if (EVP_PKEY_get_raw_private_key(get(), nullptr, &len) == 1) return len;
return 0;
}

DataPointer EVPKeyPointer::rawPublicKey() const {
if (!pkey_) return {};
if (auto data = DataPointer::Alloc(rawPublicKeySize())) {
const Buffer<unsigned char> buf = data;
size_t len = data.size();
if (EVP_PKEY_get_raw_public_key(get(),
buf.data,
&len) != 1) return {};
return data;
}
return {};
}

DataPointer EVPKeyPointer::rawPrivateKey() const {
if (!pkey_) return {};
if (auto data = DataPointer::Alloc(rawPrivateKeySize())) {
const Buffer<unsigned char> buf = data;
size_t len = data.size();
if (EVP_PKEY_get_raw_private_key(get(),
buf.data,
&len) != 1) return {};
return data;
}
return {};
}

BIOPointer EVPKeyPointer::derPublicKey() const {
if (!pkey_) return {};
auto bio = BIOPointer::NewMem();
if (!bio) return {};
if (!i2d_PUBKEY_bio(bio.get(), get())) return {};
return bio;
}

namespace {
EVPKeyPointer::ParseKeyResult TryParsePublicKeyInner(
const BIOPointer& bp,
const char* name,
auto&& parse) {
if (!bp.resetBio()) {
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::FAILED);
}
unsigned char* der_data;
long der_len;

// This skips surrounding data and decodes PEM to DER.
{
MarkPopErrorOnReturn mark_pop_error_on_return;
if (PEM_bytes_read_bio(&der_data, &der_len, nullptr, name,
bp.get(), nullptr, nullptr) != 1)
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::NOT_RECOGNIZED);
}
DataPointer data(der_data, der_len);

// OpenSSL might modify the pointer, so we need to make a copy before parsing.
const unsigned char* p = der_data;
EVPKeyPointer pkey(parse(&p, der_len));
if (!pkey) return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::FAILED);
return EVPKeyPointer::ParseKeyResult(std::move(pkey));
}

EVPKeyPointer::ParseKeyResult TryParsePublicKeyPEM(
const Buffer<const unsigned char>& buffer) {
auto bp = BIOPointer::New(buffer.data, buffer.len);
if (!bp)
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::FAILED);

// Try parsing as SubjectPublicKeyInfo (SPKI) first.
if (auto ret = TryParsePublicKeyInner(bp, "PUBLIC KEY",
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
return d2i_PUBKEY(nullptr, p, l);
})) {
return ret;
}

// Maybe it is PKCS#1.
if (auto ret = TryParsePublicKeyInner(bp, "RSA PUBLIC KEY",
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
return d2i_PublicKey(EVP_PKEY_RSA, nullptr, p, l);
})) {
return ret;
}

// X.509 fallback.
if (auto ret = TryParsePublicKeyInner(bp, "CERTIFICATE",
[](const unsigned char** p, long l) { // NOLINT(runtime/int)
X509Pointer x509(d2i_X509(nullptr, p, l));
return x509 ? X509_get_pubkey(x509.get()) : nullptr;
})) {
return ret;
};

return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::NOT_RECOGNIZED);
}
} // namespace

EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
PKFormatType format,
PKEncodingType encoding,
const Buffer<const unsigned char>& buffer) {
if (format == PKFormatType::PEM) {
return TryParsePublicKeyPEM(buffer);
}

if (format != PKFormatType::DER) {
return ParseKeyResult(PKParseError::FAILED);
}

const unsigned char* start = buffer.data;

EVP_PKEY* key = nullptr;

if (encoding == PKEncodingType::PKCS1 &&
(key = d2i_PublicKey(EVP_PKEY_RSA, nullptr, &start, buffer.len))) {
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
}

if (encoding == PKEncodingType::SPKI &&
(key = d2i_PUBKEY(nullptr, &start, buffer.len))) {
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
}

return ParseKeyResult(PKParseError::FAILED);
}

} // namespace ncrypto
83 changes: 78 additions & 5 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,16 @@ class MarkPopErrorOnReturn final {
CryptoErrorList* errors_;
};

// TODO(@jasnell): Eventually replace with std::expected when we are able to
// bump up to c++23.
template <typename T, typename E>
struct Result final {
const bool has_value;
T value;
std::optional<E> error;
Result(T&& value) : value(std::move(value)) {}
Result(E&& error) : error(std::move(error)) {}
Result(T&& value) : has_value(true), value(std::move(value)) {}
Result(E&& error) : has_value(false), error(std::move(error)) {}
inline operator bool() const { return has_value; }
};

// ============================================================================
Expand All @@ -202,7 +206,6 @@ using ECGroupPointer = DeleteFnPtr<EC_GROUP, EC_GROUP_free>;
using ECKeyPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
using ECPointPointer = DeleteFnPtr<EC_POINT, EC_POINT_free>;
using EVPKeyCtxPointer = DeleteFnPtr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
using EVPKeyPointer = DeleteFnPtr<EVP_PKEY, EVP_PKEY_free>;
using EVPMDCtxPointer = DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free>;
using HMACCtxPointer = DeleteFnPtr<HMAC_CTX, HMAC_CTX_free>;
using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI, NETSCAPE_SPKI_free>;
Expand Down Expand Up @@ -252,9 +255,10 @@ class DataPointer final {
Buffer<void> release();

// Returns a Buffer struct that is a view of the underlying data.
inline operator const Buffer<void>() const {
template <typename T = void>
inline operator const Buffer<T>() const {
return {
.data = data_,
.data = static_cast<T*>(data_),
.len = len_,
};
}
Expand Down Expand Up @@ -359,6 +363,75 @@ class BignumPointer final {
DeleteFnPtr<BIGNUM, BN_clear_free> bn_;
};

class EVPKeyPointer final {
public:
static EVPKeyPointer New();
static EVPKeyPointer NewRawPublic(int id, const Buffer<const unsigned char>& data);
static EVPKeyPointer NewRawPrivate(int id, const Buffer<const unsigned char>& data);

enum class PKEncodingType {
// RSAPublicKey / RSAPrivateKey according to PKCS#1.
PKCS1,
// PrivateKeyInfo or EncryptedPrivateKeyInfo according to PKCS#8.
PKCS8,
// SubjectPublicKeyInfo according to X.509.
SPKI,
// ECPrivateKey according to SEC1.
SEC1
};

enum class PKFormatType {
DER,
PEM,
JWK
};

enum class PKParseError {
NOT_RECOGNIZED,
NEED_PASSPHRASE,
FAILED
};
using ParseKeyResult = Result<EVPKeyPointer, PKParseError>;

static ParseKeyResult TryParsePublicKey(
PKFormatType format,
PKEncodingType encoding,
const Buffer<const unsigned char>& buffer);

EVPKeyPointer() = default;
explicit EVPKeyPointer(EVP_PKEY* pkey);
EVPKeyPointer(EVPKeyPointer&& other) noexcept;
EVPKeyPointer& operator=(EVPKeyPointer&& other) noexcept;
NCRYPTO_DISALLOW_COPY(EVPKeyPointer)
~EVPKeyPointer();

inline bool operator==(std::nullptr_t) const noexcept { return pkey_ == nullptr; }
inline operator bool() const { return pkey_ != nullptr; }
inline EVP_PKEY* get() const { return pkey_.get(); }
void reset(EVP_PKEY* pkey = nullptr);
EVP_PKEY* release();

static int id(const EVP_PKEY* key);
static int base_id(const EVP_PKEY* key);

int id() const;
int base_id() const;
int bits() const;
size_t size() const;

size_t rawPublicKeySize() const;
size_t rawPrivateKeySize() const;
DataPointer rawPublicKey() const;
DataPointer rawPrivateKey() const;

BIOPointer derPublicKey() const;

EVPKeyCtxPointer newCtx() const;

private:
DeleteFnPtr<EVP_PKEY, EVP_PKEY_free> pkey_;
};

class DHPointer final {
public:

Expand Down
4 changes: 2 additions & 2 deletions src/crypto/crypto_cipher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ bool PublicKeyCipher::Cipher(
const ArrayBufferOrViewContents<unsigned char>& oaep_label,
const ArrayBufferOrViewContents<unsigned char>& data,
std::unique_ptr<BackingStore>* out) {
EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
EVPKeyCtxPointer ctx = pkey.newCtx();
if (!ctx)
return false;
if (EVP_PKEY_cipher_init(ctx.get()) <= 0)
Expand Down Expand Up @@ -1071,7 +1071,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {

if (EVP_PKEY_cipher == EVP_PKEY_decrypt &&
operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING) {
EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
EVPKeyCtxPointer ctx = pkey.newCtx();
CHECK(ctx);

if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) {
Expand Down
27 changes: 12 additions & 15 deletions src/crypto/crypto_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -449,15 +449,14 @@ MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) {
Local<Context> context = env->context();
crypto::EVPKeyPointer key(raw_key);

int kid = EVP_PKEY_id(key.get());
int bits = EVP_PKEY_bits(key.get());
int kid = key.id();
switch (kid) {
case EVP_PKEY_DH:
if (!Set<String>(context, info, env->type_string(), env->dh_string()) ||
!Set<Integer>(context,
info,
env->size_string(),
Integer::New(env->isolate(), bits))) {
info,
env->size_string(),
Integer::New(env->isolate(), key.bits()))) {
return MaybeLocal<Object>();
}
break;
Expand All @@ -473,18 +472,16 @@ MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) {
} else {
curve_name = OBJ_nid2sn(kid);
}
if (!Set<String>(context,
info,
env->type_string(),
env->ecdh_string()) ||
if (!Set<String>(
context, info, env->type_string(), env->ecdh_string()) ||
!Set<String>(context,
info,
env->name_string(),
OneByteString(env->isolate(), curve_name)) ||
info,
env->name_string(),
OneByteString(env->isolate(), curve_name)) ||
!Set<Integer>(context,
info,
env->size_string(),
Integer::New(env->isolate(), bits))) {
info,
env->size_string(),
Integer::New(env->isolate(), key.bits()))) {
return MaybeLocal<Object>();
}
}
Expand Down
Loading
Loading