Skip to content

Commit

Permalink
src: move spkac methods to ncrypto
Browse files Browse the repository at this point in the history
PR-URL: #53985
Reviewed-By: Yagiz Nizipli <[email protected]>
  • Loading branch information
jasnell authored and targos committed Aug 14, 2024
1 parent 1e82591 commit e619133
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 68 deletions.
71 changes: 70 additions & 1 deletion deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <algorithm>
#include <cstring>
#include "openssl/bn.h"
#include "openssl/evp.h"
#if OPENSSL_VERSION_MAJOR >= 3
#include "openssl/provider.h"
#endif
Expand Down Expand Up @@ -207,7 +208,7 @@ int NoPasswordCallback(char* buf, int size, int rwflag, void* u) {
}

int PasswordCallback(char* buf, int size, int rwflag, void* u) {
const Buffer* passphrase = static_cast<const Buffer*>(u);
auto passphrase = static_cast<const Buffer<char>*>(u);
if (passphrase != nullptr) {
size_t buflen = static_cast<size_t>(size);
size_t len = passphrase->len;
Expand All @@ -220,4 +221,72 @@ int PasswordCallback(char* buf, int size, int rwflag, void* u) {
return -1;
}

// ============================================================================
// SPKAC

bool VerifySpkac(const char* input, size_t length) {
#ifdef OPENSSL_IS_BORINGSSL
// OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
// while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
// As such, we trim those characters here for compatibility.
//
// find_last_not_of can return npos, which is the maximum value of size_t.
// The + 1 will force a roll-ver to 0, which is the correct value. in that
// case.
length = std::string_view(input, length).find_last_not_of(" \n\r\t") + 1;
#endif
NetscapeSPKIPointer spki(
NETSCAPE_SPKI_b64_decode(input, length));
if (!spki)
return false;

EVPKeyPointer pkey(X509_PUBKEY_get(spki->spkac->pubkey));
return pkey ? NETSCAPE_SPKI_verify(spki.get(), pkey.get()) > 0 : false;
}

BIOPointer ExportPublicKey(const char* input, size_t length) {
BIOPointer bio(BIO_new(BIO_s_mem()));
if (!bio) return {};

#ifdef OPENSSL_IS_BORINGSSL
// OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
// while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
// As such, we trim those characters here for compatibility.
length = std::string_view(input, length).find_last_not_of(" \n\r\t") + 1;
#endif
NetscapeSPKIPointer spki(
NETSCAPE_SPKI_b64_decode(input, length));
if (!spki) return {};

EVPKeyPointer pkey(NETSCAPE_SPKI_get_pubkey(spki.get()));
if (!pkey) return {};

if (PEM_write_bio_PUBKEY(bio.get(), pkey.get()) <= 0) return { };

return std::move(bio);
}

Buffer<char> ExportChallenge(const char* input, size_t length) {
#ifdef OPENSSL_IS_BORINGSSL
// OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
// while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
// As such, we trim those characters here for compatibility.
length = std::string_view(input, length).find_last_not_of(" \n\r\t") + 1;
#endif
NetscapeSPKIPointer sp(
NETSCAPE_SPKI_b64_decode(input, length));
if (!sp) return {};

unsigned char* buf = nullptr;
int buf_size = ASN1_STRING_to_UTF8(&buf, sp->spkac->challenge);
if (buf_size >= 0) {
return {
.data = reinterpret_cast<char*>(buf),
.len = static_cast<size_t>(buf_size),
};
}

return {};
}

} // namespace ncrypto
14 changes: 12 additions & 2 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,10 @@ bool testFipsEnabled();
// ============================================================================
// Various utilities

template <typename T>
struct Buffer {
const void* data;
size_t len;
T* data = nullptr;
size_t len = 0;
};

bool CSPRNG(void* buffer, size_t length) NCRYPTO_MUST_USE_RESULT;
Expand All @@ -285,6 +286,15 @@ int NoPasswordCallback(char* buf, int size, int rwflag, void* u);

int PasswordCallback(char* buf, int size, int rwflag, void* u);

// ============================================================================
// SPKAC

bool VerifySpkac(const char* input, size_t length);
BIOPointer ExportPublicKey(const char* input, size_t length);

// The caller takes ownership of the returned Buffer<char>
Buffer<char> ExportChallenge(const char* input, size_t length);

// ============================================================================
// Version metadata
#define NCRYPTO_VERSION "0.0.1"
Expand Down
72 changes: 7 additions & 65 deletions src/crypto/crypto_spkac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "crypto/crypto_util.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
#include "ncrypto.h"
#include "node.h"
#include "v8.h"

Expand All @@ -16,25 +17,6 @@ using v8::Value;

namespace crypto {
namespace SPKAC {
bool VerifySpkac(const ArrayBufferOrViewContents<char>& input) {
size_t length = input.size();
#ifdef OPENSSL_IS_BORINGSSL
// OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
// while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
// As such, we trim those characters here for compatibility.
length = std::string(input.data()).find_last_not_of(" \n\r\t") + 1;
#endif
NetscapeSPKIPointer spki(
NETSCAPE_SPKI_b64_decode(input.data(), length));
if (!spki)
return false;

EVPKeyPointer pkey(X509_PUBKEY_get(spki->spkac->pubkey));
if (!pkey)
return false;

return NETSCAPE_SPKI_verify(spki.get(), pkey.get()) > 0;
}

void VerifySpkac(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Expand All @@ -44,31 +26,7 @@ void VerifySpkac(const FunctionCallbackInfo<Value>& args) {
if (UNLIKELY(!input.CheckSizeInt32()))
return THROW_ERR_OUT_OF_RANGE(env, "spkac is too large");

args.GetReturnValue().Set(VerifySpkac(input));
}

ByteSource ExportPublicKey(Environment* env,
const ArrayBufferOrViewContents<char>& input) {
BIOPointer bio(BIO_new(BIO_s_mem()));
if (!bio) return ByteSource();

size_t length = input.size();
#ifdef OPENSSL_IS_BORINGSSL
// OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
// while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
// As such, we trim those characters here for compatibility.
length = std::string(input.data()).find_last_not_of(" \n\r\t") + 1;
#endif
NetscapeSPKIPointer spki(
NETSCAPE_SPKI_b64_decode(input.data(), length));
if (!spki) return ByteSource();

EVPKeyPointer pkey(NETSCAPE_SPKI_get_pubkey(spki.get()));
if (!pkey) return ByteSource();

if (PEM_write_bio_PUBKEY(bio.get(), pkey.get()) <= 0) return ByteSource();

return ByteSource::FromBIO(bio);
args.GetReturnValue().Set(ncrypto::VerifySpkac(input.data(), input.size()));
}

void ExportPublicKey(const FunctionCallbackInfo<Value>& args) {
Expand All @@ -80,30 +38,13 @@ void ExportPublicKey(const FunctionCallbackInfo<Value>& args) {
if (UNLIKELY(!input.CheckSizeInt32()))
return THROW_ERR_OUT_OF_RANGE(env, "spkac is too large");

ByteSource pkey = ExportPublicKey(env, input);
if (!pkey) return args.GetReturnValue().SetEmptyString();
BIOPointer bio = ncrypto::ExportPublicKey(input.data(), input.size());
if (!bio) return args.GetReturnValue().SetEmptyString();

auto pkey = ByteSource::FromBIO(bio);
args.GetReturnValue().Set(pkey.ToBuffer(env).FromMaybe(Local<Value>()));
}

ByteSource ExportChallenge(const ArrayBufferOrViewContents<char>& input) {
size_t length = input.size();
#ifdef OPENSSL_IS_BORINGSSL
// OpenSSL uses EVP_DecodeBlock, which explicitly removes trailing characters,
// while BoringSSL uses EVP_DecodedLength and EVP_DecodeBase64, which do not.
// As such, we trim those characters here for compatibility.
length = std::string(input.data()).find_last_not_of(" \n\r\t") + 1;
#endif
NetscapeSPKIPointer sp(
NETSCAPE_SPKI_b64_decode(input.data(), length));
if (!sp)
return ByteSource();

unsigned char* buf = nullptr;
int buf_size = ASN1_STRING_to_UTF8(&buf, sp->spkac->challenge);
return (buf_size >= 0) ? ByteSource::Allocated(buf, buf_size) : ByteSource();
}

void ExportChallenge(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand All @@ -113,7 +54,8 @@ void ExportChallenge(const FunctionCallbackInfo<Value>& args) {
if (UNLIKELY(!input.CheckSizeInt32()))
return THROW_ERR_OUT_OF_RANGE(env, "spkac is too large");

ByteSource cert = ExportChallenge(input);
auto cert = ByteSource::Allocated(
ncrypto::ExportChallenge(input.data(), input.size()));
if (!cert)
return args.GetReturnValue().SetEmptyString();

Expand Down
6 changes: 6 additions & 0 deletions src/crypto/crypto_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ class ByteSource {
v8::MaybeLocal<v8::Uint8Array> ToBuffer(Environment* env);

static ByteSource Allocated(void* data, size_t size);

template <typename T>
static ByteSource Allocated(const ncrypto::Buffer<T>& buffer) {
return Allocated(buffer.data, buffer.len);
}

static ByteSource Foreign(const void* data, size_t size);

static ByteSource FromEncodedString(Environment* env,
Expand Down

0 comments on commit e619133

Please sign in to comment.