summaryrefslogtreecommitdiffstats
path: root/lib/libssl/tls13_client.c
diff options
context:
space:
mode:
authorjsing <jsing@openbsd.org>2019-02-11 17:48:15 +0000
committerjsing <jsing@openbsd.org>2019-02-11 17:48:15 +0000
commit1d05a235edb7ca4fdd47f3bed1ec796a1d493d91 (patch)
tree126c30b7928b2df7202fae41584d03ced614dbd2 /lib/libssl/tls13_client.c
parentspelling and Xr fixes; (diff)
downloadwireguard-openbsd-1d05a235edb7ca4fdd47f3bed1ec796a1d493d91.tar.xz
wireguard-openbsd-1d05a235edb7ca4fdd47f3bed1ec796a1d493d91.zip
Implement handling of Certificate and CertificateVerify messages.
This allows the TLS 1.3 client to process the certificates that the server has sent and verify that the server has possession of the private key. ok tb@
Diffstat (limited to 'lib/libssl/tls13_client.c')
-rw-r--r--lib/libssl/tls13_client.c214
1 files changed, 213 insertions, 1 deletions
diff --git a/lib/libssl/tls13_client.c b/lib/libssl/tls13_client.c
index 6578438c786..d15ab65105e 100644
--- a/lib/libssl/tls13_client.c
+++ b/lib/libssl/tls13_client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls13_client.c,v 1.5 2019/02/09 15:26:15 jsing Exp $ */
+/* $OpenBSD: tls13_client.c,v 1.6 2019/02/11 17:48:15 jsing Exp $ */
/*
* Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
*
@@ -343,3 +343,215 @@ tls13_server_encrypted_extensions_recv(struct tls13_ctx *ctx)
return 0;
}
+
+int
+tls13_server_certificate_request_recv(struct tls13_ctx *ctx)
+{
+ /*
+ * Thanks to poor state design in the RFC, this function can be called
+ * when we actually have a certificate message instead of a certificate
+ * request... in that case we call the certificate handler after
+ * switching state, to avoid advancing state.
+ */
+ if (tls13_handshake_msg_type(ctx->hs_msg) == TLS13_MT_CERTIFICATE) {
+ ctx->handshake_stage.hs_type |= WITHOUT_CR;
+ return tls13_server_certificate_recv(ctx);
+ }
+
+ /* XXX - unimplemented. */
+
+ return 0;
+}
+
+int
+tls13_server_certificate_recv(struct tls13_ctx *ctx)
+{
+ CBS cbs, cert_request_context, cert_list, cert_data, cert_exts;
+ struct stack_st_X509 *certs = NULL;
+ SSL *s = ctx->ssl;
+ X509 *cert = NULL;
+ EVP_PKEY *pkey;
+ const uint8_t *p;
+ int cert_idx;
+ int ret = 0;
+
+ if ((certs = sk_X509_new_null()) == NULL)
+ goto err;
+
+ if (!tls13_handshake_msg_content(ctx->hs_msg, &cbs))
+ goto err;
+
+ if (!CBS_get_u8_length_prefixed(&cbs, &cert_request_context))
+ goto err;
+ if (CBS_len(&cert_request_context) != 0)
+ goto err;
+ if (!CBS_get_u24_length_prefixed(&cbs, &cert_list))
+ goto err;
+ if (CBS_len(&cbs) != 0)
+ goto err;
+
+ while (CBS_len(&cert_list) > 0) {
+ if (!CBS_get_u24_length_prefixed(&cert_list, &cert_data))
+ goto err;
+ if (!CBS_get_u16_length_prefixed(&cert_list, &cert_exts))
+ goto err;
+
+ p = CBS_data(&cert_data);
+ if ((cert = d2i_X509(NULL, &p, CBS_len(&cert_data))) == NULL)
+ goto err;
+ if (p != CBS_data(&cert_data) + CBS_len(&cert_data))
+ goto err;
+
+ if (!sk_X509_push(certs, cert))
+ goto err;
+
+ cert = NULL;
+ }
+
+ /*
+ * At this stage we still have no proof of possession. As such, it would
+ * be preferable to keep the chain and verify once we have successfully
+ * processed the CertificateVerify message.
+ */
+ if (ssl_verify_cert_chain(s, certs) <= 0 &&
+ s->verify_mode != SSL_VERIFY_NONE) {
+ /* XXX send alert */
+ goto err;
+ }
+ ERR_clear_error();
+
+ cert = sk_X509_value(certs, 0);
+ X509_up_ref(cert);
+
+ if ((pkey = X509_get0_pubkey(cert)) == NULL)
+ goto err;
+ if (EVP_PKEY_missing_parameters(pkey))
+ goto err;
+ if ((cert_idx = ssl_cert_type(cert, pkey)) < 0)
+ goto err;
+
+ ssl_sess_cert_free(SSI(s)->sess_cert);
+ if ((SSI(s)->sess_cert = ssl_sess_cert_new()) == NULL)
+ goto err;
+
+ SSI(s)->sess_cert->cert_chain = certs;
+ certs = NULL;
+
+ X509_up_ref(cert);
+ SSI(s)->sess_cert->peer_pkeys[cert_idx].x509 = cert;
+ SSI(s)->sess_cert->peer_key = &(SSI(s)->sess_cert->peer_pkeys[cert_idx]);
+
+ X509_free(s->session->peer);
+
+ X509_up_ref(cert);
+ s->session->peer = cert;
+ s->session->verify_result = s->verify_result;
+
+ ret = 1;
+
+ err:
+ sk_X509_pop_free(certs, X509_free);
+ X509_free(cert);
+
+ return ret;
+}
+
+/*
+ * Certificate Verify padding - RFC 8446 section 4.4.3.
+ */
+static uint8_t cert_verify_pad[64] = {
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+};
+
+static uint8_t server_cert_verify_context[] = "TLS 1.3, server CertificateVerify";
+
+int
+tls13_server_certificate_verify_recv(struct tls13_ctx *ctx)
+{
+ const struct ssl_sigalg *sigalg;
+ uint16_t signature_scheme;
+ uint8_t *sig_content = NULL;
+ size_t sig_content_len;
+ EVP_MD_CTX *mdctx = NULL;
+ EVP_PKEY_CTX *pctx;
+ EVP_PKEY *pkey;
+ X509 *cert;
+ CBS cbs, signature;
+ CBB cbb;
+ int ret = 0;
+
+ memset(&cbb, 0, sizeof(cbb));
+
+ if (!tls13_handshake_msg_content(ctx->hs_msg, &cbs))
+ goto err;
+
+ if (!CBS_get_u16(&cbs, &signature_scheme))
+ goto err;
+ if (!CBS_get_u16_length_prefixed(&cbs, &signature))
+ goto err;
+ if (CBS_len(&cbs) != 0)
+ goto err;
+
+ if ((sigalg = ssl_sigalg(signature_scheme, tls13_sigalgs,
+ tls13_sigalgs_len)) == NULL)
+ goto err;
+
+ if (!CBB_init(&cbb, 0))
+ goto err;
+ if (!CBB_add_bytes(&cbb, cert_verify_pad, sizeof(cert_verify_pad)))
+ goto err;
+ if (!CBB_add_bytes(&cbb, server_cert_verify_context,
+ strlen(server_cert_verify_context)))
+ goto err;
+ if (!CBB_add_u8(&cbb, 0))
+ goto err;
+ if (!CBB_add_bytes(&cbb, ctx->hs->transcript_hash,
+ ctx->hs->transcript_hash_len))
+ goto err;
+ if (!CBB_finish(&cbb, &sig_content, &sig_content_len))
+ goto err;
+
+ if ((cert = ctx->ssl->session->peer) == NULL)
+ goto err;
+ if ((pkey = X509_get0_pubkey(cert)) == NULL)
+ goto err;
+ if (!ssl_sigalg_pkey_ok(sigalg, pkey))
+ goto err;
+
+ if (CBS_len(&signature) > EVP_PKEY_size(pkey))
+ goto err;
+
+ if ((mdctx = EVP_MD_CTX_new()) == NULL)
+ goto err;
+ if (!EVP_DigestVerifyInit(mdctx, &pctx, sigalg->md(), NULL, pkey))
+ goto err;
+ if (sigalg->flags & SIGALG_FLAG_RSA_PSS) {
+ if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING))
+ goto err;
+ if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1))
+ goto err;
+ }
+ if (!EVP_DigestVerifyUpdate(mdctx, sig_content, sig_content_len))
+ goto err;
+ if (EVP_DigestVerifyFinal(mdctx, CBS_data(&signature),
+ CBS_len(&signature)) <= 0) {
+ /* XXX - send alert. */
+ goto err;
+ }
+
+ ret = 1;
+
+ err:
+ CBB_cleanup(&cbb);
+ EVP_MD_CTX_free(mdctx);
+ free(sig_content);
+
+ return ret;
+}