From 17c1f41e833d9aff620e4390d331f75ad63ae924 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 21 Jul 2024 15:56:48 -0700 Subject: [PATCH] src: move spkac methods to ncrypto --- deps/ncrypto/ncrypto.cc | 71 ++++++++++++++++++++++++++++++++++++- deps/ncrypto/ncrypto.h | 14 ++++++-- src/crypto/crypto_spkac.cc | 72 ++++---------------------------------- src/crypto/crypto_util.h | 6 ++++ 4 files changed, 95 insertions(+), 68 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index b3e6a57edc1b0e..6822aac4ee74b6 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -2,6 +2,7 @@ #include #include #include "openssl/bn.h" +#include "openssl/evp.h" #if OPENSSL_VERSION_MAJOR >= 3 #include "openssl/provider.h" #endif @@ -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(u); + auto passphrase = static_cast*>(u); if (passphrase != nullptr) { size_t buflen = static_cast(size); size_t len = passphrase->len; @@ -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 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(buf), + .len = static_cast(buf_size), + }; + } + + return {}; +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 6fb66fe5234d85..979c177b4a8c65 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -269,9 +269,10 @@ bool testFipsEnabled(); // ============================================================================ // Various utilities +template 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; @@ -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 +Buffer ExportChallenge(const char* input, size_t length); + // ============================================================================ // Version metadata #define NCRYPTO_VERSION "0.0.1" diff --git a/src/crypto/crypto_spkac.cc b/src/crypto/crypto_spkac.cc index a09a09ddd2f9d4..236de520d8dbf2 100644 --- a/src/crypto/crypto_spkac.cc +++ b/src/crypto/crypto_spkac.cc @@ -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" @@ -16,25 +17,6 @@ using v8::Value; namespace crypto { namespace SPKAC { -bool VerifySpkac(const ArrayBufferOrViewContents& 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& args) { Environment* env = Environment::GetCurrent(args); @@ -44,31 +26,7 @@ void VerifySpkac(const FunctionCallbackInfo& 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& 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& args) { @@ -80,30 +38,13 @@ void ExportPublicKey(const FunctionCallbackInfo& 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())); } -ByteSource ExportChallenge(const ArrayBufferOrViewContents& 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& args) { Environment* env = Environment::GetCurrent(args); @@ -113,7 +54,8 @@ void ExportChallenge(const FunctionCallbackInfo& 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(); diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 4d97af48edf6d5..99a9ec33378d45 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -289,6 +289,12 @@ class ByteSource { v8::MaybeLocal ToBuffer(Environment* env); static ByteSource Allocated(void* data, size_t size); + + template + static ByteSource Allocated(const ncrypto::Buffer& buffer) { + return Allocated(buffer.data, buffer.len); + } + static ByteSource Foreign(const void* data, size_t size); static ByteSource FromEncodedString(Environment* env,