Convert ssl3_send_cert_verify to CBB.
In doing so, make the asynchronous portion look more like
ssl3_send_server_key_exchange. This is a considerably simpler structure,
so the save/resume doesn't need any state.
Mostly this means writing out the signature algorithm can now go through
CBB rather than a uint8_t* without bounds check.
Change-Id: If99fcffd0d41a84514c3d23034062c582f1bccb2
Reviewed-on: https://boringssl-review.googlesource.com/6771
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h
index e3748a2..2c46f5c 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1076,7 +1076,7 @@
int ssl3_get_new_session_ticket(SSL *s);
int ssl3_get_cert_status(SSL *s);
int ssl3_get_server_done(SSL *s);
-int ssl3_send_cert_verify(SSL *s);
+int ssl3_send_cert_verify(SSL *ssl);
int ssl3_send_client_certificate(SSL *s);
int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey);
int ssl3_send_client_key_exchange(SSL *ssl);
@@ -1190,10 +1190,11 @@
size_t ticket_len, const uint8_t *session_id,
size_t session_id_len);
-/* tls12_get_sigandhash assembles the SignatureAndHashAlgorithm corresponding to
- * |ssl|'s private key and |md|. The two-byte value is written to |p|. It
+/* tls12_add_sigandhash assembles the SignatureAndHashAlgorithm corresponding to
+ * |ssl|'s private key and |md|. The two-byte value is written to |out|. It
* returns one on success and zero on failure. */
-int tls12_get_sigandhash(SSL *ssl, uint8_t *p, const EVP_MD *md);
+int tls12_add_sigandhash(SSL *ssl, CBB *out, const EVP_MD *md);
+
int tls12_get_sigid(int pkey_type);
const EVP_MD *tls12_get_hash(uint8_t hash_alg);
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 7f61c89..85c0408 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1852,82 +1852,94 @@
return -1;
}
-int ssl3_send_cert_verify(SSL *s) {
- if (s->state == SSL3_ST_CW_CERT_VRFY_A ||
- s->state == SSL3_ST_CW_CERT_VRFY_B) {
- enum ssl_private_key_result_t sign_result;
- uint8_t *p = ssl_handshake_start(s);
- size_t signature_length = 0;
- unsigned long n = 0;
- assert(ssl_has_private_key(s));
-
- if (s->state == SSL3_ST_CW_CERT_VRFY_A) {
- uint8_t *buf = (uint8_t *)s->init_buf->data;
- const EVP_MD *md = NULL;
- uint8_t digest[EVP_MAX_MD_SIZE];
- size_t digest_length;
-
- /* Write out the digest type if need be. */
- if (SSL_USE_SIGALGS(s)) {
- md = tls1_choose_signing_digest(s);
- if (!tls12_get_sigandhash(s, p, md)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return -1;
- }
- p += 2;
- n += 2;
- }
-
- /* Compute the digest. */
- const int pkey_type = ssl_private_key_type(s);
- if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey_type)) {
- return -1;
- }
-
- /* The handshake buffer is no longer necessary. */
- ssl3_free_handshake_buffer(s);
-
- /* Sign the digest. */
- signature_length = ssl_private_key_max_signature_len(s);
- if (p + 2 + signature_length > buf + SSL3_RT_MAX_PLAIN_LENGTH) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
- return -1;
- }
-
- s->rwstate = SSL_PRIVATE_KEY_OPERATION;
- sign_result = ssl_private_key_sign(s, &p[2], &signature_length,
- signature_length, md, digest,
- digest_length);
- } else {
- if (SSL_USE_SIGALGS(s)) {
- /* The digest has already been selected and written. */
- p += 2;
- n += 2;
- }
- signature_length = ssl_private_key_max_signature_len(s);
- s->rwstate = SSL_PRIVATE_KEY_OPERATION;
- sign_result = ssl_private_key_sign_complete(s, &p[2], &signature_length,
- signature_length);
- }
-
- if (sign_result == ssl_private_key_retry) {
- s->state = SSL3_ST_CW_CERT_VRFY_B;
- return -1;
- }
- s->rwstate = SSL_NOTHING;
- if (sign_result != ssl_private_key_success) {
- return -1;
- }
-
- s2n(signature_length, p);
- n += signature_length + 2;
- if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_VERIFY, n)) {
- return -1;
- }
- s->state = SSL3_ST_CW_CERT_VRFY_C;
+int ssl3_send_cert_verify(SSL *ssl) {
+ if (ssl->state == SSL3_ST_CW_CERT_VRFY_C) {
+ return ssl_do_write(ssl);
}
- return ssl_do_write(s);
+ CBB cbb, child;
+ if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
+ ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) {
+ goto err;
+ }
+
+ assert(ssl_has_private_key(ssl));
+
+ const size_t max_sig_len = ssl_private_key_max_signature_len(ssl);
+ size_t sig_len;
+ enum ssl_private_key_result_t sign_result;
+ if (ssl->state == SSL3_ST_CW_CERT_VRFY_A) {
+ /* Select and write out the digest type in TLS 1.2. */
+ const EVP_MD *md = NULL;
+ if (SSL_USE_SIGALGS(ssl)) {
+ md = tls1_choose_signing_digest(ssl);
+ if (!tls12_add_sigandhash(ssl, &cbb, md)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ /* Compute the digest. In TLS 1.1 and below, the digest type is also
+ * selected here. */
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ size_t digest_len;
+ if (!ssl3_cert_verify_hash(ssl, digest, &digest_len, &md,
+ ssl_private_key_type(ssl))) {
+ goto err;
+ }
+
+ /* The handshake buffer is no longer necessary. */
+ ssl3_free_handshake_buffer(ssl);
+
+ /* Sign the digest. */
+ uint8_t *ptr;
+ if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+ !CBB_reserve(&child, &ptr, max_sig_len)) {
+ goto err;
+ }
+ sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len, md,
+ digest, digest_len);
+ } else {
+ assert(ssl->state == SSL3_ST_CW_CERT_VRFY_B);
+
+ /* Skip over the already written signature algorithm and retry the
+ * signature. */
+ uint8_t *ptr;
+ if ((SSL_USE_SIGALGS(ssl) && !CBB_did_write(&cbb, 2)) ||
+ !CBB_add_u16_length_prefixed(&cbb, &child) ||
+ !CBB_reserve(&child, &ptr, max_sig_len)) {
+ goto err;
+ }
+ sign_result =
+ ssl_private_key_sign_complete(ssl, ptr, &sig_len, max_sig_len);
+ }
+
+ switch (sign_result) {
+ case ssl_private_key_success:
+ ssl->rwstate = SSL_NOTHING;
+ break;
+ case ssl_private_key_failure:
+ ssl->rwstate = SSL_NOTHING;
+ goto err;
+ case ssl_private_key_retry:
+ ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+ ssl->state = SSL3_ST_CW_CERT_VRFY_B;
+ goto err;
+ }
+
+ size_t length;
+ if (!CBB_did_write(&child, sig_len) ||
+ !CBB_finish(&cbb, NULL, &length) ||
+ !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_VERIFY, length)) {
+ goto err;
+ }
+
+ ssl->state = SSL3_ST_CW_CERT_VRFY_C;
+ return ssl_do_write(ssl);
+
+err:
+ CBB_cleanup(&cbb);
+ return -1;
}
/* ssl3_has_client_certificate returns true if a client certificate is
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 72cd1c4..bcd662a 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -1327,11 +1327,9 @@
/* Determine signature algorithm. */
const EVP_MD *md;
- uint8_t *ptr;
if (SSL_USE_SIGALGS(ssl)) {
md = tls1_choose_signing_digest(ssl);
- if (!CBB_add_space(&cbb, &ptr, 2) ||
- !tls12_get_sigandhash(ssl, ptr, md)) {
+ if (!tls12_add_sigandhash(ssl, &cbb, md)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
goto err;
@@ -1354,6 +1352,7 @@
EVP_DigestUpdate(&md_ctx, CBB_data(&cbb), params_len) &&
EVP_DigestFinal_ex(&md_ctx, digest, &digest_len);
EVP_MD_CTX_cleanup(&md_ctx);
+ uint8_t *ptr;
if (!digest_ret ||
!CBB_add_u16_length_prefixed(&cbb, &child) ||
!CBB_reserve(&child, &ptr, max_sig_len)) {
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index c0ef97e..8b5bf8e 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -2693,27 +2693,15 @@
sizeof(tls12_sig) / sizeof(tls12_lookup));
}
-int tls12_get_sigandhash(SSL *ssl, uint8_t *p, const EVP_MD *md) {
- int sig_id, md_id;
+int tls12_add_sigandhash(SSL *ssl, CBB *out, const EVP_MD *md) {
+ int md_id = tls12_find_id(EVP_MD_type(md), tls12_md,
+ sizeof(tls12_md) / sizeof(tls12_lookup));
+ int sig_id = tls12_get_sigid(ssl_private_key_type(ssl));
- if (!md) {
- return 0;
- }
-
- md_id = tls12_find_id(EVP_MD_type(md), tls12_md,
- sizeof(tls12_md) / sizeof(tls12_lookup));
- if (md_id == -1) {
- return 0;
- }
-
- sig_id = tls12_get_sigid(ssl_private_key_type(ssl));
- if (sig_id == -1) {
- return 0;
- }
-
- p[0] = (uint8_t)md_id;
- p[1] = (uint8_t)sig_id;
- return 1;
+ return md_id != -1 &&
+ sig_id != -1 &&
+ CBB_add_u8(out, (uint8_t)md_id) &&
+ CBB_add_u8(out, (uint8_t)sig_id);
}
const EVP_MD *tls12_get_hash(uint8_t hash_alg) {