This is a simpler implementation than OpenSSL's, lacking responder IDs and request extensions support. This mirrors the client implementation already present. Change-Id: I54592b60e0a708bfb003d491c9250401403c9e69 Reviewed-on: https://boringssl-review.googlesource.com/5700 Reviewed-by: Adam Langley <agl@google.com>kris/onging/CECPQ3_patch15
@@ -627,6 +627,13 @@ OPENSSL_EXPORT int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx, | |||
OPENSSL_EXPORT int SSL_get0_chain_certs(const SSL *ssl, | |||
STACK_OF(X509) **out_chain); | |||
/* SSL_CTX_set_ocsp_response sets the OCSP reponse that is sent to clients | |||
* which request it. It returns one on success and zero on error. The caller | |||
* retains ownership of |response|. */ | |||
OPENSSL_EXPORT int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, | |||
const uint8_t *response, | |||
size_t response_len); | |||
/* Certificate and private key convenience functions. */ | |||
@@ -1464,6 +1471,10 @@ struct ssl_ctx_st { | |||
/* If true, a client will request a stapled OCSP response. */ | |||
char ocsp_stapling_enabled; | |||
/* OCSP response to be sent to the client, if requested. */ | |||
uint8_t *ocsp_response; | |||
size_t ocsp_response_length; | |||
/* If not NULL, session key material will be logged to this BIO for debugging | |||
* purposes. The format matches NSS's and is readable by Wireshark. */ | |||
BIO *keylog_bio; | |||
@@ -1830,9 +1841,8 @@ struct ssl_st { | |||
/* Enable signed certificate time stamps. Currently client only. */ | |||
char signed_cert_timestamps_enabled; | |||
/* Enable OCSP stapling. Currently client only. | |||
* TODO(davidben): Add a server-side implementation when it becomes | |||
* necesary. */ | |||
/* ocsp_stapling_enabled is only used by client connections and indicates | |||
* whether OCSP stapling will be requested. */ | |||
char ocsp_stapling_enabled; | |||
/* For a client, this contains the list of supported protocols in wire | |||
@@ -491,8 +491,12 @@ typedef struct ssl3_state_st { | |||
int cert_request; | |||
/* certificate_status_expected is true if OCSP stapling was negotiated and | |||
* the server is expected to send a CertificateStatus message. */ | |||
char certificate_status_expected; | |||
* the server is expected to send a CertificateStatus message. (This is | |||
* used on both the client and server sides.) */ | |||
unsigned certificate_status_expected:1; | |||
/* ocsp_stapling_requested is true if a client requested OCSP stapling. */ | |||
unsigned ocsp_stapling_requested:1; | |||
/* Server-only: peer_ellipticcurvelist contains the EC curve IDs advertised | |||
* by the peer. This is only set on the server's end. The server does not | |||
@@ -239,6 +239,16 @@ int dtls1_accept(SSL *s) { | |||
s->init_num = 0; | |||
break; | |||
case SSL3_ST_SW_CERT_STATUS_A: | |||
case SSL3_ST_SW_CERT_STATUS_B: | |||
ret = ssl3_send_certificate_status(s); | |||
if (ret <= 0) { | |||
goto end; | |||
} | |||
s->state = SSL3_ST_SW_KEY_EXCH_A; | |||
s->init_num = 0; | |||
break; | |||
case SSL3_ST_SW_KEY_EXCH_A: | |||
case SSL3_ST_SW_KEY_EXCH_B: | |||
case SSL3_ST_SW_KEY_EXCH_C: | |||
@@ -880,7 +880,7 @@ int ssl_fill_hello_random(uint8_t *out, size_t len, int is_server); | |||
int ssl3_send_server_certificate(SSL *s); | |||
int ssl3_send_new_session_ticket(SSL *s); | |||
int ssl3_send_cert_status(SSL *s); | |||
int ssl3_send_certificate_status(SSL *s); | |||
int ssl3_get_finished(SSL *s, int state_a, int state_b); | |||
int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b); | |||
int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret, | |||
@@ -306,6 +306,16 @@ int ssl3_accept(SSL *s) { | |||
s->init_num = 0; | |||
break; | |||
case SSL3_ST_SW_CERT_STATUS_A: | |||
case SSL3_ST_SW_CERT_STATUS_B: | |||
ret = ssl3_send_certificate_status(s); | |||
if (ret <= 0) { | |||
goto end; | |||
} | |||
s->state = SSL3_ST_SW_KEY_EXCH_A; | |||
s->init_num = 0; | |||
break; | |||
case SSL3_ST_SW_KEY_EXCH_A: | |||
case SSL3_ST_SW_KEY_EXCH_B: | |||
case SSL3_ST_SW_KEY_EXCH_C: | |||
@@ -1191,6 +1201,32 @@ int ssl3_send_server_hello(SSL *s) { | |||
return ssl_do_write(s); | |||
} | |||
int ssl3_send_certificate_status(SSL *ssl) { | |||
if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) { | |||
CBB out, ocsp_response; | |||
size_t length; | |||
CBB_zero(&out); | |||
if (!CBB_init_fixed(&out, ssl_handshake_start(ssl), | |||
ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) || | |||
!CBB_add_u8(&out, TLSEXT_STATUSTYPE_ocsp) || | |||
!CBB_add_u24_length_prefixed(&out, &ocsp_response) || | |||
!CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response, | |||
ssl->ctx->ocsp_response_length) || | |||
!CBB_finish(&out, NULL, &length) || | |||
!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_STATUS, length)) { | |||
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); | |||
CBB_cleanup(&out); | |||
return -1; | |||
} | |||
ssl->state = SSL3_ST_SW_CERT_STATUS_B; | |||
} | |||
/* SSL3_ST_SW_CERT_STATUS_B */ | |||
return ssl_do_write(ssl); | |||
} | |||
int ssl3_send_server_done(SSL *s) { | |||
if (s->state == SSL3_ST_SW_SRVR_DONE_A) { | |||
if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_DONE, 0)) { | |||
@@ -1385,6 +1385,20 @@ void SSL_get0_ocsp_response(const SSL *ssl, const uint8_t **out, | |||
*out_len = session->ocsp_response_length; | |||
} | |||
int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response, | |||
size_t response_len) { | |||
OPENSSL_free(ctx->ocsp_response); | |||
ctx->ocsp_response_length = 0; | |||
ctx->ocsp_response = BUF_memdup(response, response_len); | |||
if (ctx->ocsp_response == NULL) { | |||
return 0; | |||
} | |||
ctx->ocsp_response_length = response_len; | |||
return 1; | |||
} | |||
/* SSL_select_next_proto implements the standard protocol selection. It is | |||
* expected that this function is called from the callback set by | |||
* SSL_CTX_set_next_proto_select_cb. | |||
@@ -1753,6 +1767,7 @@ void SSL_CTX_free(SSL_CTX *ctx) { | |||
OPENSSL_free(ctx->psk_identity_hint); | |||
OPENSSL_free(ctx->tlsext_ellipticcurvelist); | |||
OPENSSL_free(ctx->alpn_client_proto_list); | |||
OPENSSL_free(ctx->ocsp_response); | |||
EVP_PKEY_free(ctx->tlsext_channel_id_private); | |||
BIO_free(ctx->keylog_bio); | |||
@@ -1330,7 +1330,7 @@ static int ext_ocsp_add_clienthello(SSL *ssl, CBB *out) { | |||
} | |||
static int ext_ocsp_parse_serverhello(SSL *ssl, uint8_t *out_alert, | |||
CBS *contents) { | |||
CBS *contents) { | |||
if (contents == NULL) { | |||
return 1; | |||
} | |||
@@ -1345,13 +1345,32 @@ static int ext_ocsp_parse_serverhello(SSL *ssl, uint8_t *out_alert, | |||
static int ext_ocsp_parse_clienthello(SSL *ssl, uint8_t *out_alert, | |||
CBS *contents) { | |||
/* OCSP stapling as a server is not supported. */ | |||
if (contents == NULL) { | |||
return 1; | |||
} | |||
uint8_t status_type; | |||
if (!CBS_get_u8(contents, &status_type)) { | |||
return 0; | |||
} | |||
/* We cannot decide whether OCSP stapling will occur yet because the correct | |||
* SSL_CTX might not have been selected. */ | |||
ssl->s3->tmp.ocsp_stapling_requested = status_type == TLSEXT_STATUSTYPE_ocsp; | |||
return 1; | |||
} | |||
static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) { | |||
/* OCSP stapling as a server is not supported. */ | |||
return 1; | |||
if (!ssl->s3->tmp.ocsp_stapling_requested || | |||
ssl->ctx->ocsp_response_length == 0) { | |||
return 1; | |||
} | |||
ssl->s3->tmp.certificate_status_expected = 1; | |||
return CBB_add_u16(out, TLSEXT_TYPE_status_request) && | |||
CBB_add_u16(out, 0 /* length */); | |||
} | |||
@@ -240,6 +240,12 @@ static bool InstallCertificate(SSL *ssl) { | |||
SSL_FILETYPE_PEM)) { | |||
return false; | |||
} | |||
if (!config->ocsp_response.empty() && | |||
!SSL_CTX_set_ocsp_response(ssl->ctx, | |||
(const uint8_t *)config->ocsp_response.data(), | |||
config->ocsp_response.size())) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
@@ -155,6 +155,8 @@ type testCase struct { | |||
// expectedSRTPProtectionProfile is the DTLS-SRTP profile that | |||
// should be negotiated. If zero, none should be negotiated. | |||
expectedSRTPProtectionProfile uint16 | |||
// expectedOCSPResponse, if not nil, is the expected OCSP response to be received. | |||
expectedOCSPResponse []uint8 | |||
// messageLen is the length, in bytes, of the test message that will be | |||
// sent. | |||
messageLen int | |||
@@ -320,6 +322,10 @@ func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool) er | |||
return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile) | |||
} | |||
if test.expectedOCSPResponse != nil && !bytes.Equal(test.expectedOCSPResponse, tlsConn.OCSPResponse()) { | |||
return fmt.Errorf("OCSP Response mismatch") | |||
} | |||
if test.exportKeyingMaterial > 0 { | |||
actual := make([]byte, test.exportKeyingMaterial) | |||
if _, err := io.ReadFull(tlsConn, actual); err != nil { | |||
@@ -2333,6 +2339,26 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) | |||
flags: []string{"-psk", "secret"}, | |||
}) | |||
tests = append(tests, testCase{ | |||
testType: clientTest, | |||
name: "OCSPStapling-Client", | |||
flags: []string{ | |||
"-enable-ocsp-stapling", | |||
"-expect-ocsp-response", | |||
base64.StdEncoding.EncodeToString(testOCSPResponse), | |||
}, | |||
}) | |||
tests = append(tests, testCase{ | |||
testType: serverTest, | |||
name: "OCSPStapling-Server", | |||
expectedOCSPResponse: testOCSPResponse, | |||
flags: []string{ | |||
"-ocsp-response", | |||
base64.StdEncoding.EncodeToString(testOCSPResponse), | |||
}, | |||
}) | |||
if protocol == tls { | |||
tests = append(tests, testCase{ | |||
name: "Renegotiate-Client", | |||
@@ -3034,15 +3060,7 @@ func addExtensionTests() { | |||
shouldFail: true, | |||
expectedError: ":BAD_SRTP_PROTECTION_PROFILE_LIST:", | |||
}) | |||
// Test OCSP stapling and SCT list. | |||
testCases = append(testCases, testCase{ | |||
name: "OCSPStapling", | |||
flags: []string{ | |||
"-enable-ocsp-stapling", | |||
"-expect-ocsp-response", | |||
base64.StdEncoding.EncodeToString(testOCSPResponse), | |||
}, | |||
}) | |||
// Test SCT list. | |||
testCases = append(testCases, testCase{ | |||
name: "SignedCertificateTimestampList", | |||
flags: []string{ | |||
@@ -119,6 +119,7 @@ const Flag<std::string> kBase64Flags[] = { | |||
{ "-expect-ocsp-response", &TestConfig::expected_ocsp_response }, | |||
{ "-expect-signed-cert-timestamps", | |||
&TestConfig::expected_signed_cert_timestamps }, | |||
{ "-ocsp-response", &TestConfig::ocsp_response }, | |||
}; | |||
const Flag<int> kIntFlags[] = { | |||
@@ -86,6 +86,7 @@ struct TestConfig { | |||
bool enable_server_custom_extension = false; | |||
bool custom_extension_skip = false; | |||
bool custom_extension_fail_add = false; | |||
std::string ocsp_response; | |||
}; | |||
bool ParseConfig(int argc, char **argv, TestConfig *out_config); | |||
@@ -34,11 +34,53 @@ static const struct argument kArguments[] = { | |||
"-key", kOptionalArgument, | |||
"Private-key file to use (default is server.pem)", | |||
}, | |||
{ | |||
"-ocsp-response", kOptionalArgument, | |||
"OCSP response file to send", | |||
}, | |||
{ | |||
"", kOptionalArgument, "", | |||
}, | |||
}; | |||
static bool LoadOCSPResponse(SSL_CTX *ctx, const char *filename) { | |||
void *data = NULL; | |||
bool ret = false; | |||
long length; | |||
FILE *f = fopen(filename, "rb"); | |||
if (f == NULL || | |||
fseek(f, 0, SEEK_END) != 0) { | |||
goto out; | |||
} | |||
length = ftell(f); | |||
if (length < 0) { | |||
goto out; | |||
} | |||
data = malloc(length); | |||
if (data == NULL) { | |||
goto out; | |||
} | |||
rewind(f); | |||
fread(data, 1, length, f); | |||
if (ferror(f) != 0 || | |||
!SSL_CTX_set_ocsp_response(ctx, (uint8_t*)data, length)) { | |||
goto out; | |||
} | |||
ret = true; | |||
out: | |||
if (f != NULL) { | |||
fclose(f); | |||
} | |||
free(data); | |||
return ret; | |||
} | |||
bool Server(const std::vector<std::string> &args) { | |||
if (!InitSocketLibrary()) { | |||
return false; | |||
@@ -74,6 +116,12 @@ bool Server(const std::vector<std::string> &args) { | |||
return false; | |||
} | |||
if (args_map.count("-ocsp-response") != 0 && | |||
!LoadOCSPResponse(ctx, args_map["-ocsp-response"].c_str())) { | |||
fprintf(stderr, "Failed to load OCSP response: %s\n", args_map["-ocsp-response"].c_str()); | |||
return false; | |||
} | |||
int sock = -1; | |||
if (!Accept(&sock, args_map["-accept"])) { | |||
return false; | |||