summaryrefslogtreecommitdiffstats
path: root/lib/libssl/tls13_client.c
diff options
context:
space:
mode:
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;
+}