Don't depend on the X509 code for getting public keys.

This change removes the use of |X509_get_pubkey| from the TLS <= 1.2
code. That function is replaced with a shallow parse of the certificate
to extract the public key instead.

Change-Id: I8938c6c5a01b32038c6b6fa58eb065e5b44ca6d2
Reviewed-on: https://boringssl-review.googlesource.com/12707
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index e9b2066..bac2f39 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -22,6 +22,7 @@
 SSL,120,BN_LIB
 SSL,255,BUFFERED_MESSAGES_ON_CIPHER_CHANGE
 SSL,121,BUFFER_TOO_SMALL
+SSL,272,CANNOT_PARSE_LEAF_CERT
 SSL,122,CA_DN_LENGTH_MISMATCH
 SSL,123,CA_DN_TOO_LONG
 SSL,124,CCS_RECEIVED_EARLY
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 49a3ffd..58f8d64 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4587,6 +4587,7 @@
 #define SSL_R_INVALID_SCT_LIST 269
 #define SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA 270
 #define SSL_R_PSK_IDENTITY_BINDER_COUNT_MISMATCH 271
+#define SSL_R_CANNOT_PARSE_LEAF_CERT 272
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 5949020..352ddc9 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -1044,8 +1044,10 @@
 
   uint8_t alert;
   sk_CRYPTO_BUFFER_pop_free(ssl->s3->new_session->certs, CRYPTO_BUFFER_free);
-  ssl->s3->new_session->certs =
-      ssl_parse_cert_chain(&alert, NULL, &cbs, ssl->ctx->pool);
+  EVP_PKEY_free(hs->peer_pubkey);
+  hs->peer_pubkey = NULL;
+  ssl->s3->new_session->certs = ssl_parse_cert_chain(
+      &alert, &hs->peer_pubkey, NULL, &cbs, ssl->ctx->pool);
   if (ssl->s3->new_session->certs == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return -1;
@@ -1123,7 +1125,6 @@
 static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   int al;
-  EVP_PKEY *pkey = NULL;
   DH *dh = NULL;
   EC_KEY *ecdh = NULL;
   EC_POINT *srvr_ecpoint = NULL;
@@ -1276,11 +1277,6 @@
 
   /* ServerKeyExchange should be signed by the server's public key. */
   if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-    pkey = X509_get_pubkey(ssl->s3->new_session->x509_peer);
-    if (pkey == NULL) {
-      goto err;
-    }
-
     uint16_t signature_algorithm = 0;
     if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
       if (!CBS_get_u16(&server_key_exchange, &signature_algorithm)) {
@@ -1292,9 +1288,9 @@
         goto f_err;
       }
       ssl->s3->tmp.peer_signature_algorithm = signature_algorithm;
-    } else if (pkey->type == EVP_PKEY_RSA) {
+    } else if (hs->peer_pubkey->type == EVP_PKEY_RSA) {
       signature_algorithm = SSL_SIGN_RSA_PKCS1_MD5_SHA1;
-    } else if (pkey->type == EVP_PKEY_EC) {
+    } else if (hs->peer_pubkey->type == EVP_PKEY_EC) {
       signature_algorithm = SSL_SIGN_ECDSA_SHA1;
     } else {
       al = SSL_AD_UNSUPPORTED_CERTIFICATE;
@@ -1327,7 +1323,7 @@
 
     int sig_ok = ssl_public_key_verify(
         ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
-        pkey, transcript_data, transcript_len);
+        hs->peer_pubkey, transcript_data, transcript_len);
     OPENSSL_free(transcript_data);
 
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
@@ -1350,13 +1346,11 @@
       goto f_err;
     }
   }
-  EVP_PKEY_free(pkey);
   return 1;
 
 f_err:
   ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
-  EVP_PKEY_free(pkey);
   DH_free(dh);
   EC_POINT_free(srvr_ecpoint);
   EC_KEY_free(ecdh);
@@ -1557,20 +1551,12 @@
       goto err;
     }
 
-    EVP_PKEY *pkey = X509_get_pubkey(ssl->s3->new_session->x509_peer);
-    if (pkey == NULL) {
-      goto err;
-    }
-
-    RSA *rsa = EVP_PKEY_get0_RSA(pkey);
+    RSA *rsa = EVP_PKEY_get0_RSA(hs->peer_pubkey);
     if (rsa == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      EVP_PKEY_free(pkey);
       goto err;
     }
 
