Add support for legacy ECDSA private keys to certificate_view.h

NOKEYCHECK

PiperOrigin-RevId: 323862773
Change-Id: If8a9265debe9f3fd2b2c22ea4eaffb01a05c787e
diff --git a/quic/core/crypto/certificate_view.cc b/quic/core/crypto/certificate_view.cc
index af6b54c..fa6b30c 100644
--- a/quic/core/crypto/certificate_view.cc
+++ b/quic/core/crypto/certificate_view.cc
@@ -13,6 +13,7 @@
 #include "third_party/boringssl/src/include/openssl/bytestring.h"
 #include "third_party/boringssl/src/include/openssl/digest.h"
 #include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ec_key.h"
 #include "third_party/boringssl/src/include/openssl/evp.h"
 #include "third_party/boringssl/src/include/openssl/nid.h"
 #include "third_party/boringssl/src/include/openssl/rsa.h"
@@ -459,6 +460,7 @@
 
 std::unique_ptr<CertificatePrivateKey> CertificatePrivateKey::LoadPemFromStream(
     std::istream* input) {
+skip:
   PemReadResult result = ReadNextPemMessage(input);
   if (result.status != PemReadResult::kOk) {
     return nullptr;
@@ -480,6 +482,26 @@
     EVP_PKEY_assign_RSA(key->private_key_.get(), rsa.release());
     return key;
   }
+  // EC keys are sometimes generated with "openssl ecparam -genkey". If the user
+  // forgets -noout, OpenSSL will output a redundant copy of the EC parameters.
+  // Skip those.
+  if (result.type == "EC PARAMETERS") {
+    goto skip;
+  }
+  // Legacy OpenSSL format: RFC 5915 ECPrivateKey message.
+  if (result.type == "EC PRIVATE KEY") {
+    CBS private_key_cbs = StringPieceToCbs(result.contents);
+    bssl::UniquePtr<EC_KEY> ec_key(
+        EC_KEY_parse_private_key(&private_key_cbs, /*group=*/nullptr));
+    if (ec_key == nullptr || CBS_len(&private_key_cbs) != 0) {
+      return nullptr;
+    }
+
+    std::unique_ptr<CertificatePrivateKey> key(new CertificatePrivateKey());
+    key->private_key_.reset(EVP_PKEY_new());
+    EVP_PKEY_assign_EC_KEY(key->private_key_.get(), ec_key.release());
+    return key;
+  }
   // Unknown format.
   return nullptr;
 }
diff --git a/quic/core/crypto/certificate_view_test.cc b/quic/core/crypto/certificate_view_test.cc
index ad51214..e5ad8e9 100644
--- a/quic/core/crypto/certificate_view_test.cc
+++ b/quic/core/crypto/certificate_view_test.cc
@@ -131,6 +131,13 @@
   EXPECT_TRUE(legacy_key->MatchesPublicKey(*view));
 }
 
+TEST(CertificateViewTest, PrivateKeyEcdsaPem) {
+  std::stringstream pem_stream(kTestEcPrivateKeyLegacyPem);
+  std::unique_ptr<CertificatePrivateKey> key =
+      CertificatePrivateKey::LoadPemFromStream(&pem_stream);
+  ASSERT_TRUE(key != nullptr);
+}
+
 TEST(CertificateViewTest, DerTime) {
   EXPECT_THAT(ParseDerTime(CBS_ASN1_GENERALIZEDTIME, "19700101000024Z"),
               Optional(QuicWallTime::FromUNIXSeconds(24)));
diff --git a/quic/test_tools/test_certificates.cc b/quic/test_tools/test_certificates.cc
index d179d00..025a816 100644
--- a/quic/test_tools/test_certificates.cc
+++ b/quic/test_tools/test_certificates.cc
@@ -720,5 +720,15 @@
     kWildcardCertificatePrivateKeyRaw,
     sizeof(kWildcardCertificatePrivateKeyRaw));
 
+QUIC_CONST_INIT const char kTestEcPrivateKeyLegacyPem[] =
+    R"(-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIMdjXX0hg399DlccZuYFXPKq+dMGduXWmQYClDYJNDGroAoGCCqGSM49
+AwEHoUQDQgAENCuPQTywFI8hbsGo68AeN1KVWmd09buzlu/2CAtsJcNoECUmpVXH
+4dwvWMv6zWn9RJ5EzI72R/5FVcO485s5MQ==
+-----END EC PRIVATE KEY-----)";
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/test_certificates.h b/quic/test_tools/test_certificates.h
index e7d3035..f617941 100644
--- a/quic/test_tools/test_certificates.h
+++ b/quic/test_tools/test_certificates.h
@@ -43,6 +43,9 @@
 QUIC_CONST_INIT extern const quiche::QuicheStringPiece
     kWildcardCertificatePrivateKey;
 
+// PEM-encoded P-256 private key using legacy OpenSSL encoding.
+QUIC_CONST_INIT extern const char kTestEcPrivateKeyLegacyPem[];
+
 }  // namespace test
 }  // namespace quic