diff --git a/crypto/curve25519/internal.h b/crypto/curve25519/internal.h index 0cd1a12..a40f109 100644 --- a/crypto/curve25519/internal.h +++ b/crypto/curve25519/internal.h
@@ -32,7 +32,7 @@ #endif #if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_SMALL) && \ - defined(__GNUC__) && defined(__x86_64__) + defined(__GNUC__) && defined(__x86_64__) && !defined(OPENSSL_WINDOWS) #define BORINGSSL_FE25519_ADX // fiat_curve25519_adx_mul is defined in
diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index bda1a3e..c79c558 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c
@@ -230,13 +230,11 @@ } } - // if (ret->data.ptr != NULL) X509_OBJECT_free_contents(ret); - + // TODO(crbug.com/boringssl/685): This should call + // |X509_OBJECT_free_contents|. ret->type = tmp->type; - ret->data.ptr = tmp->data.ptr; - + ret->data = tmp->data; X509_OBJECT_up_ref_count(ret); - return 1; } @@ -391,8 +389,27 @@ return sk_X509_OBJECT_value(h, idx); } -STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *st) { - return st->objs; +static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj) { + X509_OBJECT *ret = X509_OBJECT_new(); + if (ret == NULL) { + return NULL; + } + ret->type = obj->type; + ret->data = obj->data; + X509_OBJECT_up_ref_count(ret); + return ret; +} + +STACK_OF(X509_OBJECT) *X509_STORE_get1_objects(X509_STORE *store) { + CRYPTO_MUTEX_lock_read(&store->objs_lock); + STACK_OF(X509_OBJECT) *ret = + sk_X509_OBJECT_deep_copy(store->objs, x509_object_dup, X509_OBJECT_free); + CRYPTO_MUTEX_unlock_read(&store->objs_lock); + return ret; +} + +STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *store) { + return store->objs; } STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm) {
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index b40eb99..a3616dc 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc
@@ -1382,6 +1382,11 @@ threads.emplace_back([&] { ASSERT_TRUE(X509_STORE_add_cert(store.get(), other2.get())); }); + threads.emplace_back([&] { + bssl::UniquePtr<STACK_OF(X509_OBJECT)> objs( + X509_STORE_get1_objects(store.get())); + ASSERT_TRUE(objs); + }); } for (auto &thread : threads) { thread.join();
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h index c9f265a..b7afbbb 100644 --- a/include/openssl/asn1.h +++ b/include/openssl/asn1.h
@@ -1636,18 +1636,18 @@ // ASN1_STRFLGS_ESC_2253 causes characters to be escaped as in RFC 2253, section // 2.4. -#define ASN1_STRFLGS_ESC_2253 1 +#define ASN1_STRFLGS_ESC_2253 1ul // ASN1_STRFLGS_ESC_CTRL causes all control characters to be escaped. -#define ASN1_STRFLGS_ESC_CTRL 2 +#define ASN1_STRFLGS_ESC_CTRL 2ul // ASN1_STRFLGS_ESC_MSB causes all characters above 127 to be escaped. -#define ASN1_STRFLGS_ESC_MSB 4 +#define ASN1_STRFLGS_ESC_MSB 4ul // ASN1_STRFLGS_ESC_QUOTE causes the string to be surrounded by quotes, rather // than using backslashes, when characters are escaped. Fewer characters will // require escapes in this case. -#define ASN1_STRFLGS_ESC_QUOTE 8 +#define ASN1_STRFLGS_ESC_QUOTE 8ul // ASN1_STRFLGS_UTF8_CONVERT causes the string to be encoded as UTF-8, with each // byte in the UTF-8 encoding treated as an individual character for purposes of @@ -1655,29 +1655,29 @@ // as a character, with wide characters escaped as "\Uxxxx" or "\Wxxxxxxxx". // Note this can be ambiguous if |ASN1_STRFLGS_ESC_*| are all unset. In that // case, backslashes are not escaped, but wide characters are. -#define ASN1_STRFLGS_UTF8_CONVERT 0x10 +#define ASN1_STRFLGS_UTF8_CONVERT 0x10ul // ASN1_STRFLGS_IGNORE_TYPE causes the string type to be ignored. The // |ASN1_STRING| in-memory representation will be printed directly. -#define ASN1_STRFLGS_IGNORE_TYPE 0x20 +#define ASN1_STRFLGS_IGNORE_TYPE 0x20ul // ASN1_STRFLGS_SHOW_TYPE causes the string type to be included in the output. -#define ASN1_STRFLGS_SHOW_TYPE 0x40 +#define ASN1_STRFLGS_SHOW_TYPE 0x40ul // ASN1_STRFLGS_DUMP_ALL causes all strings to be printed as a hexdump, using // RFC 2253 hexstring notation, such as "#0123456789ABCDEF". -#define ASN1_STRFLGS_DUMP_ALL 0x80 +#define ASN1_STRFLGS_DUMP_ALL 0x80ul // ASN1_STRFLGS_DUMP_UNKNOWN behaves like |ASN1_STRFLGS_DUMP_ALL| but only // applies to values of unknown type. If unset, unknown values will print // their contents as single-byte characters with escape sequences. -#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100 +#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100ul // ASN1_STRFLGS_DUMP_DER causes hexdumped strings (as determined by // |ASN1_STRFLGS_DUMP_ALL| or |ASN1_STRFLGS_DUMP_UNKNOWN|) to print the entire // DER element as in RFC 2253, rather than only the contents of the // |ASN1_STRING|. -#define ASN1_STRFLGS_DUMP_DER 0x200 +#define ASN1_STRFLGS_DUMP_DER 0x200ul // ASN1_STRFLGS_RFC2253 causes the string to be escaped as in RFC 2253, // additionally escaping control characters.
diff --git a/include/openssl/base.h b/include/openssl/base.h index 9a47321..98de503 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h
@@ -109,7 +109,7 @@ // A consumer may use this symbol in the preprocessor to temporarily build // against multiple revisions of BoringSSL at the same time. It is not // recommended to do so for longer than is necessary. -#define BORINGSSL_API_VERSION 29 +#define BORINGSSL_API_VERSION 30 #if defined(BORINGSSL_SHARED_LIBRARY)
diff --git a/include/openssl/pki/certificate.h b/include/openssl/pki/certificate.h new file mode 100644 index 0000000..3adaa03 --- /dev/null +++ b/include/openssl/pki/certificate.h
@@ -0,0 +1,83 @@ +/* Copyright (c) 2023, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef OPENSSL_HEADER_BSSL_PKI_CERTIFICATE_H_ +#define OPENSSL_HEADER_BSSL_PKI_CERTIFICATE_H_ + +#include <memory> +#include <string> +#include <string_view> + +#include <openssl/base.h> +#include <openssl/span.h> + +namespace bssl { + +struct CertificateInternals; + +// Certificate represents a parsed X.509 certificate. It includes accessors for +// the various things that one might want to extract from a certificate, +class OPENSSL_EXPORT Certificate { + public: + Certificate(Certificate&& other); + Certificate(const Certificate& other) = delete; + ~Certificate(); + Certificate& operator=(const Certificate& other) = delete; + + // FromDER returns a certificate from an DER-encoded X.509 object in |der|. + // In the event of a failure, it will return no value, and |out_diagnostic| + // may be set to a string of human readable debugging information if + // information abou the failure is available. + static std::unique_ptr<Certificate> FromDER( + bssl::Span<const uint8_t> der, std::string *out_diagnostic); + + // FromPEM returns a certificate from the first CERTIFICATE PEM block in + // |pem|. In the event of a failure, it will return no value, and + // |out_diagnostic| may be set to a string of human readable debugging + // informtion if informaiton about the failuew is available. + static std::unique_ptr<Certificate> FromPEM( + std::string_view pem, std::string *out_diagnostic); + + // IsSelfIssued returns true if the certificate is "self-issued" per RFC 5280 + // section 6.1. I.e. that the subject and issuer names are equal after + // canonicalization (and no other checks). + // + // Other contexts may have a different notion such as "self signed" which + // may or may not be this, and may check other properties of the certificate. + bool IsSelfIssued() const; + + // Validity specifies the temporal validity of a cerificate, expressed in + // POSIX time values of seconds since the POSIX epoch. The certificate is + // valid at POSIX time t in second granularity, where not_before <= t <= + // not_after. + struct Validity { + int64_t not_before; + int64_t not_after; + }; + + Validity GetValidity() const; + + // The binary, big-endian, DER representation of the certificate serial + // number. It may include a leading 00 byte. + bssl::Span<const uint8_t> GetSerialNumber() const; + + private: + explicit Certificate(std::unique_ptr<CertificateInternals> internals); + + std::unique_ptr<CertificateInternals> internals_; +}; + +} // namespace bssl + +#endif // OPENSSL_HEADER_BSSL_PKI_CERTIFICATE_H_
diff --git a/pki/signature_verify_cache.h b/include/openssl/pki/signature_verify_cache.h similarity index 100% rename from pki/signature_verify_cache.h rename to include/openssl/pki/signature_verify_cache.h
diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 59356ed..c7e6919 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h
@@ -2215,6 +2215,9 @@ // functions which take a non-const pointer may not. Callers that wish to modify // verification parameters in a shared |X509_STORE| should instead modify // |X509_STORE_CTX|s individually. +// +// Objects in an |X509_STORE| are represented as an |X509_OBJECT|. Some +// functions in this library return values with this type. // X509_STORE_new returns a newly-allocated |X509_STORE|, or NULL on error. OPENSSL_EXPORT X509_STORE *X509_STORE_new(void); @@ -2303,6 +2306,41 @@ // |X509_VERIFY_PARAM_set_trust| for details. OPENSSL_EXPORT int X509_STORE_set_trust(X509_STORE *store, int trust); +// The following constants indicate the type of an |X509_OBJECT|. +#define X509_LU_NONE 0 +#define X509_LU_X509 1 +#define X509_LU_CRL 2 +#define X509_LU_PKEY 3 + +DEFINE_STACK_OF(X509_OBJECT) + +// X509_OBJECT_new returns a newly-allocated, empty |X509_OBJECT| or NULL on +// error. +OPENSSL_EXPORT X509_OBJECT *X509_OBJECT_new(void); + +// X509_OBJECT_free releases memory associated with |obj|. +OPENSSL_EXPORT void X509_OBJECT_free(X509_OBJECT *obj); + +// X509_OBJECT_get_type returns the type of |obj|, which will be one of the +// |X509_LU_*| constants. +OPENSSL_EXPORT int X509_OBJECT_get_type(const X509_OBJECT *obj); + +// X509_OBJECT_get0_X509 returns |obj| as a certificate, or NULL if |obj| is not +// a certificate. +OPENSSL_EXPORT X509 *X509_OBJECT_get0_X509(const X509_OBJECT *obj); + +// X509_STORE_get1_objects returns a newly-allocated stack containing the +// contents of |store|, or NULL on error. The caller must release the result +// with |sk_X509_OBJECT_pop_free| and |X509_OBJECT_free| when done. +// +// The result will include all certificates and CRLs added via +// |X509_STORE_add_cert| and |X509_STORE_add_crl|, as well as any cached objects +// added by |X509_LOOKUP_hash_dir|. The last of these may change over time, as +// different objects are loaded from the filesystem. Callers should not depend +// on this caching behavior. The objects are returned in no particular order. +OPENSSL_EXPORT STACK_OF(X509_OBJECT) *X509_STORE_get1_objects( + X509_STORE *store); + // Certificate verification. // @@ -3187,42 +3225,42 @@ // XN_FLAG_COMPAT prints with |X509_NAME_print|'s format and return value // convention. -#define XN_FLAG_COMPAT 0 +#define XN_FLAG_COMPAT 0ul // XN_FLAG_SEP_MASK determines the separators to use between attributes. -#define XN_FLAG_SEP_MASK (0xf << 16) +#define XN_FLAG_SEP_MASK (0xful << 16) // XN_FLAG_SEP_COMMA_PLUS separates RDNs with "," and attributes within an RDN // with "+", as in RFC 2253. -#define XN_FLAG_SEP_COMMA_PLUS (1 << 16) +#define XN_FLAG_SEP_COMMA_PLUS (1ul << 16) // XN_FLAG_SEP_CPLUS_SPC behaves like |XN_FLAG_SEP_COMMA_PLUS| but adds spaces // between the separators. -#define XN_FLAG_SEP_CPLUS_SPC (2 << 16) +#define XN_FLAG_SEP_CPLUS_SPC (2ul << 16) // XN_FLAG_SEP_SPLUS_SPC separates RDNs with "; " and attributes within an RDN // with " + ". -#define XN_FLAG_SEP_SPLUS_SPC (3 << 16) +#define XN_FLAG_SEP_SPLUS_SPC (3ul << 16) // XN_FLAG_SEP_MULTILINE prints each attribute on one line. -#define XN_FLAG_SEP_MULTILINE (4 << 16) +#define XN_FLAG_SEP_MULTILINE (4ul << 16) // XN_FLAG_DN_REV prints RDNs in reverse, from least significant to most // significant, as RFC 2253. -#define XN_FLAG_DN_REV (1 << 20) +#define XN_FLAG_DN_REV (1ul << 20) // XN_FLAG_FN_MASK determines how attribute types are displayed. -#define XN_FLAG_FN_MASK (0x3 << 21) +#define XN_FLAG_FN_MASK (0x3ul << 21) // XN_FLAG_FN_SN uses the attribute type's short name, when available. -#define XN_FLAG_FN_SN 0 +#define XN_FLAG_FN_SN 0ul // XN_FLAG_SPC_EQ wraps the "=" operator with spaces when printing attributes. -#define XN_FLAG_SPC_EQ (1 << 23) +#define XN_FLAG_SPC_EQ (1ul << 23) // XN_FLAG_DUMP_UNKNOWN_FIELDS causes unknown attribute types to be printed in // hex, as in RFC 2253. -#define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24) +#define XN_FLAG_DUMP_UNKNOWN_FIELDS (1ul << 24) // XN_FLAG_RFC2253 prints like RFC 2253. #define XN_FLAG_RFC2253 \ @@ -3787,6 +3825,43 @@ // |flags| should be zero and is ignored. OPENSSL_EXPORT int X509_check_trust(X509 *x509, int id, int flags); +// X509_STORE_CTX_get1_certs returns a newly-allocated stack containing all +// trusted certificates in |ctx|'s |X509_STORE| whose subject matches |name|, or +// NULL on error. The caller must release the result with |sk_X509_pop_free| and +// |X509_free| when done. +// +// TODO(crbug.com/boringssl/407): |name| should be const. +OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, + X509_NAME *name); + +// X509_STORE_CTX_get1_crls returns a newly-allocated stack containing all +// CRLs in |ctx|'s |X509_STORE| whose subject matches |name|, or NULL on error. +// The caller must release the result with |sk_X509_CRL_pop_free| and +// |X509_CRL_free| when done. +// +// TODO(crbug.com/boringssl/407): |name| should be const. +OPENSSL_EXPORT STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *ctx, + X509_NAME *name); + +// X509_STORE_CTX_get_by_subject looks up an object of type |type| in |ctx|'s +// |X509_STORE| that matches |name|. |type| should be one of the |X509_LU_*| +// constants to indicate the type of object. If a match was found, it stores the +// result in |ret| and returns one. Otherwise, it returns zero. If multiple +// objects match, this function outputs an arbitray one. +// +// WARNING: |ret| must be in the empty state, as returned by |X509_OBJECT_new|. +// Otherwise, the object currently in |ret| will be leaked when overwritten. +// https://crbug.com/boringssl/685 tracks fixing this. +// +// WARNING: Multiple trusted certificates or CRLs may share a name. In this +// case, this function returns an arbitrary match. Use +// |X509_STORE_CTX_get1_certs| or |X509_STORE_CTX_get1_crls| instead. +// +// TODO(crbug.com/boringssl/407): |name| should be const. +OPENSSL_EXPORT int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *ctx, int type, + X509_NAME *name, + X509_OBJECT *ret); + // X.509 information. // @@ -4224,6 +4299,20 @@ // always enabled. #define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 +// X509_STORE_get0_objects returns a non-owning pointer of |store|'s internal +// object list. Although this function is not const, callers must not modify +// the result of this function. +// +// WARNING: This function is not thread-safe. If |store| is shared across +// multiple threads, callers cannot safely inspect the result of this function, +// because another thread may have concurrently added to it. In particular, +// |X509_LOOKUP_hash_dir| treats this list as a cache and may add to it in the +// course of certificate verification. This API additionally prevents fixing +// some quadratic worst-case behavior in |X509_STORE| and may be removed in the +// future. Use |X509_STORE_get1_objects| instead. +OPENSSL_EXPORT STACK_OF(X509_OBJECT) *X509_STORE_get0_objects( + X509_STORE *store); + // Private structures. @@ -4324,13 +4413,6 @@ certificate chain. */ -#define X509_LU_NONE 0 -#define X509_LU_X509 1 -#define X509_LU_CRL 2 -#define X509_LU_PKEY 3 - -DEFINE_STACK_OF(X509_OBJECT) - #define X509_STORE_CTX_set_app_data(ctx, data) \ X509_STORE_CTX_set_ex_data(ctx, 0, data) #define X509_STORE_CTX_get_app_data(ctx) X509_STORE_CTX_get_ex_data(ctx, 0) @@ -4407,37 +4489,12 @@ // verification. #define X509_V_FLAG_NO_CHECK_TIME 0x200000 -// X509_OBJECT_new returns a newly-allocated, empty |X509_OBJECT| or NULL on -// error. -OPENSSL_EXPORT X509_OBJECT *X509_OBJECT_new(void); - -// X509_OBJECT_free releases memory associated with |obj|. -OPENSSL_EXPORT void X509_OBJECT_free(X509_OBJECT *obj); - -// X509_OBJECT_get_type returns the type of |obj|, which will be one of the -// |X509_LU_*| constants. -OPENSSL_EXPORT int X509_OBJECT_get_type(const X509_OBJECT *obj); - -// X509_OBJECT_get0_X509 returns |obj| as a certificate, or NULL if |obj| is not -// a certificate. -OPENSSL_EXPORT X509 *X509_OBJECT_get0_X509(const X509_OBJECT *obj); - -OPENSSL_EXPORT STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *st); -OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *st, - X509_NAME *nm); -OPENSSL_EXPORT STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *st, - X509_NAME *nm); - OPENSSL_EXPORT X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, const X509_LOOKUP_METHOD *m); OPENSSL_EXPORT const X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void); OPENSSL_EXPORT const X509_LOOKUP_METHOD *X509_LOOKUP_file(void); -OPENSSL_EXPORT int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, int type, - X509_NAME *name, - X509_OBJECT *ret); - OPENSSL_EXPORT int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret); @@ -4885,6 +4942,7 @@ BORINGSSL_MAKE_DELETER(X509_LOOKUP, X509_LOOKUP_free) BORINGSSL_MAKE_DELETER(X509_NAME, X509_NAME_free) BORINGSSL_MAKE_DELETER(X509_NAME_ENTRY, X509_NAME_ENTRY_free) +BORINGSSL_MAKE_DELETER(X509_OBJECT, X509_OBJECT_free) BORINGSSL_MAKE_DELETER(X509_PUBKEY, X509_PUBKEY_free) BORINGSSL_MAKE_DELETER(X509_REQ, X509_REQ_free) BORINGSSL_MAKE_DELETER(X509_REVOKED, X509_REVOKED_free)
diff --git a/pki/cert_errors.cc b/pki/cert_errors.cc index da95b49..3f5a77f 100644 --- a/pki/cert_errors.cc +++ b/pki/cert_errors.cc
@@ -92,15 +92,20 @@ return result; } -bool CertErrors::ContainsError(CertErrorId id) const { +bool CertErrors::ContainsErrorWithSeverity(CertErrorId id, + CertError::Severity severity) const { for (const CertError &node : nodes_) { - if (node.id == id) { + if (node.id == id && node.severity == severity) { return true; } } return false; } +bool CertErrors::ContainsError(CertErrorId id) const { + return ContainsErrorWithSeverity(id, CertError::SEVERITY_HIGH); +} + bool CertErrors::ContainsAnyErrorWithSeverity( CertError::Severity severity) const { for (const CertError &node : nodes_) {
diff --git a/pki/cert_errors.h b/pki/cert_errors.h index 8f16e11..da35060 100644 --- a/pki/cert_errors.h +++ b/pki/cert_errors.h
@@ -104,8 +104,13 @@ // Dumps a textual representation of the errors for debugging purposes. std::string ToDebugString() const; - // Returns true if the error |id| was added to this CertErrors (of any - // severity). + // Returns true if the error |id| was added to this CertErrors at + // severity |severity| + bool ContainsErrorWithSeverity(CertErrorId id, + CertError::Severity severity) const; + + // Returns true if the error |id| was added to this CertErrors at + // high serverity. bool ContainsError(CertErrorId id) const; // Returns true if this contains any errors of the given severity level.
diff --git a/pki/certificate.cc b/pki/certificate.cc new file mode 100644 index 0000000..db1c627 --- /dev/null +++ b/pki/certificate.cc
@@ -0,0 +1,117 @@ +/* Copyright (c) 2023, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <optional> +#include <string_view> + +#include <openssl/pki/certificate.h> +#include <openssl/pool.h> + +#include "cert_errors.h" +#include "encode_values.h" +#include "parsed_certificate.h" +#include "pem.h" +#include "parse_values.h" + +namespace bssl { + +namespace { + +std::shared_ptr<const bssl::ParsedCertificate> ParseCertificateFromDer( + bssl::Span<const uint8_t>cert, std::string *out_diagnostic) { + bssl::ParseCertificateOptions default_options{}; + // We follow Chromium in setting |allow_invalid_serial_numbers| in order to + // not choke on 21-byte serial numbers, which are common. davidben explains + // why: + // + // The reason for the discrepancy is that unsigned numbers with the high bit + // otherwise set get an extra 0 byte in front to keep them positive. So if you + // do: + // var num [20]byte + // fillWithRandom(num[:]) + // serialNumber := new(big.Int).SetBytes(num[:]) + // encodeASN1Integer(serialNumber) + // + // Then half of your serial numbers will be encoded with 21 bytes. (And + // 1/512th will have 19 bytes instead of 20.) + default_options.allow_invalid_serial_numbers = true; + + bssl::UniquePtr<CRYPTO_BUFFER> buffer( + CRYPTO_BUFFER_new(cert.data(), cert.size(), nullptr)); + bssl::CertErrors errors; + std::shared_ptr<const bssl::ParsedCertificate> parsed_cert( + bssl::ParsedCertificate::Create(std::move(buffer), default_options, &errors)); + if (!parsed_cert) { + *out_diagnostic = errors.ToDebugString(); + return nullptr; + } + return parsed_cert; +} + +} // namespace + +struct CertificateInternals { + std::shared_ptr<const bssl::ParsedCertificate> cert; +}; + +Certificate::Certificate(std::unique_ptr<CertificateInternals> internals) + : internals_(std::move(internals)) {} +Certificate::~Certificate() = default; +Certificate::Certificate(Certificate&& other) = default; + +std::unique_ptr<Certificate> Certificate::FromDER(bssl::Span<const uint8_t> der, + std::string *out_diagnostic) { + std::shared_ptr<const bssl::ParsedCertificate> result = + ParseCertificateFromDer(der, out_diagnostic); + if (result == nullptr) { + return nullptr; + } + + auto internals = std::make_unique<CertificateInternals>(); + internals->cert = std::move(result); + std::unique_ptr<Certificate> ret(new Certificate(std::move(internals))); + return ret; +} + +std::unique_ptr<Certificate> Certificate::FromPEM(std::string_view pem, + std::string *out_diagnostic) { + bssl::PEMTokenizer tokenizer(pem, {"CERTIFICATE"}); + if (!tokenizer.GetNext()) { + return nullptr; + } + return FromDER(StringAsBytes(tokenizer.data()), out_diagnostic); +} + +bool Certificate::IsSelfIssued() const { + return internals_->cert->normalized_subject() == + internals_->cert->normalized_issuer(); +} + +Certificate::Validity Certificate::GetValidity() const { + Certificate::Validity validity; + + // As this is a previously parsed certificate, we know the not_before + // and not after are valid, so these conversions can not fail. + (void) GeneralizedTimeToPosixTime( + internals_->cert->tbs().validity_not_before, &validity.not_before); + (void) GeneralizedTimeToPosixTime( + internals_->cert->tbs().validity_not_after, &validity.not_after); + return validity; +} + +bssl::Span<const uint8_t> Certificate::GetSerialNumber() const { + return internals_->cert->tbs().serial_number; +} + +} // namespace boringssl
diff --git a/pki/certificate_unittest.cc b/pki/certificate_unittest.cc new file mode 100644 index 0000000..57a51fa --- /dev/null +++ b/pki/certificate_unittest.cc
@@ -0,0 +1,76 @@ +/* Copyright (c) 2023, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <optional> +#include <string> +#include <string_view> + +#include <openssl/pki/certificate.h> +#include <gmock/gmock.h> + +#include "string_util.h" +#include "test_helpers.h" + +TEST(CertificateTest, FromPEM) { + std::string diagnostic; + std::unique_ptr<bssl::Certificate> cert( + bssl::Certificate::FromPEM("nonsense", &diagnostic)); + EXPECT_FALSE(cert); + + cert = bssl::Certificate::FromPEM(bssl::ReadTestFileToString( + "testdata/verify_unittest/self-issued.pem"), &diagnostic); + EXPECT_TRUE(cert); +} + +TEST(CertificateTest, IsSelfIssued) { + std::string diagnostic; + const std::string leaf = + bssl::ReadTestFileToString("testdata/verify_unittest/google-leaf.der"); + std::unique_ptr<bssl::Certificate> leaf_cert( + bssl::Certificate::FromDER(bssl::StringAsBytes(leaf), &diagnostic)); + EXPECT_TRUE(leaf_cert); + EXPECT_FALSE(leaf_cert->IsSelfIssued()); + + const std::string self_issued = + bssl::ReadTestFileToString("testdata/verify_unittest/self-issued.pem"); + std::unique_ptr<bssl::Certificate> self_issued_cert( + bssl::Certificate::FromPEM(self_issued, &diagnostic)); + EXPECT_TRUE(self_issued_cert); + EXPECT_TRUE(self_issued_cert->IsSelfIssued()); +} + +TEST(CertificateTest, Validity) { + std::string diagnostic; + const std::string leaf = + bssl::ReadTestFileToString("testdata/verify_unittest/google-leaf.der"); + std::unique_ptr<bssl::Certificate> cert( + bssl::Certificate::FromDER(bssl::StringAsBytes(leaf), &diagnostic)); + EXPECT_TRUE(cert); + + bssl::Certificate::Validity validity = cert->GetValidity(); + EXPECT_EQ(validity.not_before, 1498644466); + EXPECT_EQ(validity.not_after, 1505899620); +} + +TEST(CertificateTest, SerialNumber) { + std::string diagnostic; + const std::string leaf = + bssl::ReadTestFileToString("testdata/verify_unittest/google-leaf.der"); + std::unique_ptr<bssl::Certificate> cert( + bssl::Certificate::FromDER(bssl::StringAsBytes(leaf), &diagnostic)); + EXPECT_TRUE(cert); + + EXPECT_EQ(bssl::string_util::HexEncode(cert->GetSerialNumber()), + "0118F044A8F31892"); +}
diff --git a/pki/mock_signature_verify_cache.h b/pki/mock_signature_verify_cache.h index 80c4fa3..f380f61 100644 --- a/pki/mock_signature_verify_cache.h +++ b/pki/mock_signature_verify_cache.h
@@ -11,8 +11,7 @@ #include <string_view> #include <unordered_map> - -#include "signature_verify_cache.h" +#include <openssl/pki/signature_verify_cache.h> namespace bssl {
diff --git a/pki/parse_certificate.h b/pki/parse_certificate.h index cd38c65..81e267f 100644 --- a/pki/parse_certificate.h +++ b/pki/parse_certificate.h
@@ -415,6 +415,14 @@ // In dotted notation: 2.5.29.31 inline constexpr uint8_t kCrlDistributionPointsOid[] = {0x55, 0x1d, 0x1f}; +// From RFC 6962: +// +// critical poison extension. +// +// In dotted notation 1.3.6.1.4.1.11129.2.4.3 +inline constexpr uint8_t kCtPoisonOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, + 0xD6, 0x79, 0x02, 0x04, 0x03}; + // From // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/supported-extensions#msapplicationpolicies //
diff --git a/pki/path_builder_unittest.cc b/pki/path_builder_unittest.cc index 7747c34..624c0bc 100644 --- a/pki/path_builder_unittest.cc +++ b/pki/path_builder_unittest.cc
@@ -54,9 +54,18 @@ MockSignatureVerifyCache *GetMockVerifyCache() { return &cache_; } - private: + void AllowPrecert() { allow_precertificate_ = true; } + + void DisallowPrecert() { allow_precertificate_ = false; } + + bool AcceptPreCertificates() override { + return allow_precertificate_; + } + +private: bool deadline_is_expired_ = false; bool use_signature_cache_ = false; + bool allow_precertificate_ = false; MockSignatureVerifyCache cache_; }; @@ -907,6 +916,46 @@ EXPECT_EQ(c_by_d_, valid_path->certs[2]); } +TEST_F(PathBuilderMultiRootTest, TestPreCertificate) { + + std::string test_dir = + "testdata/path_builder_unittest/precertificate/"; + std::shared_ptr<const ParsedCertificate> root1 = + ReadCertFromFile(test_dir + "root.pem"); + ASSERT_TRUE(root1); + std::shared_ptr<const ParsedCertificate> target = + ReadCertFromFile(test_dir + "precertificate.pem"); + ASSERT_TRUE(target); + + der::GeneralizedTime precert_time = {2023, 10, 1, 0, 0, 0}; + + TrustStoreInMemory trust_store; + trust_store.AddTrustAnchor(root1); + + // PreCertificate should be rejected by default. + EXPECT_FALSE(delegate_.AcceptPreCertificates()); + CertPathBuilder path_builder( + target, &trust_store, &delegate_, precert_time, KeyPurpose::ANY_EKU, + initial_explicit_policy_, user_initial_policy_set_, + initial_policy_mapping_inhibit_, initial_any_policy_inhibit_); + auto result = path_builder.Run(); + ASSERT_EQ(1U, result.paths.size()); + ASSERT_FALSE(result.paths[0]->IsValid()) + << result.paths[0]->errors.ToDebugString(result.paths[0]->certs); + + // PreCertificate should be accepted if configured. + delegate_.AllowPrecert(); + EXPECT_TRUE(delegate_.AcceptPreCertificates()); + CertPathBuilder path_builder2( + target, &trust_store, &delegate_, precert_time, KeyPurpose::ANY_EKU, + initial_explicit_policy_, user_initial_policy_set_, + initial_policy_mapping_inhibit_, initial_any_policy_inhibit_); + auto result2 = path_builder2.Run(); + ASSERT_EQ(1U, result2.paths.size()); + ASSERT_TRUE(result2.paths[0]->IsValid()) + << result2.paths[0]->errors.ToDebugString(result.paths[0]->certs); +} + class PathBuilderKeyRolloverTest : public ::testing::Test { public: PathBuilderKeyRolloverTest() @@ -2010,7 +2059,8 @@ const CertErrors *cert1_errors = result.GetBestValidPath()->errors.GetErrorsForCert(1); ASSERT_TRUE(cert1_errors); - EXPECT_TRUE(cert1_errors->ContainsError(kWarningFromDelegate)); + EXPECT_TRUE(cert1_errors->ContainsErrorWithSeverity( + kWarningFromDelegate, CertError::SEVERITY_WARNING)); } DEFINE_CERT_ERROR_ID(kErrorFromDelegate, "Error from delegate");
diff --git a/pki/simple_path_builder_delegate.cc b/pki/simple_path_builder_delegate.cc index a168513..822019b 100644 --- a/pki/simple_path_builder_delegate.cc +++ b/pki/simple_path_builder_delegate.cc
@@ -11,11 +11,12 @@ #include <openssl/ec_key.h> #include <openssl/evp.h> #include <openssl/nid.h> +#include <openssl/pki/signature_verify_cache.h> #include <openssl/rsa.h> + #include "cert_error_params.h" #include "cert_errors.h" #include "signature_algorithm.h" -#include "signature_verify_cache.h" #include "verify_signed_data.h" namespace bssl { @@ -55,6 +56,8 @@ bool SimplePathBuilderDelegate::IsDebugLogEnabled() { return false; } +bool SimplePathBuilderDelegate::AcceptPreCertificates() { return false; } + void SimplePathBuilderDelegate::DebugLog(std::string_view msg) {} SignatureVerifyCache *SimplePathBuilderDelegate::GetVerifyCache() {
diff --git a/pki/simple_path_builder_delegate.h b/pki/simple_path_builder_delegate.h index 4c36b09..6d95b0d 100644 --- a/pki/simple_path_builder_delegate.h +++ b/pki/simple_path_builder_delegate.h
@@ -8,10 +8,10 @@ #include <stddef.h> #include <openssl/base.h> +#include <openssl/pki/signature_verify_cache.h> #include "path_builder.h" #include "signature_algorithm.h" -#include "signature_verify_cache.h" namespace bssl { @@ -70,6 +70,9 @@ // No-op implementation. void DebugLog(std::string_view msg) override; + // No-op implementation. + bool AcceptPreCertificates() override; + private: const size_t min_rsa_modulus_length_bits_; const DigestPolicy digest_policy_;
diff --git a/pki/testdata/path_builder_unittest/precertificate/precertificate.pem b/pki/testdata/path_builder_unittest/precertificate/precertificate.pem new file mode 100644 index 0000000..23b9900 --- /dev/null +++ b/pki/testdata/path_builder_unittest/precertificate/precertificate.pem
@@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEfzCCA2egAwIBAgIQDIMDs7tv6W4RteOR0LnF9DANBgkqhkiG9w0BAQsFADBG +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzETMBEGA1UEAxMKR1RTIENBIDFQNTAeFw0yMzA5MTQwMDU5NTFaFw0yMzEyMTMw +MDU5NTBaMBkxFzAVBgNVBAMTDnJmYy1lZGl0b3Iub3JnMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA2jNKh7UQ/v36Qz/KV+QLBYwxI9+kzv3t6363d63V +3EkhIPTLZF8wQ7kfztmFKUuMcLcvEpY1jwxyBioGJN1b9QaxlLCsK8WvEyxnR+nq +pR1h+j+zceZIEZoDVcS2GIr7LoFtFUeGidQRYBRf9Wu6bRMaT2fHaI6FcZECH7Bn +TSe2e62BFejah3pA91+oVlnI9EjKtMQOGEaoyKjrKswVaWcFiUWKcvGnjygkYWRb +6jBUhGINamkCgrtAblHrwB0ym9VENj06fpnWbkxFtU6GmmRplHag5npRwWI439+9 +QNtqj6PgMsSMBLj7ljq8GlY81Y6TZTZ2F06b0NLkRdjetwIDAQABo4IBlDCCAZAw +DgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQC +MAAwHQYDVR0OBBYEFGMbD3hNq55udW3jdGHpx+v2rzbWMB8GA1UdIwQYMBaAFNX8 +ng3fHsrdCJeXbivFX8Ur9ey4MHgGCCsGAQUFBwEBBGwwajA1BggrBgEFBQcwAYYp +aHR0cDovL29jc3AucGtpLmdvb2cvcy9ndHMxcDUvZDJ5N2JUcElndEkwMQYIKwYB +BQUHMAKGJWh0dHA6Ly9wa2kuZ29vZy9yZXBvL2NlcnRzL2d0czFwNS5kZXIwKwYD +VR0RBCQwIoIOcmZjLWVkaXRvci5vcmeCECoucmZjLWVkaXRvci5vcmcwIQYDVR0g +BBowGDAIBgZngQwBAgEwDAYKKwYBBAHWeQIFAzA8BgNVHR8ENTAzMDGgL6Athito +dHRwOi8vY3Jscy5wa2kuZ29vZy9ndHMxcDUvZWV4a0MyUEp4YXcuY3JsMBMGCisG +AQQB1nkCBAMBAf8EAgUAMA0GCSqGSIb3DQEBCwUAA4IBAQAP3c64wNkNq6Nq7HrV +OzaygYS0dZ9gC8EuJsBe591AkPO8/B8y2kyPFRSHaG7IfRDDSyb9KqgtbknBDbHh +GWDF7CS6sid8ulT1kwDd8HKBVCfd37EODrHmzfJhtxMfEcB7FGjxHjcNZ7g5A4K1 +ph0AXHym+hPB0Jz/0MzDMeFpFp7llib3vAYfXz6bS4xYcBMqPmJV+okpHYjF4UXZ +0JIeMMSlypw5FgoMcsMydFW5X1KzoeKwSDhsVet03AM14QqlKv9p9u4MPzzVxYuW +syhj72JMl1YA01MZ06Org8SaLwUtKu0/kq4mysCNA3GYkKwUQIod/ilW65l6dZoS ++lRn +-----END CERTIFICATE-----
diff --git a/pki/testdata/path_builder_unittest/precertificate/root.pem b/pki/testdata/path_builder_unittest/precertificate/root.pem new file mode 100644 index 0000000..ab573a0 --- /dev/null +++ b/pki/testdata/path_builder_unittest/precertificate/root.pem
@@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFjDCCA3SgAwIBAgINAgO8UKMnU/CRgCLt8TANBgkqhkiG9w0BAQsFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMjAwODEzMDAwMDQyWhcNMjcwOTMwMDAw +MDQyWjBGMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzETMBEGA1UEAxMKR1RTIENBIDFQNTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALOC8CSMvy2Hr7LZp676yrpE1ls+/rL3smUW3N4Q6E8tEFha +KIaHoe5qs6DZdU9/oVIBi1WoSlsGSMg2EiWrifnyI1+dYGX5XNq+OuhcbX2c0IQY +hTDNTpvsPNiz4ZbU88ULZduPsHTL9h7zePGslcXdc8MxiIGvdKpv/QzjBZXwxRBP +ZWP6oK/GGD3Fod+XedcFibMwsHSuPZIQa4wVd90LBFf7gQPd6iI01eVWsvDEjUGx +wwLbYuyA0P921IbkBBq2tgwrYnF92a/Z8V76wB7KoBlcVfCA0SoMB4aQnzXjKCtb +7yPIox2kozru/oPcgkwlsE3FUa2em9NbhMIaWukCAwEAAaOCAXYwggFyMA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0T +AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU1fyeDd8eyt0Il5duK8VfxSv17LgwHwYD +VR0jBBgwFoAU5K8rJnEaK0gnhS9SZizv8IkTcT4waAYIKwYBBQUHAQEEXDBaMCYG +CCsGAQUFBzABhhpodHRwOi8vb2NzcC5wa2kuZ29vZy9ndHNyMTAwBggrBgEFBQcw +AoYkaHR0cDovL3BraS5nb29nL3JlcG8vY2VydHMvZ3RzcjEuZGVyMDQGA1UdHwQt +MCswKaAnoCWGI2h0dHA6Ly9jcmwucGtpLmdvb2cvZ3RzcjEvZ3RzcjEuY3JsME0G +A1UdIARGMEQwOAYKKwYBBAHWeQIFAzAqMCgGCCsGAQUFBwIBFhxodHRwczovL3Br +aS5nb29nL3JlcG9zaXRvcnkvMAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAgEA +bGMn7iPf5VJoTYFmkYXffWXlWzcxCCayB12avrHKAbmtv5139lEd15jFC0mhe6HX +02jlRA+LujbdQoJ30o3d9T/768gHmJPuWtC1Pd5LHC2MTex+jHv+TkD98LSzWQIQ +UVzjwCv9twZIUX4JXj8P3Kf+l+d5xQ5EiXjFaVkpoJo6SDYpppSTVS24R7XplrWf +B82mqz4yisCGg8XBQcifLzWODcAHeuGsyWW1y4qn3XHYYWU5hKwyPvd6NvFWn1ep +QW1akKfbOup1gAxjC2l0bwdMFfM3KKUZpG719iDNY7J+xCsJdYna0Twuck82GqGe +RNDNm6YjCD+XoaeeWqX3CZStXXZdKFbRGmZRUQd73j2wyO8weiQtvrizhvZL9/C1 +T//Oxvn2PyonCA8JPiNax+NCLXo25D2YlmA5mOrR22Mq63gJsU4hs463zj6S8ZVc +pDnQwCvIUxX10i+CzQZ0Z5mQdzcKly3FHB700FvpFePqAgnIE9cTcGW/+4ibWiW+ +dwnhp2pOEXW5Hk3xABtqZnmOw27YbaIiom0F+yzy8VDloNHYnzV9/HCrWSoC8b6w +0/H4zRK5aiWQW+OFIOb12stAHBk0IANhd7p/SA9JCynr52Fkx2PRR+sc4e6URu85 +c8zuTyuN3PtYp7NlIJmVuftVb9eWbpQ99HqSjmMd320= +-----END CERTIFICATE-----
diff --git a/pki/testdata/verify_unittest/google-leaf.der b/pki/testdata/verify_unittest/google-leaf.der new file mode 100644 index 0000000..01eea35 --- /dev/null +++ b/pki/testdata/verify_unittest/google-leaf.der Binary files differ
diff --git a/pki/testdata/verify_unittest/self-issued.pem b/pki/testdata/verify_unittest/self-issued.pem new file mode 100644 index 0000000..e219b6d --- /dev/null +++ b/pki/testdata/verify_unittest/self-issued.pem
@@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrTCCApWgAwIBAgIUcu/NYcYTwG79IqzpuKoOf0Jyu1UwDQYJKoZIhvcNAQEL +BQAwZjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEfMB0GA1UEAwwWSXNTZWxmSXNzdWVk +IHRlc3QgY2VydDAeFw0yMjA0MDcyMzIyMzRaFw0yMzA0MDcyMzIyMzRaMGYxCzAJ +BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQxHzAdBgNVBAMMFklzU2VsZklzc3VlZCB0ZXN0IGNl +cnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv6ueRHng0bJsKdDSg +YP1wfpao+dGZ6iY0wfDaLGY2SUgHn7HCvGeS1btZ7lCdCBdRTd6OjrMJkaoPXOCG +rqUVv9/WBPyYgpA+Zlm3F8Uf8vuYUZ96ZOdvHBeAO8Ac9wFoZhC3VNwVF0mynlVb +AhMu1T33Pi387jaE2OMMAle2w4zApcEMbMvlgEk1UdlpL0PBT4nT6XIoCbO04tEG +KLMnGutdHUmVW3L+77hjy/H4LhfsZb7PD3xqFkWpspP3OgfWJU0NUiAX/RIbgKTe +pYNVUElAGgchziGQyDJ8Si7rXslCIx6m/qMD9+JX4hZa+ANPf9psEAVjt8TKhozi +TCBHAgMBAAGjUzBRMB0GA1UdDgQWBBQludvSp1hD9JPiK6JyzuROz9S95zAfBgNV +HSMEGDAWgBQludvSp1hD9JPiK6JyzuROz9S95zAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQA3a4h15aX4xbinxNUNDNRoqrHL5J/nwFgZxTKvzvC+ +I+evxJr3b2vWip70nepiDYW9XyzDD3rr18P/XlacxaBWy2L6iwAc0SDHHUwPJtid +7av3VjC3Qz8kk3gIuLb9SMJ9eQweKzox7UF6ZJhoEhSMDkyvbHmkMYmQGjIwuVvG +GZm0Df1XYiBQyqj6jqsTkZeqlWyFkpuFMOOSvFwWV5rOt0WrIYB/iIbHqXtePGU1 +aE2CZqTTTCldn4gvMjtAAJ5D/JXY788a0apaEG5FjU1XzVHzWbKm0bz7y1cEPBhQ +iEgWK2iloE3NzPABUZIcqdQF5NUiP45i396ccRGuNxY5 +-----END CERTIFICATE-----
diff --git a/pki/verify_certificate_chain.cc b/pki/verify_certificate_chain.cc index 1d3d0c0..c42f757 100644 --- a/pki/verify_certificate_chain.cc +++ b/pki/verify_certificate_chain.cc
@@ -84,9 +84,13 @@ // Adds errors to |errors| if the certificate contains unconsumed _critical_ // extensions. void VerifyNoUnconsumedCriticalExtensions(const ParsedCertificate &cert, - CertErrors *errors) { + CertErrors *errors, + bool allow_precertificate) { for (const auto &it : cert.extensions()) { const ParsedExtension &extension = it.second; + if (allow_precertificate && extension.oid == der::Input(kCtPoisonOid)) { + continue; + } if (extension.critical && !IsHandledCriticalExtension(extension, cert)) { errors->AddError(cert_errors::kUnconsumedCriticalExtension, CreateCertErrorParams2Der("oid", extension.oid, "value", @@ -659,7 +663,7 @@ // Procedure". It does processing for the final certificate (the target cert). void WrapUp(const ParsedCertificate &cert, KeyPurpose required_key_purpose, const std::set<der::Input> &user_initial_policy_set, - CertErrors *errors); + bool allow_precertificate, CertErrors *errors); // Enforces trust anchor constraints compatibile with RFC 5937. // @@ -1165,7 +1169,8 @@ // the certificate. Process any other recognized non-critical // extension present in the certificate that is relevant to path // processing. - VerifyNoUnconsumedCriticalExtensions(cert, errors); + VerifyNoUnconsumedCriticalExtensions(cert, errors, + delegate_->AcceptPreCertificates()); } // Checks if the target certificate has the CA bit set. If it does, add @@ -1196,7 +1201,8 @@ void PathVerifier::WrapUp(const ParsedCertificate &cert, KeyPurpose required_key_purpose, const std::set<der::Input> &user_initial_policy_set, - CertErrors *errors) { + bool allow_precertificate, + CertErrors * errors) { // From RFC 5280 section 6.1.5: // (a) If explicit_policy is not 0, decrement explicit_policy by 1. if (explicit_policy_ > 0) { @@ -1224,7 +1230,7 @@ // // Note that this is duplicated by PrepareForNextCertificate() so as to // directly match the procedures in RFC 5280's section 6.1. - VerifyNoUnconsumedCriticalExtensions(cert, errors); + VerifyNoUnconsumedCriticalExtensions(cert, errors, allow_precertificate); // This calculates the intersection from RFC 5280 section 6.1.5 step g, as // well as applying the deferred recursive node that were skipped earlier in @@ -1325,7 +1331,8 @@ // Extensions may be marked critical or not critical. When trust anchor // constraints are enforced, clients MUST reject certification paths // containing a trust anchor with unrecognized critical extensions. - VerifyNoUnconsumedCriticalExtensions(cert, errors); + VerifyNoUnconsumedCriticalExtensions(cert, errors, + /*allow_precertificate=*/false); } void PathVerifier::ProcessRootCertificate(const ParsedCertificate &cert, @@ -1427,7 +1434,8 @@ // Checking for unknown critical extensions matches Windows, but is stricter // than the Mac verifier. - VerifyNoUnconsumedCriticalExtensions(cert, errors); + VerifyNoUnconsumedCriticalExtensions(cert, errors, + /*allow_precertificate=*/false); } bssl::UniquePtr<EVP_PKEY> PathVerifier::ParseAndCheckPublicKey( @@ -1572,7 +1580,8 @@ if (!is_target_cert) { PrepareForNextCertificate(cert, cert_errors); } else { - WrapUp(cert, required_key_purpose, user_initial_policy_set, cert_errors); + WrapUp(cert, required_key_purpose, user_initial_policy_set, + delegate->AcceptPreCertificates(), cert_errors); } }
diff --git a/pki/verify_certificate_chain.h b/pki/verify_certificate_chain.h index 718eceb..6c4cccf 100644 --- a/pki/verify_certificate_chain.h +++ b/pki/verify_certificate_chain.h
@@ -9,11 +9,11 @@ #include <openssl/base.h> #include <openssl/evp.h> +#include <openssl/pki/signature_verify_cache.h> #include "cert_errors.h" #include "input.h" #include "parsed_certificate.h" -#include "signature_verify_cache.h" namespace bssl { @@ -74,6 +74,13 @@ // is no verification cache. virtual SignatureVerifyCache *GetVerifyCache() = 0; + // This is called to determine if PreCertificates should be accepted, for the + // purpose of validating issued PreCertificates in a path. Most callers should + // return false here. This should never return true for TLS certificate + // validation. If this function returns true the CT precertificate poison + // extension will not prevent the certificate from being validated. + virtual bool AcceptPreCertificates() = 0; + virtual ~VerifyCertificateChainDelegate(); };
diff --git a/pki/verify_signed_data.cc b/pki/verify_signed_data.cc index a30f9d6..4502e2d 100644 --- a/pki/verify_signed_data.cc +++ b/pki/verify_signed_data.cc
@@ -8,14 +8,15 @@ #include <openssl/digest.h> #include <openssl/err.h> #include <openssl/evp.h> +#include <openssl/pki/signature_verify_cache.h> #include <openssl/rsa.h> #include <openssl/sha.h> + #include "cert_errors.h" #include "input.h" #include "parse_values.h" #include "parser.h" #include "signature_algorithm.h" -#include "signature_verify_cache.h" namespace bssl {
diff --git a/pki/verify_signed_data.h b/pki/verify_signed_data.h index e67c919..5ecfcea 100644 --- a/pki/verify_signed_data.h +++ b/pki/verify_signed_data.h
@@ -7,9 +7,9 @@ #include <openssl/base.h> #include <openssl/evp.h> +#include <openssl/pki/signature_verify_cache.h> #include "signature_algorithm.h" -#include "signature_verify_cache.h" namespace bssl {
diff --git a/sources.cmake b/sources.cmake index c65afb9..febdb31 100644 --- a/sources.cmake +++ b/sources.cmake
@@ -359,6 +359,7 @@ pki/cert_error_params.cc pki/cert_errors.cc pki/cert_issuer_source_static.cc + pki/certificate.cc pki/certificate_policies.cc pki/common_cert_errors.cc pki/crl.cc @@ -395,6 +396,7 @@ crypto/test/gtest_main.cc pki/cert_issuer_source_static_unittest.cc + pki/certificate_unittest.cc pki/certificate_policies_unittest.cc pki/crl_unittest.cc pki/encode_values_unittest.cc @@ -1430,6 +1432,8 @@ pki/testdata/path_builder_unittest/multi-root-D-by-D.pem pki/testdata/path_builder_unittest/multi-root-E-by-E.pem pki/testdata/path_builder_unittest/multi-root-F-by-E.pem + pki/testdata/path_builder_unittest/precertificate/precertificate.pem + pki/testdata/path_builder_unittest/precertificate/root.pem pki/testdata/path_builder_unittest/self_issued_prioritization/generate-certs.py pki/testdata/path_builder_unittest/self_issued_prioritization/keys/Root1.key pki/testdata/path_builder_unittest/self_issued_prioritization/keys/Root2.key