-    EVP_PKEY_free(pkey);
-
     pms[0] = hs->client_version >> 8;
     pms[1] = hs->client_version & 0xff;
     if (!RAND_bytes(&pms[2], SSL_MAX_MASTER_KEY_LENGTH - 2)) {
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 8847251..7f4a627 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -1468,11 +1468,14 @@
   CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
 
   sk_CRYPTO_BUFFER_pop_free(ssl->s3->new_session->certs, CRYPTO_BUFFER_free);
+  EVP_PKEY_free(hs->peer_pubkey);
+  hs->peer_pubkey = NULL;
   uint8_t alert;
   ssl->s3->new_session->certs =
-      ssl_parse_cert_chain(&alert, ssl->retain_only_sha256_of_client_certs
-                                       ? ssl->s3->new_session->peer_sha256
-                                       : NULL,
+      ssl_parse_cert_chain(&alert, &hs->peer_pubkey,
+                           ssl->retain_only_sha256_of_client_certs
+                               ? ssl->s3->new_session->peer_sha256
+                               : NULL,
                            &certificate_msg, ssl->ctx->pool);
   if (ssl->s3->new_session->certs == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -1794,15 +1797,13 @@
 
 static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int al, ret = 0;
+  int al;
   CBS certificate_verify, signature;
-  X509 *peer = ssl->s3->new_session->x509_peer;
-  EVP_PKEY *pkey = NULL;
 
   /* Only RSA and ECDSA client certificates are supported, so a
    * CertificateVerify is required if and only if there's a client certificate.
    * */
-  if (peer == NULL) {
+  if (hs->peer_pubkey == NULL) {
     ssl3_free_handshake_buffer(ssl);
     return 1;
   }
@@ -1813,12 +1814,6 @@
     return msg_ret;
   }
 
-  /* Filter out unsupported certificate types. */
-  pkey = X509_get_pubkey(peer);
-  if (pkey == NULL) {
-    goto err;
-  }
-
   CBS_init(&certificate_verify, ssl->init_msg, ssl->init_num);
 
   /* Determine the digest type if needbe. */
@@ -1833,9 +1828,9 @@
       goto f_err;
     }
     ssl->s3->tmp.peer_signature_algorithm = signature_algorithm;
-  } else if (pkey->type == EVP_PKEY_RSA) {
+  } else if (hs->peer_pubkey->type == EVP_PKEY_RSA) {
     signature_algorithm = SSL_SIGN_RSA_PKCS1_MD5_SHA1;
-  } else if (pkey->type == EVP_PKEY_EC) {
+  } else if (hs->peer_pubkey->type == EVP_PKEY_EC) {
     signature_algorithm = SSL_SIGN_ECDSA_SHA1;
   } else {
     al = SSL_AD_UNSUPPORTED_CERTIFICATE;
@@ -1863,7 +1858,7 @@
       goto err;
     }
 
-    EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pkey, NULL);
+    EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(hs->peer_pubkey, NULL);
     sig_ok = pctx != NULL &&
              EVP_PKEY_verify_init(pctx) &&
              EVP_PKEY_CTX_set_signature_md(pctx, md) &&
@@ -1873,7 +1868,7 @@
   } else {
     sig_ok = ssl_public_key_verify(
         ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
-        pkey, (const uint8_t *)ssl->s3->handshake_buffer->data,
+        hs->peer_pubkey, (const uint8_t *)ssl->s3->handshake_buffer->data,
         ssl->s3->handshake_buffer->length);
   }
 
@@ -1894,17 +1889,12 @@
     goto err;
   }
 
-  ret = 1;
+  return 1;
 
-  if (0) {
-  f_err:
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  }
-
+f_err:
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
-  EVP_PKEY_free(pkey);
-
-  return ret;
+  return 0;
 }
 
 /* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
diff --git a/ssl/internal.h b/ssl/internal.h
index 3640b65..98c5cdf 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -757,10 +757,15 @@
 /* ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
  * by a TLS Certificate message. On success, it returns a newly-allocated
  * |CRYPTO_BUFFER| list and advances |cbs|. Otherwise, it returns NULL and sets
- * |*out_alert| to an alert to send to the peer. If the list is non-empty and
- * |out_leaf_sha256| is non-NULL, it writes the SHA-256 hash of the leaf to
- * |out_leaf_sha256|. */
+ * |*out_alert| to an alert to send to the peer.
+ *
+ * If the list is non-empty then |*out_pubkey| will be set to a freshly
+ * allocated public-key from the leaf certificate.
+ *
+ * If the list is non-empty and |out_leaf_sha256| is non-NULL, it writes the
+ * SHA-256 hash of the leaf to |out_leaf_sha256|. */
 STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert,
+                                              EVP_PKEY **out_pubkey,
                                               uint8_t *out_leaf_sha256,
                                               CBS *cbs,
                                               CRYPTO_BUFFER_POOL *pool);
@@ -774,6 +779,11 @@
  * empty certificate list. It returns one on success and zero on error. */
 int ssl_add_cert_chain(SSL *ssl, CBB *cbb);
 
+/* ssl_cert_parse_pubkey extracts the public key from the DER-encoded, X.509
+ * certificate in |in|. It returns an allocated |EVP_PKEY| or else returns NULL
+ * and pushes to the error queue. */
+EVP_PKEY *ssl_cert_parse_pubkey(const CBS *in);
+
 /* ssl_parse_client_CA_list parses a CA list from |cbs| in the format used by a
  * TLS CertificateRequest message. On success, it returns a newly-allocated
  * |X509_NAME| list and advances |cbs|. Otherwise, it returns NULL and sets
@@ -980,6 +990,9 @@
   /* hostname, on the server, is the value of the SNI extension. */
   char *hostname;
 
