From 5843486948ec52290add75ee7f2c62c36dde619e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 25 Jan 2025 13:52:02 -0800 Subject: [PATCH] src: move more x509 to ncrypto Signed-off-by: James M Snell --- deps/ncrypto/ncrypto.cc | 71 ++++++++++++++++++++++++++++++++++++ deps/ncrypto/ncrypto.h | 39 ++++++++++++++++++++ src/crypto/crypto_x509.cc | 75 +++++++-------------------------------- 3 files changed, 123 insertions(+), 62 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 2ff0213564e16a..70182313ab431e 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -915,6 +915,18 @@ BIOPointer X509View::toDER() const { return bio; } +const X509Name X509View::getSubjectName() const { + ClearErrorOnReturn clearErrorOnReturn; + if (cert_ == nullptr) return {}; + return X509Name(X509_get_subject_name(cert_)); +} + +const X509Name X509View::getIssuerName() const { + ClearErrorOnReturn clearErrorOnReturn; + if (cert_ == nullptr) return {}; + return X509Name(X509_get_issuer_name(cert_)); +} + BIOPointer X509View::getSubject() const { ClearErrorOnReturn clearErrorOnReturn; if (cert_ == nullptr) return {}; @@ -3831,4 +3843,63 @@ DataPointer hashDigest(const Buffer& buf, return data.resize(result_size); } +// ============================================================================ + +X509Name::X509Name() : name_(nullptr), total_(0) {} + +X509Name::X509Name(const X509_NAME* name) + : name_(name), total_(X509_NAME_entry_count(name)) {} + +X509Name::Iterator::Iterator(const X509Name& name, int pos) + : name_(name), loc_(pos) {} + +X509Name::Iterator& X509Name::Iterator::operator++() { + ++loc_; + return *this; +} + +X509Name::Iterator::operator bool() const { + return loc_ < name_.total_; +} + +bool X509Name::Iterator::operator==(const Iterator& other) const { + return loc_ == other.loc_; +} + +bool X509Name::Iterator::operator!=(const Iterator& other) const { + return loc_ != other.loc_; +} + +std::pair X509Name::Iterator::operator*() const { + if (loc_ == name_.total_) return {{}, {}}; + + X509_NAME_ENTRY* entry = X509_NAME_get_entry(name_, loc_); + if (entry == nullptr) [[unlikely]] + return {{}, {}}; + + ASN1_OBJECT* name = X509_NAME_ENTRY_get_object(entry); + ASN1_STRING* value = X509_NAME_ENTRY_get_data(entry); + + if (name == nullptr || value == nullptr) [[unlikely]] { + return {{}, {}}; + } + + int nid = OBJ_obj2nid(name); + std::string name_str; + if (nid != NID_undef) { + name_str = std::string(OBJ_nid2sn(nid)); + } else { + char buf[80]; + OBJ_obj2txt(buf, sizeof(buf), name, 0); + name_str = std::string(buf); + } + + unsigned char* value_str; + int value_str_size = ASN1_STRING_to_UTF8(&value_str, value); + + return { + std::move(name_str), + std::string(reinterpret_cast(value_str), value_str_size)}; +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 0d1d466c3a0c14..3340c137e52669 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -934,6 +934,43 @@ class SSLPointer final { DeleteFnPtr ssl_; }; +class X509Name final { + public: + X509Name(); + explicit X509Name(const X509_NAME* name); + NCRYPTO_DISALLOW_COPY_AND_MOVE(X509Name) + + inline operator const X509_NAME*() const { return name_; } + inline operator bool() const { return name_ != nullptr; } + inline const X509_NAME* get() const { return name_; } + inline size_t size() const { return total_; } + + class Iterator final { + public: + Iterator(const X509Name& name, int pos); + Iterator(const Iterator& other) = default; + Iterator(Iterator&& other) = default; + Iterator& operator=(const Iterator& other) = delete; + Iterator& operator=(Iterator&& other) = delete; + Iterator& operator++(); + operator bool() const; + bool operator==(const Iterator& other) const; + bool operator!=(const Iterator& other) const; + std::pair operator*() const; + + private: + const X509Name& name_; + int loc_; + }; + + inline Iterator begin() const { return Iterator(*this, 0); } + inline Iterator end() const { return Iterator(*this, total_); } + + private: + const X509_NAME* name_; + int total_; +}; + class X509View final { public: static X509View From(const SSLPointer& ssl); @@ -955,6 +992,8 @@ class X509View final { BIOPointer toPEM() const; BIOPointer toDER() const; + const X509Name getSubjectName() const; + const X509Name getIssuerName() const; BIOPointer getSubject() const; BIOPointer getSubjectAltName() const; BIOPointer getIssuer() const; diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 782eced277518d..b974667a4cacb9 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -21,6 +21,7 @@ using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; +using ncrypto::X509Name; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::Array; @@ -92,6 +93,11 @@ void Fingerprint(const FunctionCallbackInfo& args) { } } +MaybeLocal ToV8Value(Environment* env, std::string_view val) { + return String::NewFromUtf8( + env->isolate(), val.data(), NewStringType::kNormal, val.size()); +} + MaybeLocal ToV8Value(Local context, BIOPointer&& bio) { if (!bio) [[unlikely]] return {}; @@ -106,51 +112,6 @@ MaybeLocal ToV8Value(Local context, BIOPointer&& bio) { return ret; } -MaybeLocal ToV8Value(Local context, const ASN1_OBJECT* obj) { - // If OpenSSL knows the type, use the short name of the type as the key, and - // the numeric representation of the type's OID otherwise. - int nid = OBJ_obj2nid(obj); - char buf[80]; - const char* str; - if (nid != NID_undef) { - str = OBJ_nid2sn(nid); - CHECK_NOT_NULL(str); - } else { - OBJ_obj2txt(buf, sizeof(buf), obj, true); - str = buf; - } - - Local result; - if (!String::NewFromUtf8(context->GetIsolate(), str).ToLocal(&result)) { - return {}; - } - return result; -} - -MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { - // The previous implementation used X509_NAME_print_ex, which escapes some - // characters in the value. The old implementation did not decode/unescape - // values correctly though, leading to ambiguous and incorrect - // representations. The new implementation only converts to Unicode and does - // not escape anything. - unsigned char* value_str; - int value_str_size = ASN1_STRING_to_UTF8(&value_str, str); - if (value_str_size < 0) [[unlikely]] { - return Undefined(context->GetIsolate()); - } - DataPointer free_value_str(value_str, value_str_size); - - Local result; - if (!String::NewFromUtf8(context->GetIsolate(), - reinterpret_cast(value_str), - NewStringType::kNormal, - value_str_size) - .ToLocal(&result)) { - return {}; - } - return result; -} - MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { if (!bio) [[unlikely]] return {}; @@ -594,14 +555,9 @@ bool Set(Environment* env, // Convert an X509_NAME* into a JavaScript object. // Each entry of the name is converted into a property of the object. // The property value may be a single string or an array of strings. -template static MaybeLocal GetX509NameObject(Environment* env, - const X509View& cert) { - X509_NAME* name = get_name(cert.get()); - CHECK_NOT_NULL(name); - - int cnt = X509_NAME_entry_count(name); - CHECK_GE(cnt, 0); + const X509Name& name) { + if (!name) return {}; Local v8_name; Local v8_value; @@ -610,14 +566,9 @@ static MaybeLocal GetX509NameObject(Environment* env, Object::New(env->isolate(), Null(env->isolate()), nullptr, nullptr, 0); if (result.IsEmpty()) return {}; - for (int i = 0; i < cnt; i++) { - X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, i); - CHECK_NOT_NULL(entry); - - if (!ToV8Value(env->context(), X509_NAME_ENTRY_get_object(entry)) - .ToLocal(&v8_name) || - !ToV8Value(env->context(), X509_NAME_ENTRY_get_data(entry)) - .ToLocal(&v8_value)) { + for (auto i : name) { + if (!ToV8Value(env, i.first).ToLocal(&v8_name) || + !ToV8Value(env, i.second).ToLocal(&v8_value)) { return {}; } @@ -727,11 +678,11 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { if (!Set(env, info, env->subject_string(), - GetX509NameObject(env, cert)) || + GetX509NameObject(env, cert.getSubjectName())) || !Set(env, info, env->issuer_string(), - GetX509NameObject(env, cert)) || + GetX509NameObject(env, cert.getIssuerName())) || !Set(env, info, env->subjectaltname_string(),