Add server-side support for asynchronous signing.
The RSA key exchange needs decryption and is still unsupported.
Change-Id: I8c13b74e25a5424356afbe6e97b5f700a56de41f
Reviewed-on: https://boringssl-review.googlesource.com/5467
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index faf69ab..0694e56 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -634,6 +634,8 @@
#define SSL3_ST_SW_CERT_B (0x141 | SSL_ST_ACCEPT)
#define SSL3_ST_SW_KEY_EXCH_A (0x150 | SSL_ST_ACCEPT)
#define SSL3_ST_SW_KEY_EXCH_B (0x151 | SSL_ST_ACCEPT)
+#define SSL3_ST_SW_KEY_EXCH_C (0x152 | SSL_ST_ACCEPT)
+#define SSL3_ST_SW_KEY_EXCH_D (0x153 | SSL_ST_ACCEPT)
#define SSL3_ST_SW_CERT_REQ_A (0x160 | SSL_ST_ACCEPT)
#define SSL3_ST_SW_CERT_REQ_B (0x161 | SSL_ST_ACCEPT)
#define SSL3_ST_SW_SRVR_DONE_A (0x170 | SSL_ST_ACCEPT)
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 32aa591..7a7d1ea 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -246,6 +246,8 @@
case SSL3_ST_SW_KEY_EXCH_A:
case SSL3_ST_SW_KEY_EXCH_B:
+ case SSL3_ST_SW_KEY_EXCH_C:
+ case SSL3_ST_SW_KEY_EXCH_D:
alg_a = s->s3->tmp.new_cipher->algorithm_auth;
/* Send a ServerKeyExchange message if:
diff --git a/ssl/internal.h b/ssl/internal.h
index f19a265..a1600f8 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -346,6 +346,10 @@
/* Private key operations. */
+/* ssl_has_private_key returns one if |ssl| has a private key
+ * configured and zero otherwise. */
+int ssl_has_private_key(SSL *ssl);
+
/* ssl_private_key_* call the corresponding function on the
* |SSL_PRIVATE_KEY_METHOD| for |ssl|, if configured. Otherwise, they implement
* the operation with |EVP_PKEY|. */
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index cf98bb6..a1ed39a 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1959,7 +1959,7 @@
uint8_t *p = ssl_handshake_start(s);
size_t signature_length = 0;
unsigned long n = 0;
- assert(s->cert->privatekey != NULL || s->cert->key_method != NULL);
+ assert(ssl_has_private_key(s));
if (s->state == SSL3_ST_CW_CERT_VRFY_A) {
uint8_t *buf = (uint8_t *)s->init_buf->data;
@@ -2036,8 +2036,7 @@
/* ssl3_has_client_certificate returns true if a client certificate is
* configured. */
static int ssl3_has_client_certificate(SSL *ssl) {
- return ssl->cert && ssl->cert->x509 && (ssl->cert->privatekey ||
- ssl->cert->key_method);
+ return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl);
}
int ssl3_send_client_certificate(SSL *s) {
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index fba5bcb..dca6207 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -312,6 +312,8 @@
case SSL3_ST_SW_KEY_EXCH_A:
case SSL3_ST_SW_KEY_EXCH_B:
+ case SSL3_ST_SW_KEY_EXCH_C:
+ case SSL3_ST_SW_KEY_EXCH_D:
alg_a = s->s3->tmp.new_cipher->algorithm_auth;
/* Send a ServerKeyExchange message if:
@@ -1247,7 +1249,8 @@
BN_CTX *bn_ctx = NULL;
const char *psk_identity_hint = NULL;
size_t psk_identity_hint_len = 0;
- EVP_PKEY *pkey;
+ size_t sig_len;
+ size_t max_sig_len;
uint8_t *p, *d;
int al, i;
uint32_t alg_k;
@@ -1255,10 +1258,20 @@
int n;
CERT *cert;
BIGNUM *r[4];
- int nr[4], kn;
+ int nr[4];
BUF_MEM *buf;
EVP_MD_CTX md_ctx;
+ if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
+ if (!ssl_has_private_key(s)) {
+ al = SSL_AD_INTERNAL_ERROR;
+ goto f_err;
+ }
+ max_sig_len = ssl_private_key_max_signature_len(s);
+ } else {
+ max_sig_len = 0;
+ }
+
EVP_MD_CTX_init(&md_ctx);
if (s->state == SSL3_ST_SW_KEY_EXCH_A) {
alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
@@ -1400,19 +1413,7 @@
n += 2 + nr[i];
}
- if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
- pkey = s->cert->privatekey;
- if (pkey == NULL) {
- al = SSL_AD_DECODE_ERROR;
- goto f_err;
- }
- kn = EVP_PKEY_size(pkey);
- } else {
- pkey = NULL;
- kn = 0;
- }
-
- if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + kn)) {
+ if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + max_sig_len)) {
OPENSSL_PUT_ERROR(SSL, ERR_LIB_BUF);
goto err;
}
@@ -1453,11 +1454,14 @@
}
/* not anonymous */
- if (pkey != NULL) {
- /* n is the length of the params, they start at &(d[4]) and p points to
+ if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
+ /* n is the length of the params, they start at d and p points to
* the space at the end. */
const EVP_MD *md;
- size_t sig_len = EVP_PKEY_size(pkey);
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ unsigned int digest_length;
+
+ const int pkey_type = ssl_private_key_type(s);
/* Determine signature algorithm. */
if (SSL_USE_SIGALGS(s)) {
@@ -1469,36 +1473,66 @@
goto f_err;
}
p += 2;
- } else if (pkey->type == EVP_PKEY_RSA) {
+ } else if (pkey_type == EVP_PKEY_RSA) {
md = EVP_md5_sha1();
} else {
md = EVP_sha1();
}
- if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey) ||
- !EVP_DigestSignUpdate(&md_ctx, s->s3->client_random,
- SSL3_RANDOM_SIZE) ||
- !EVP_DigestSignUpdate(&md_ctx, s->s3->server_random,
- SSL3_RANDOM_SIZE) ||
- !EVP_DigestSignUpdate(&md_ctx, d, n) ||
- !EVP_DigestSignFinal(&md_ctx, &p[2], &sig_len)) {
+ if (!EVP_DigestInit_ex(&md_ctx, md, NULL) ||
+ !EVP_DigestUpdate(&md_ctx, s->s3->client_random, SSL3_RANDOM_SIZE) ||
+ !EVP_DigestUpdate(&md_ctx, s->s3->server_random, SSL3_RANDOM_SIZE) ||
+ !EVP_DigestUpdate(&md_ctx, d, n) ||
+ !EVP_DigestFinal_ex(&md_ctx, digest, &digest_length)) {
OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
goto err;
}
- s2n(sig_len, p);
- n += sig_len + 2;
- if (SSL_USE_SIGALGS(s)) {
- n += 2;
+ const enum ssl_private_key_result_t sign_result = ssl_private_key_sign(
+ s, &p[2], &sig_len, max_sig_len, EVP_MD_CTX_md(&md_ctx),
+ digest, digest_length);
+ if (sign_result == ssl_private_key_retry) {
+ s->rwstate = SSL_PRIVATE_KEY_OPERATION;
+ /* Stash away |p|. */
+ s->init_num = p - ssl_handshake_start(s) + SSL_HM_HEADER_LENGTH(s);
+ s->state = SSL3_ST_SW_KEY_EXCH_B;
+ goto err;
+ } else if (sign_result != ssl_private_key_success) {
+ goto err;
}
}
- if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE, n)) {
+ s->state = SSL3_ST_SW_KEY_EXCH_C;
+ } else if (s->state == SSL3_ST_SW_KEY_EXCH_B) {
+ /* Complete async sign. */
+ /* Restore |p|. */
+ p = ssl_handshake_start(s) + s->init_num - SSL_HM_HEADER_LENGTH(s);
+ const enum ssl_private_key_result_t sign_result =
+ ssl_private_key_sign_complete(s, &p[2], &sig_len, max_sig_len);
+ if (sign_result == ssl_private_key_retry) {
+ s->rwstate = SSL_PRIVATE_KEY_OPERATION;
+ goto err;
+ } else if (sign_result != ssl_private_key_success) {
goto err;
}
+
+ s->rwstate = SSL_NOTHING;
+ s->state = SSL3_ST_SW_KEY_EXCH_C;
}
- s->state = SSL3_ST_SW_KEY_EXCH_B;
+ if (s->state == SSL3_ST_SW_KEY_EXCH_C) {
+ if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
+ s2n(sig_len, p);
+ p += sig_len;
+ }
+ if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE,
+ p - ssl_handshake_start(s))) {
+ goto err;
+ }
+ s->state = SSL3_ST_SW_KEY_EXCH_D;
+ }
+
+ /* state SSL3_ST_SW_KEY_EXCH_D */
EVP_MD_CTX_cleanup(&md_ctx);
return ssl_do_write(s);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a7d5d5b..72b4c79 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1834,13 +1834,14 @@
dh_tmp = (c->dh_tmp != NULL || c->dh_tmp_cb != NULL);
- if (s->cert->x509 != NULL && s->cert->privatekey != NULL) {
- if (s->cert->privatekey->type == EVP_PKEY_RSA) {
+ if (s->cert->x509 != NULL && ssl_has_private_key(s)) {
+ if (ssl_private_key_type(s) == EVP_PKEY_RSA) {
have_rsa_cert = 1;
- } else if (s->cert->privatekey->type == EVP_PKEY_EC) {
+ } else if (ssl_private_key_type(s) == EVP_PKEY_EC) {
have_ecc_cert = 1;
}
}
+
mask_k = 0;
mask_a = 0;
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 7b6cd75..da69f8b 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -632,6 +632,10 @@
ssl->cert->key_method = key_method;
}
+int ssl_has_private_key(SSL *ssl) {
+ return ssl->cert->privatekey != NULL || ssl->cert->key_method != NULL;
+}
+
int ssl_private_key_type(SSL *ssl) {
if (ssl->cert->key_method != NULL) {
return ssl->cert->key_method->type(ssl);
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 571680b..c0d5475 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -205,6 +205,7 @@
}
memcpy(out, bssl::vector_data(&test_state->signature),
test_state->signature.size());
+ *out_len = test_state->signature.size();
test_state->signature.clear();
test_state->signature_retries = 0;
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index d66dc74..35d0bc6 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2234,6 +2234,24 @@
"-use-async-private-key",
},
})
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "Basic-Server-RSAAsyncKey",
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-use-async-private-key",
+ },
+ })
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "Basic-Server-ECDSAAsyncKey",
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, ecdsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, ecdsaKeyFile),
+ "-use-async-private-key",
+ },
+ })
}
tests = append(tests, testCase{
testType: serverTest,