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) {