crypto/x509: Add convenience function to verify signature from byte span Change-Id: I9e476670eeeaa74c42c194dad5914a9e6a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/100449 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Lily Chen <chlily@google.com>
diff --git a/crypto/x509/a_verify.cc b/crypto/x509/a_verify.cc index 052c521..e7b8bd9 100644 --- a/crypto/x509/a_verify.cc +++ b/crypto/x509/a_verify.cc
@@ -34,23 +34,30 @@ int bssl::x509_verify_signature(const X509_ALGOR *sigalg, const ASN1_BIT_STRING *signature, Span<const uint8_t> in, EVP_PKEY *pkey) { - if (!pkey) { - OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER); - return 0; - } - if (signature->type == V_ASN1_BIT_STRING && ASN1_BIT_STRING_unused_bits(signature) != 0) { OPENSSL_PUT_ERROR(X509, X509_R_INVALID_BIT_STRING_BITS_LEFT); return 0; } + Span<const uint8_t> signature_bytes(ASN1_STRING_get0_data(signature), + ASN1_STRING_length(signature)); + return x509_verify_signature_bytes(sigalg, signature_bytes, in, pkey); +} + +int bssl::x509_verify_signature_bytes(const X509_ALGOR *sigalg, + Span<const uint8_t> signature, + Span<const uint8_t> in, EVP_PKEY *pkey) { + if (!pkey) { + OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } ScopedEVP_MD_CTX ctx; if (!x509_digest_verify_init(ctx.get(), sigalg, pkey)) { return 0; } - if (!EVP_DigestVerify(ctx.get(), ASN1_STRING_get0_data(signature), - ASN1_STRING_length(signature), in.data(), in.size())) { + if (!EVP_DigestVerify(ctx.get(), signature.data(), signature.size(), + in.data(), in.size())) { OPENSSL_PUT_ERROR(X509, ERR_R_EVP_LIB); return 0; }
diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h index f9a09d3..c809d9c 100644 --- a/crypto/x509/internal.h +++ b/crypto/x509/internal.h
@@ -469,6 +469,13 @@ const ASN1_BIT_STRING *signature, Span<const uint8_t> in, EVP_PKEY *pkey); +// x509_verify_signature_bytes behaves like `x509_verify_signature` but takes in +// a span containing the signature bytes (without ASN.1 headers). It returns one +// if the signature is valid and zero on error. +int x509_verify_signature_bytes(const X509_ALGOR *sigalg, + Span<const uint8_t> signature, + Span<const uint8_t> in, EVP_PKEY *pkey); + // x509_sign_to_bit_string signs `in` using `ctx` and saves the result in `out`. // It returns the length of the signature on success and zero on error. int x509_sign_to_bit_string(EVP_MD_CTX *ctx, ASN1_BIT_STRING *out,
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc index 4b571d3..de8e101 100644 --- a/crypto/x509/x509_test.cc +++ b/crypto/x509/x509_test.cc
@@ -3673,6 +3673,32 @@ X509_R_SIGNATURE_ALGORITHM_MISMATCH)); } +TEST(X509Test, VerifyUnusedBits) { + UniquePtr<X509> cert(CertFromPEM(kLeafPEM)); + ASSERT_TRUE(cert); + UniquePtr<X509> issuer(CertFromPEM(kIntermediatePEM)); + ASSERT_TRUE(issuer); + UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(issuer.get())); + ASSERT_TRUE(pkey); + ASSERT_TRUE(X509_verify(cert.get(), pkey.get())); + + X509Impl *impl = FromOpaque(cert.get()); + const uint8_t *data = ASN1_STRING_get0_data(impl->signature.get()); + int len = ASN1_STRING_length(impl->signature.get()); + ASSERT_TRUE(data); + ASSERT_GT(len, 0); + + std::vector<uint8_t> sig_bytes(data, data + len); + sig_bytes[len - 1] &= 0xf0; // Ensure lower bits are 0 for set1. + ASSERT_TRUE(ASN1_BIT_STRING_set1(impl->signature.get(), sig_bytes.data(), + sig_bytes.size(), 4)); // Set 4 unused bits. + + ERR_clear_error(); + EXPECT_FALSE(X509_verify(cert.get(), pkey.get())); + EXPECT_TRUE(ErrorEquals(ERR_get_error(), ERR_LIB_X509, + X509_R_INVALID_BIT_STRING_BITS_LEFT)); +} + // TODO(crbug.com/387737061): Test that this function can decrypt certificates // and CRLs, even though it leaves encrypted private keys alone. TEST(X509Test, PEMX509Info) {