diff options
Diffstat (limited to 'lib/libssl/tls13_client.c')
-rw-r--r-- | lib/libssl/tls13_client.c | 214 |
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; +} |