diff --git a/crypto/err/x509.errordata b/crypto/err/x509.errordata index e30d667bd7..ab886f6700 100644 --- a/crypto/err/x509.errordata +++ b/crypto/err/x509.errordata @@ -39,6 +39,7 @@ X509,137,SIGNATURE_ALGORITHM_MISMATCH X509,128,UNKNOWN_KEY_TYPE X509,129,UNKNOWN_NID X509,130,UNKNOWN_PURPOSE_ID +X509,145,UNKNOWN_SIGID_ALGS X509,131,UNKNOWN_TRUST_ID X509,132,UNSUPPORTED_ALGORITHM X509,133,WRONG_LOOKUP_TYPE diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index 5de3ed8daa..0fa48b1788 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h @@ -139,10 +139,21 @@ typedef struct { // an |X509_NAME|. DECLARE_ASN1_FUNCTIONS(X509_CINF) +struct x509_sig_info_st { + // NID of message digest. + int digest_nid; + // NID of public key algorithm. + int pubkey_nid; + // Security bits. + int sec_bits; + uint32_t flags; +} /* X509_SIG_INFO */; + struct x509_st { X509_CINF *cert_info; X509_ALGOR *sig_alg; ASN1_BIT_STRING *signature; + X509_SIG_INFO sig_info; CRYPTO_refcount_t references; CRYPTO_EX_DATA ex_data; // These contain copies of various extension values @@ -350,9 +361,9 @@ struct x509_store_ctx_st { X509_STORE_CTX_cleanup_fn cleanup; // The following is built up - int valid; // if 0, rebuild chain - int last_untrusted; // index of last untrusted cert - STACK_OF(X509) *chain; // chain of X509s - built up and trusted + int valid; // if 0, rebuild chain + int last_untrusted; // index of last untrusted cert + STACK_OF(X509) *chain; // chain of X509s - built up and trusted // When something goes wrong, this is why int error_depth; @@ -415,6 +426,9 @@ int x509_digest_sign_algorithm(EVP_MD_CTX *ctx, X509_ALGOR *algor); int x509_digest_verify_init(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg, EVP_PKEY *pkey); +// x509_init_signature_info initializes the signature info for |x509|. +int x509_init_signature_info(X509 *x509); + // Path-building functions. diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c index bfc3ae0152..33307896f3 100644 --- a/crypto/x509/x509_set.c +++ b/crypto/x509/x509_set.c @@ -60,7 +60,9 @@ #include #include +#include "../x509v3/internal.h" #include "internal.h" +#include "openssl/x509v3.h" long X509_get_version(const X509 *x509) { @@ -238,3 +240,77 @@ const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x) { X509_PUBKEY *X509_get_X509_PUBKEY(const X509 *x509) { return x509->cert_info->key; } + +static int X509_SIG_INFO_get(const X509_SIG_INFO *sig_info, int *digest_nid, + int *pubkey_nid, int *sec_bits, uint32_t *flags) { + if (sig_info == NULL) { + OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (digest_nid != NULL) { + *digest_nid = sig_info->digest_nid; + } + if (pubkey_nid != NULL) { + *pubkey_nid = sig_info->pubkey_nid; + } + if (sec_bits != NULL) { + *sec_bits = sig_info->sec_bits; + } + if (flags != NULL) { + *flags = sig_info->flags; + } + return (sig_info->flags & X509_SIG_INFO_VALID) != 0; +} + +int X509_get_signature_info(X509 *x509, int *digest_nid, int *pubkey_nid, + int *sec_bits, uint32_t *flags) { + if (x509 == NULL) { + OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + // The return value of |x509v3_cache_extensions| is not checked because + // |X509_get_signature_info|'s function contract does not encapsulate failures + // if any invalid extensions do exist. + x509v3_cache_extensions(x509); + return X509_SIG_INFO_get(&x509->sig_info, digest_nid, pubkey_nid, sec_bits, + flags); +} + +int x509_init_signature_info(X509 *x509) { + int pubkey_nid, digest_nid; + const EVP_MD *md; + + x509->sig_info.digest_nid = NID_undef; + x509->sig_info.pubkey_nid = NID_undef; + x509->sig_info.sec_bits = -1; + x509->sig_info.flags = 0; + if (!OBJ_find_sigid_algs(OBJ_obj2nid(x509->sig_alg->algorithm), &digest_nid, + &pubkey_nid) || + pubkey_nid == NID_undef) { + OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_SIGID_ALGS); + return 0; + } + x509->sig_info.pubkey_nid = pubkey_nid; + x509->sig_info.digest_nid = digest_nid; + x509->sig_info.flags |= X509_SIG_INFO_VALID; + + md = EVP_get_digestbynid(digest_nid); + if (md == NULL) { + // Some valid signature algorithms have an undefined digest. See + // crypto/obj/obj_xref.c. + return 1; + } + // Security bits: half number of bits in digest. + x509->sig_info.sec_bits = (int)EVP_MD_size(md) * 4; + + switch (digest_nid) { + case NID_sha1: + case NID_sha256: + case NID_sha384: + case NID_sha512: + x509->sig_info.flags |= X509_SIG_INFO_TLS; + } + return 1; +} diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 2d4bb95e85..ea861db1c5 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc @@ -7062,3 +7062,34 @@ TEST(X509Test, GetTextByOBJ) { } } } + +TEST(X509Test, GetSigInfo) { + bssl::UniquePtr cert(CertFromPEM(kLeafPEM)); + ASSERT_TRUE(cert); + + int digest_nid, pubkey_nid, sec_bits; + uint32_t flags; + EXPECT_TRUE(X509_get_signature_info(cert.get(), &digest_nid, &pubkey_nid, + &sec_bits, &flags)); + + EXPECT_EQ(digest_nid, NID_sha256); + EXPECT_EQ(pubkey_nid, NID_rsaEncryption); + EXPECT_EQ(sec_bits, (int)EVP_MD_size(EVP_sha256()) * 4); + EXPECT_TRUE(flags & (X509_SIG_INFO_VALID | X509_SIG_INFO_TLS)); + + cert = CertFromPEM(kEd25519Cert); + EXPECT_TRUE(X509_get_signature_info(cert.get(), &digest_nid, &pubkey_nid, + &sec_bits, &flags)); + EXPECT_EQ(digest_nid, NID_undef); + EXPECT_EQ(pubkey_nid, NID_ED25519); + EXPECT_EQ(sec_bits, -1); + EXPECT_TRUE(flags & X509_SIG_INFO_VALID); + + cert = CertFromPEM(kExampleRsassaPssCert); + EXPECT_TRUE(X509_get_signature_info(cert.get(), &digest_nid, &pubkey_nid, + &sec_bits, &flags)); + EXPECT_EQ(digest_nid, NID_undef); + EXPECT_EQ(pubkey_nid, NID_rsaEncryption); + EXPECT_EQ(sec_bits, -1); + EXPECT_TRUE(flags & X509_SIG_INFO_VALID); +} diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c index 1696225f53..2aa21119a7 100644 --- a/crypto/x509v3/v3_purp.c +++ b/crypto/x509v3/v3_purp.c @@ -132,7 +132,9 @@ static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b) { // As much as I'd like to make X509_check_purpose use a "const" X509* I // really can't because it does recalculate hashes and do other non-const -// things. +// things. If |id| is -1 it just calls |x509v3_cache_extensions| for its +// side-effect. +// Returns 1 on success, 0 if x does not allow purpose, -1 on (internal) error. int X509_check_purpose(X509 *x, int id, int ca) { int idx; const X509_PURPOSE *pt; @@ -550,6 +552,9 @@ int x509v3_cache_extensions(X509 *x) { break; } } + if (!x509_init_signature_info(x)) { + x->ex_flags |= EXFLAG_INVALID; + } x->ex_flags |= EXFLAG_SET; CRYPTO_MUTEX_unlock_write(&x->lock); diff --git a/generated-src/err_data.c b/generated-src/err_data.c index 672078671c..957d14637c 100644 --- a/generated-src/err_data.c +++ b/generated-src/err_data.c @@ -233,9 +233,9 @@ const uint32_t kOpenSSLReasonValues[] = { 0x2c403998, 0x2c4093a9, 0x2c4139a9, - 0x2c41b9bc, + 0x2c41b9cf, 0x2c42136f, - 0x2c42b9cd, + 0x2c42b9e0, 0x2c43076d, 0x2c43b8b3, 0x2c4437fb, @@ -247,6 +247,7 @@ const uint32_t kOpenSSLReasonValues[] = { 0x2c4738ea, 0x2c47b923, 0x2c48380d, + 0x2c48b9bc, 0x30320000, 0x30328015, 0x3033001f, @@ -705,71 +706,71 @@ const uint32_t kOpenSSLReasonValues[] = { 0x4c4197bc, 0x4c421925, 0x4c429704, - 0x503239df, - 0x5032b9ee, - 0x503339f9, - 0x5033ba09, - 0x50343a22, - 0x5034ba3c, - 0x50353a4a, - 0x5035ba60, - 0x50363a72, - 0x5036ba88, - 0x50373aa1, - 0x5037bab4, - 0x50383acc, - 0x5038badd, - 0x50393af2, - 0x5039bb06, - 0x503a3b26, - 0x503abb3c, - 0x503b3b54, - 0x503bbb66, - 0x503c3b82, - 0x503cbb99, - 0x503d3bb2, - 0x503dbbc8, - 0x503e3bd5, - 0x503ebbeb, - 0x503f3bfd, + 0x503239f2, + 0x5032ba01, + 0x50333a0c, + 0x5033ba1c, + 0x50343a35, + 0x5034ba4f, + 0x50353a5d, + 0x5035ba73, + 0x50363a85, + 0x5036ba9b, + 0x50373ab4, + 0x5037bac7, + 0x50383adf, + 0x5038baf0, + 0x50393b05, + 0x5039bb19, + 0x503a3b39, + 0x503abb4f, + 0x503b3b67, + 0x503bbb79, + 0x503c3b95, + 0x503cbbac, + 0x503d3bc5, + 0x503dbbdb, + 0x503e3be8, + 0x503ebbfe, + 0x503f3c10, 0x503f83b3, - 0x50403c10, - 0x5040bc20, - 0x50413c3a, - 0x5041bc49, - 0x50423c63, - 0x5042bc80, - 0x50433c90, - 0x5043bca0, - 0x50443cbd, + 0x50403c23, + 0x5040bc33, + 0x50413c4d, + 0x5041bc5c, + 0x50423c76, + 0x5042bc93, + 0x50433ca3, + 0x5043bcb3, + 0x50443cd0, 0x50448469, - 0x50453cd1, - 0x5045bcef, - 0x50463d02, - 0x5046bd18, - 0x50473d2a, - 0x5047bd3f, - 0x50483d65, - 0x5048bd73, - 0x50493d86, - 0x5049bd9b, - 0x504a3db1, - 0x504abdc1, - 0x504b3de1, - 0x504bbdf4, - 0x504c3e17, - 0x504cbe45, - 0x504d3e72, - 0x504dbe8f, - 0x504e3eaa, - 0x504ebec6, - 0x504f3ed8, - 0x504fbeef, - 0x50503efe, + 0x50453ce4, + 0x5045bd02, + 0x50463d15, + 0x5046bd2b, + 0x50473d3d, + 0x5047bd52, + 0x50483d78, + 0x5048bd86, + 0x50493d99, + 0x5049bdae, + 0x504a3dc4, + 0x504abdd4, + 0x504b3df4, + 0x504bbe07, + 0x504c3e2a, + 0x504cbe58, + 0x504d3e85, + 0x504dbea2, + 0x504e3ebd, + 0x504ebed9, + 0x504f3eeb, + 0x504fbf02, + 0x50503f11, 0x50508729, - 0x50513f11, - 0x5051bcaf, - 0x50523e57, + 0x50513f24, + 0x5051bcc2, + 0x50523e6a, 0x583210b7, 0x5c3293b5, 0x5c3313ce, @@ -1534,6 +1535,7 @@ const char kOpenSSLReasonStringData[] = "SIGNATURE_ALGORITHM_MISMATCH\0" "UNKNOWN_KEY_TYPE\0" "UNKNOWN_PURPOSE_ID\0" + "UNKNOWN_SIGID_ALGS\0" "UNKNOWN_TRUST_ID\0" "WRONG_LOOKUP_TYPE\0" "BAD_IP_ADDRESS\0" diff --git a/include/openssl/base.h b/include/openssl/base.h index b643bfea39..6c5d0afb93 100644 --- a/include/openssl/base.h +++ b/include/openssl/base.h @@ -300,6 +300,7 @@ typedef struct X509_name_entry_st X509_NAME_ENTRY; typedef struct X509_name_st X509_NAME; typedef struct X509_pubkey_st X509_PUBKEY; typedef struct X509_req_st X509_REQ; +typedef struct x509_sig_info_st X509_SIG_INFO; typedef struct X509_sig_st X509_SIG; typedef struct bignum_ctx BN_CTX; typedef struct bignum_st BIGNUM; diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 9e3091a4cf..99ae0187b3 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -275,6 +275,22 @@ OPENSSL_EXPORT void *X509_get_ext_d2i(const X509 *x509, int nid, // but they will be rejected when verifying. OPENSSL_EXPORT const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x509); +// X509_SIG_INFO_* are flags for |X509_get_signature_info|. +// X509_SIG_INFO_VALID means that the signature info is valid. +#define X509_SIG_INFO_VALID 0x1 +// X509_SIG_INFO_TLS means that the signature is suitable for TLS use. +#define X509_SIG_INFO_TLS 0x2 + +// X509_get_signature_info retrieves information about the signature of |x509|. +// The NID of the signing digest is written to |digest_nid|, the public key +// algorithm to |pubkey_nid|, the effective security bits to |sec_bits|, and +// flag details to |flags|. Parameters other than |x509| can be set to NULL if +// the information is not required. It is an error to pass a null pointer to +// |x509|. +OPENSSL_EXPORT int X509_get_signature_info(X509 *x509, int *digest_nid, + int *pubkey_nid, int *sec_bits, + uint32_t *flags); + // X509_get0_signature sets |*out_sig| and |*out_alg| to the signature and // signature algorithm of |x509|, respectively. Either output pointer may be // NULL to ignore the value. @@ -2279,7 +2295,8 @@ OPENSSL_EXPORT int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase); // This function outputs a legacy format that does not correctly handle string // encodings and other cases. Prefer |X509_NAME_print_ex| if printing a name for // debugging purposes. -OPENSSL_EXPORT char *X509_NAME_oneline(const X509_NAME *name, char *buf, int size); +OPENSSL_EXPORT char *X509_NAME_oneline(const X509_NAME *name, char *buf, + int size); // X509_NAME_print_ex_fp behaves like |X509_NAME_print_ex| but writes to |fp|. OPENSSL_EXPORT int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, @@ -3259,8 +3276,7 @@ OPENSSL_EXPORT int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, // may be |X509_LU_X509| or |X509_LU_CRL|, and the subject name from the store // in |vs|. If found and |ret| is not NULL, it increments the reference count // and stores the object in |ret|. -OPENSSL_EXPORT int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, - int type, +OPENSSL_EXPORT int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name, X509_OBJECT *ret); @@ -3433,5 +3449,6 @@ BSSL_NAMESPACE_END #define X509_R_NO_CERTIFICATE_OR_CRL_FOUND 142 #define X509_R_NO_CRL_FOUND 143 #define X509_R_INVALID_POLICY_EXTENSION 144 +#define X509_R_UNKNOWN_SIGID_ALGS 145 #endif // OPENSSL_HEADER_X509_H diff --git a/tests/ci/integration/postgres_patch/aws-lc-postgres.patch b/tests/ci/integration/postgres_patch/aws-lc-postgres.patch new file mode 100644 index 0000000000..6ba8b59473 --- /dev/null +++ b/tests/ci/integration/postgres_patch/aws-lc-postgres.patch @@ -0,0 +1,33 @@ +diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl +index dd93224124..44f570c8e2 100644 +--- a/src/test/ssl/t/002_scram.pl ++++ b/src/test/ssl/t/002_scram.pl +@@ -155,14 +155,18 @@ $node->connect_ok( + # Now test with a server certificate that uses the RSA-PSS algorithm. + # This checks that the certificate can be loaded and that channel binding + # works. (see bug #17760) +-if ($supports_rsapss_certs) +-{ +- switch_server_cert($node, certfile => 'server-rsapss'); +- $node->connect_ok( +- "$common_connstr user=ssltestuser channel_binding=require", +- "SCRAM with SSL and channel_binding=require, server certificate uses 'rsassaPss'", +- log_like => [ +- qr/connection authenticated: identity="ssltestuser" method=scram-sha-256/ +- ]); +-} ++# ++# AWS-LC does not support RSA-PSS certificates in libssl. If there is a relevant ++# feature request for this, cut an issue to our public repository. ++# ++# if ($supports_rsapss_certs) ++# { ++# switch_server_cert($node, certfile => 'server-rsapss'); ++# $node->connect_ok( ++# "$common_connstr user=ssltestuser channel_binding=require", ++# "SCRAM with SSL and channel_binding=require, server certificate uses 'rsassaPss'", ++# log_like => [ ++# qr/connection authenticated: identity="ssltestuser" method=scram-sha-256/ ++# ]); ++# } + done_testing(); diff --git a/tests/ci/integration/run_postgres_integration.sh b/tests/ci/integration/run_postgres_integration.sh index 20aa6f7603..506208bf48 100755 --- a/tests/ci/integration/run_postgres_integration.sh +++ b/tests/ci/integration/run_postgres_integration.sh @@ -17,6 +17,7 @@ source tests/ci/common_posix_setup.sh SCRATCH_FOLDER=${SRC_ROOT}/"POSTGRES_BUILD_ROOT" POSTGRES_SRC_FOLDER="${SCRATCH_FOLDER}/postgres" POSTGRES_BUILD_FOLDER="${SCRATCH_FOLDER}/postgres/build" +POSTGRES_PATCH_FOLDER="${SRC_ROOT}/tests/ci/integration/postgres_patch" AWS_LC_BUILD_FOLDER="${SCRATCH_FOLDER}/aws-lc-build" AWS_LC_INSTALL_FOLDER="${POSTGRES_SRC_FOLDER}/aws-lc-install" @@ -47,6 +48,10 @@ function postgres_patch() { for i in "${!POSTGRES_ERROR_STRING[@]}"; do find ./ -type f -name "001_ssltests.pl" | xargs sed -i -e "s|${POSTGRES_ERROR_STRING[$i]}|${AWS_LC_EXPECTED_ERROR_STRING[$i]}|g" done + for patchfile in $(find -L "${POSTGRES_PATCH_FOLDER}" -type f -name '*.patch'); do + echo "Apply patch $patchfile..." + patch -p1 --quiet -i "$patchfile" + done } # Get latest postgres version.