+  /* peer_pubkey is the public key parsed from the peer's leaf certificate. */
+  EVP_PKEY *peer_pubkey;
+
   /* key_block is the record-layer key block for TLS 1.2 and earlier. */
   uint8_t *key_block;
   uint8_t key_block_len;
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index eeccab1..4800f92 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -171,6 +171,7 @@
   }
 
   OPENSSL_free(hs->hostname);
+  EVP_PKEY_free(hs->peer_pubkey);
   OPENSSL_free(hs);
 }
 
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 7711cd1..f9ee793 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -120,8 +120,9 @@
 
 #include <openssl/bn.h>
 #include <openssl/buf.h>
-#include <openssl/ec_key.h>
+#include <openssl/bytestring.h>
 #include <openssl/dh.h>
+#include <openssl/ec_key.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 #include <openssl/sha.h>
@@ -463,9 +464,12 @@
 }
 
 STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert,
+                                              EVP_PKEY **out_pubkey,
                                               uint8_t *out_leaf_sha256,
                                               CBS *cbs,
                                               CRYPTO_BUFFER_POOL *pool) {
+  *out_pubkey = NULL;
+
   STACK_OF(CRYPTO_BUFFER) *ret = sk_CRYPTO_BUFFER_new_null();
   if (ret == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
@@ -489,9 +493,16 @@
       goto err;
     }
 
-    /* Retain the hash of the leaf certificate if requested. */
-    if (sk_CRYPTO_BUFFER_num(ret) == 0 && out_leaf_sha256 != NULL) {
-      SHA256(CBS_data(&certificate), CBS_len(&certificate), out_leaf_sha256);
+    if (sk_CRYPTO_BUFFER_num(ret) == 0) {
+      *out_pubkey = ssl_cert_parse_pubkey(&certificate);
+      if (*out_pubkey == NULL) {
+        goto err;
+      }
+
+      /* Retain the hash of the leaf certificate if requested. */
+      if (out_leaf_sha256 != NULL) {
+        SHA256(CBS_data(&certificate), CBS_len(&certificate), out_leaf_sha256);
+      }
     }
 
     CRYPTO_BUFFER *buf =
@@ -512,6 +523,8 @@
   return ret;
 
 err:
+  EVP_PKEY_free(*out_pubkey);
+  *out_pubkey = NULL;
   sk_CRYPTO_BUFFER_pop_free(ret, CRYPTO_BUFFER_free);
   return NULL;
 }
@@ -594,6 +607,50 @@
   return CBB_flush(cbb);
 }
 
+EVP_PKEY *ssl_cert_parse_pubkey(const CBS *in) {
+  /* From RFC 5280, section 4.1
+   *    Certificate  ::=  SEQUENCE  {
+   *      tbsCertificate       TBSCertificate,
+   *      signatureAlgorithm   AlgorithmIdentifier,
+   *      signatureValue       BIT STRING  }
+
+   * TBSCertificate  ::=  SEQUENCE  {
+   *      version         [0]  EXPLICIT Version DEFAULT v1,
+   *      serialNumber         CertificateSerialNumber,
+   *      signature            AlgorithmIdentifier,
+   *      issuer               Name,
+   *      validity             Validity,
+   *      subject              Name,
+   *      subjectPublicKeyInfo SubjectPublicKeyInfo,
+   *      ... } */
+  CBS buf = *in;
+
+  CBS toplevel, tbs_cert, spki;
+  if (!CBS_get_asn1(&buf, &toplevel, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&buf) != 0 ||
+      !CBS_get_asn1(&toplevel, &tbs_cert, CBS_ASN1_SEQUENCE) ||
+      /* version */
+      !CBS_get_optional_asn1(
+          &tbs_cert, NULL, NULL,
+          CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+      /* serialNumber */
+      !CBS_get_asn1(&tbs_cert, NULL, CBS_ASN1_INTEGER) ||
+      /* signature algorithm */
+      !CBS_get_asn1(&tbs_cert, NULL, CBS_ASN1_SEQUENCE) ||
+      /* issuer */
+      !CBS_get_asn1(&tbs_cert, NULL, CBS_ASN1_SEQUENCE) ||
+      /* validity */
+      !CBS_get_asn1(&tbs_cert, NULL, CBS_ASN1_SEQUENCE) ||
+      /* subject */
+      !CBS_get_asn1(&tbs_cert, NULL, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1_element(&tbs_cert, &spki, CBS_ASN1_SEQUENCE)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
+    return NULL;
+  }
+
+  return EVP_parse_public_key(&spki);
+}
+
 static int ca_dn_cmp(const X509_NAME **a, const X509_NAME **b) {
   return X509_NAME_cmp(*a, *b);
 }