From 609deb032c627fa816a874ccdbd2dee9fb145508 Mon Sep 17 00:00:00 2001 From: mirkobrombin Date: Wed, 4 Nov 2026 14:25:41 +0000 Subject: [PATCH] fix: read ASN.1 strings through their accessors for OpenSSL 4 --- native/nt/authenticode.cpp | 22 +++++++++---------- native/nt/crypt32_api.cpp | 45 +++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/native/nt/authenticode.cpp b/native/nt/authenticode.cpp index b6b385d..5eac567 100644 --- a/native/nt/authenticode.cpp +++ b/native/nt/authenticode.cpp @@ -344,12 +344,12 @@ std::optional stamped_time(PKCS7_SIGNER_INFO *signer) return std::nullopt; return timegm(&parts); }; - if (ASN1_TYPE *counter = PKCS7_get_attribute(signer, NID_pkcs9_countersignature); counter && counter->type == V_ASN1_SEQUENCE) { - const unsigned char *at = counter->value.sequence->data; + if (const ASN1_TYPE *counter = PKCS7_get_attribute(signer, NID_pkcs9_countersignature); counter && counter->type == V_ASN1_SEQUENCE) { + const unsigned char *at = ASN1_STRING_get0_data(counter->value.sequence); std::unique_ptr info( - d2i_PKCS7_SIGNER_INFO(nullptr, &at, counter->value.sequence->length), PKCS7_SIGNER_INFO_free); + d2i_PKCS7_SIGNER_INFO(nullptr, &at, ASN1_STRING_length(counter->value.sequence)), PKCS7_SIGNER_INFO_free); if (info) - if (ASN1_TYPE *signed_at = PKCS7_get_signed_attribute(info.get(), NID_pkcs9_signingTime); signed_at && signed_at->type == V_ASN1_UTCTIME) + if (const ASN1_TYPE *signed_at = PKCS7_get_signed_attribute(info.get(), NID_pkcs9_signingTime); signed_at && signed_at->type == V_ASN1_UTCTIME) return to_seconds(signed_at->value.utctime); } // An RFC 3161 timestamp token, which newer signatures carry under Microsoft's attribute; its identifier is compared as text, since the @@ -359,17 +359,17 @@ std::optional stamped_time(PKCS7_SIGNER_INFO *signer) X509_ATTRIBUTE *attribute = sk_X509_ATTRIBUTE_value(unsigned_attributes, index); char name[64] = {}; OBJ_obj2txt(name, sizeof(name), X509_ATTRIBUTE_get0_object(attribute), 1); - ASN1_TYPE *token = X509_ATTRIBUTE_get0_type(attribute, 0); + const ASN1_TYPE *token = X509_ATTRIBUTE_get0_type(attribute, 0); if (std::strcmp(name, "1.3.6.1.4.1.311.3.3.1") != 0 || !token || token->type != V_ASN1_SEQUENCE) continue; // The token is CMS signed data, which the PKCS #7 parser does not accept in every form; its content is the TSTInfo. - const unsigned char *at = token->value.sequence->data; + const unsigned char *at = ASN1_STRING_get0_data(token->value.sequence); std::unique_ptr signed_token( - d2i_CMS_ContentInfo(nullptr, &at, token->value.sequence->length), CMS_ContentInfo_free); + d2i_CMS_ContentInfo(nullptr, &at, ASN1_STRING_length(token->value.sequence)), CMS_ContentInfo_free); ASN1_OCTET_STRING **content = signed_token ? CMS_get0_content(signed_token.get()) : nullptr; if (content && *content) { - const unsigned char *info_at = (*content)->data; - std::unique_ptr info(d2i_TS_TST_INFO(nullptr, &info_at, (*content)->length), TS_TST_INFO_free); + const unsigned char *info_at = ASN1_STRING_get0_data(*content); + std::unique_ptr info(d2i_TS_TST_INFO(nullptr, &info_at, ASN1_STRING_length(*content)), TS_TST_INFO_free); if (info) return to_seconds(TS_TST_INFO_get_time(info.get())); } @@ -482,7 +482,7 @@ AuthenticodeResult verify_authenticode(std::span file) } // SpcIndirectDataContent: what was signed, then the DigestInfo of the file's content. const auto *content = signature->d.sign->contents->d.other->value.sequence; - const std::span indirect(content->data, size_t(content->length)); + const std::span indirect(ASN1_STRING_get0_data(content), size_t(ASN1_STRING_length(content))); const auto outer = der_header(indirect, 0); const auto first = outer ? der_header(indirect, outer->first) : std::nullopt; if (!outer || !first) { @@ -511,7 +511,7 @@ AuthenticodeResult verify_authenticode(std::span file) EVP_DigestInit_ex(hashing.get(), method, nullptr); found->hash(hashing.get()); EVP_DigestFinal_ex(hashing.get(), computed, &computed_size); - if (int(computed_size) != expected->length || std::memcmp(computed, expected->data, computed_size) != 0) { + if (int(computed_size) != ASN1_STRING_length(expected) || std::memcmp(computed, ASN1_STRING_get0_data(expected), computed_size) != 0) { result.status = trust_bad_digest; return result; } diff --git a/native/nt/crypt32_api.cpp b/native/nt/crypt32_api.cpp index db17c90..d91cff5 100644 --- a/native/nt/crypt32_api.cpp +++ b/native/nt/crypt32_api.cpp @@ -37,6 +37,13 @@ namespace raven { namespace { +// The bytes of an ASN.1 string. OpenSSL 4 makes the structure opaque, so they are read through the accessors. +std::vector asn1_bytes(const ASN1_STRING *value) +{ + const unsigned char *data = value ? ASN1_STRING_get0_data(value) : nullptr; + return data ? std::vector(data, data + ASN1_STRING_length(value)) : std::vector(); +} + constexpr uint32_t error_asn1_bad_tag = 0x8009310b, trust_not_time_valid = 0x1, trust_not_signature_valid = 0x8, trust_revoked = 0x4, trust_untrusted_root = 0x20, trust_partial_chain = 0x10000, trust_self_signed = 0x8; constexpr uint32_t store_provider_memory = 2, store_provider_system_a = 9, store_provider_system_w = 10; @@ -115,14 +122,14 @@ const char *attribute_label(int nid) // A distinguished name as CertNameToStr writes it: values alone, or prefixed by their object identifier or their short name, in the order // of the encoding or reversed, separated by commas, semicolons or line breaks. -std::string name_text(X509_NAME *name, uint32_t type) +std::string name_text(const X509_NAME *name, uint32_t type) { constexpr uint32_t simple = 1, oid = 2, semicolon = 0x40000000, crlf = 0x08000000, no_plus = 0x20000000, no_quote = 0x10000000, reverse = 0x02000000; (void)no_plus; std::vector parts; for (int index = 0; index < X509_NAME_entry_count(name); ++index) { - X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, index); + const X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, index); unsigned char *utf8 = nullptr; const int length = ASN1_STRING_to_UTF8(&utf8, X509_NAME_ENTRY_get_data(entry)); std::string value(utf8 ? reinterpret_cast(utf8) : "", length > 0 ? size_t(length) : 0); @@ -150,7 +157,7 @@ std::string name_text(X509_NAME *name, uint32_t type) } // The first value of an attribute of a name, as UTF-8. -std::string name_attribute(X509_NAME *name, int nid) +std::string name_attribute(const X509_NAME *name, int nid) { const int index = X509_NAME_get_index_by_NID(name, nid, -1); if (index < 0) @@ -228,8 +235,6 @@ uint64_t filetime_of(const ASN1_TIME *time) if (!time) return 0; int days = 0, seconds = 0; - ASN1_TIME epoch; - std::memset(&epoch, 0, sizeof(epoch)); ASN1_TIME *unix_epoch = ASN1_TIME_set(nullptr, 0); if (!unix_epoch || !ASN1_TIME_diff(&days, &seconds, unix_epoch, time)) { ASN1_STRING_free(unix_epoch); @@ -280,9 +285,9 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) const auto encoding_at = head.dword(), encoded_at = head.pointer(), encoded_size_at = head.dword(), info_at = head.pointer(), store_at = head.pointer(); // The serial number as CRYPT_INTEGER_BLOB holds it, little-endian. const auto *name = X509_get_serialNumber(certificate.get()); - std::vector serial(name ? name->data : nullptr, name ? name->data + name->length : nullptr); + std::vector serial = asn1_bytes(name); std::reverse(serial.begin(), serial.end()); - const auto name_bytes = [](X509_NAME *value) { + const auto name_bytes = [](const X509_NAME *value) { const int length = i2d_X509_NAME(value, nullptr); std::vector bytes(length > 0 ? size_t(length) : 0); unsigned char *at = bytes.data(); @@ -318,7 +323,7 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) size_t extension_bytes = size_t(extension_total) * extension.size(); for (int index = 0; index < extension_total; ++index) { X509_EXTENSION *entry = sk_X509_EXTENSION_value(extensions, index); - extension_bytes += object_text(X509_EXTENSION_get_object(entry)).size() + size_t(X509_EXTENSION_get_data(entry)->length); + extension_bytes += object_text(X509_EXTENSION_get_object(entry)).size() + size_t(ASN1_STRING_length(X509_EXTENSION_get_data(entry))); } // One block: the context, the CERT_INFO, the extension array, then the byte strings they point at. const size_t info_start = (head.size() + 15) / 16 * 16, bytes_start = (info_start + info.size() + 15) / 16 * 16; @@ -373,8 +378,8 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) const auto *value = X509_EXTENSION_get_data(entry); put_pointer(at + extension_oid, place(object_text(X509_EXTENSION_get_object(entry)))); put32(at + extension_critical, uint32_t(X509_EXTENSION_get_critical(entry) > 0)); - put32(at + extension_size, uint32_t(value->length)); - put_pointer(at + extension_value, place(std::vector(value->data, value->data + value->length))); + put32(at + extension_size, uint32_t(ASN1_STRING_length(value))); + put_pointer(at + extension_value, place(asn1_bytes(value))); } (void)issuer_unique; (void)subject_unique; call.write(block, image); @@ -861,10 +866,10 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) } else if (call.u32(1) == key_identifier) { if (const auto certificate = parse(encoded)) { if (const ASN1_OCTET_STRING *subject = X509_get0_subject_key_id(certificate.get())) { - value.assign(subject->data, subject->data + subject->length); + value = asn1_bytes(subject); } else if (const ASN1_BIT_STRING *key = X509_get0_pubkey_bitstr(certificate.get())) { value.resize(SHA_DIGEST_LENGTH); - SHA1(key->data, size_t(key->length), value.data()); + SHA1(ASN1_STRING_get0_data(key), size_t(ASN1_STRING_length(key)), value.data()); } } } @@ -1162,7 +1167,7 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) }; // A serial number as CRYPT_INTEGER_BLOB holds it: little-endian. const auto serial_bytes = [](const ASN1_INTEGER *serial) { - std::vector bytes(serial->data, serial->data + serial->length); + std::vector bytes = asn1_bytes(serial); std::reverse(bytes.begin(), bytes.end()); return bytes; }; @@ -1187,7 +1192,7 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) }; algorithm(signer->digest_alg, hash_oid, hash_parameters_size, hash_parameters); algorithm(signer->digest_enc_alg, encryption_oid, encryption_parameters_size, encryption_parameters); - packed.put_blob(encrypted_size, encrypted_data, std::span(signer->enc_digest->data, size_t(signer->enc_digest->length))); + packed.put_blob(encrypted_size, encrypted_data, asn1_bytes(signer->enc_digest)); const auto attributes = [&](STACK_OF(X509_ATTRIBUTE) *list, size_t count_at, size_t array_at) { const int count = list ? sk_X509_ATTRIBUTE_num(list) : 0; Layout attribute(width); @@ -1342,7 +1347,7 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) } else if (type == octet_string) { const unsigned char *at = bytes.data(); if (ASN1_OCTET_STRING *octets = d2i_ASN1_OCTET_STRING(nullptr, &at, long(bytes.size()))) { - std::vector content(octets->data, octets->data + octets->length); + std::vector content = asn1_bytes(octets); ASN1_OCTET_STRING_free(octets); build = [content, width](uint64_t base) { std::vector image(width * 2 + content.size()); @@ -1424,7 +1429,7 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) case content_param: { const auto *other = message->d.sign->contents->d.other; const std::vector inner = other && other->type == V_ASN1_SEQUENCE - ? std::vector(other->value.sequence->data, other->value.sequence->data + other->value.sequence->length) : std::vector(); + ? asn1_bytes(other->value.sequence) : std::vector(); answer_bytes(call, call[3], call[4], [&](uint64_t) { return inner; }); return; } @@ -1510,9 +1515,9 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) // The content an Authenticode signer signs is the indirect data without its own header. const auto *other = message->d.sign->contents->d.other; std::unique_ptr content(nullptr, BIO_free); - if (other && other->type == V_ASN1_SEQUENCE && other->value.sequence->length > 2) { - const unsigned char *bytes = other->value.sequence->data; - const size_t length = size_t(other->value.sequence->length); + if (other && other->type == V_ASN1_SEQUENCE && ASN1_STRING_length(other->value.sequence) > 2) { + const unsigned char *bytes = ASN1_STRING_get0_data(other->value.sequence); + const size_t length = size_t(ASN1_STRING_length(other->value.sequence)); const size_t header = bytes[1] < 0x80 ? 2 : 2 + (bytes[1] & 0x7f); content.reset(BIO_new_mem_buf(bytes + header, int(length - header))); } @@ -1603,7 +1608,7 @@ void register_crypt32_api(ApiRegistry ®istry, KernelContext &context) std::string text; if (call[0]) if (const auto certificate = parse(encoded_of(call, call[0]))) { - X509_NAME *name = call.u32(2) & issuer_flag ? X509_get_issuer_name(certificate.get()) : X509_get_subject_name(certificate.get()); + const X509_NAME *name = call.u32(2) & issuer_flag ? X509_get_issuer_name(certificate.get()) : X509_get_subject_name(certificate.get()); switch (call.u32(1)) { case email: text = name_attribute(name, NID_pkcs9_emailAddress); break; case rdn: text = name_text(name, call[3] ? call.load(call[3]) : 3); break;