Export keying material using early exporter master secret
This commit adds SSL_export_keying_material_early() which exports keying material using early exporter master secret. Reviewed-by: Rich Salz <rsalz@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/5252)
This commit is contained in:
parent
e454f3add6
commit
b38ede8043
10 changed files with 232 additions and 4 deletions
|
@ -2,7 +2,9 @@
|
|||
|
||||
=head1 NAME
|
||||
|
||||
SSL_export_keying_material - obtain keying material for application use
|
||||
SSL_export_keying_material,
|
||||
SSL_export_keying_material_early
|
||||
- obtain keying material for application use
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
|
@ -13,14 +15,29 @@ SSL_export_keying_material - obtain keying material for application use
|
|||
const unsigned char *context,
|
||||
size_t contextlen, int use_context);
|
||||
|
||||
int SSL_export_keying_material_early(SSL *s, unsigned char *out, size_t olen,
|
||||
const char *label, size_t llen,
|
||||
const unsigned char *context,
|
||||
size_t contextlen);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
During the creation of a TLS or DTLS connection shared keying material is
|
||||
established between the two endpoints. The function SSL_export_keying_material()
|
||||
enables an application to use some of this keying material for its own purposes
|
||||
in accordance with RFC5705 (for TLSv1.2 and below) or RFCXXXX (for TLSv1.3).
|
||||
established between the two endpoints. The functions
|
||||
SSL_export_keying_material() and SSL_export_keying_material_early() enable an
|
||||
application to use some of this keying material for its own purposes in
|
||||
accordance with RFC5705 (for TLSv1.2 and below) or RFCXXXX (for TLSv1.3).
|
||||
TODO(TLS1.3): Update the RFC number when the RFC is published.
|
||||
|
||||
SSL_export_keying_material() derives keying material using
|
||||
the F<exporter_master_secret> established in the handshake.
|
||||
|
||||
SSL_export_keying_material_early() is only usable with TLSv1.3, and derives
|
||||
keying material using the F<early_exporter_master_secret> (as defined in the
|
||||
TLS 1.3 RFC). For the client, the F<early_exporter_master_secret> is only
|
||||
available when the client attempts to send 0-RTT data. For the server, it is
|
||||
only available when the server accepts 0-RTT data.
|
||||
|
||||
An application may need to securely establish the context within which this
|
||||
keying material will be used. For example this may include identifiers for the
|
||||
application session, application algorithms or parameters, or the lifetime of
|
||||
|
@ -52,6 +69,12 @@ above. Attempting to use it in SSLv3 will result in an error.
|
|||
|
||||
SSL_export_keying_material() returns 0 or -1 on failure or 1 on success.
|
||||
|
||||
SSL_export_keying_material_early() returns 0 on failure or 1 on success.
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
SSL_export_keying_material_early() was first added in OpenSSL 1.1.1.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
|
|
@ -232,6 +232,19 @@ __owur int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
|
|||
const unsigned char *context,
|
||||
size_t contextlen, int use_context);
|
||||
|
||||
/*
|
||||
* SSL_export_keying_material_early exports a value derived from the
|
||||
* early exporter master secret, as specified in
|
||||
* https://tools.ietf.org/html/draft-ietf-tls-tls13-23. It writes
|
||||
* |olen| bytes to |out| given a label and optional context. It
|
||||
* returns 1 on success and 0 otherwise.
|
||||
*/
|
||||
__owur int SSL_export_keying_material_early(SSL *s, unsigned char *out,
|
||||
size_t olen, const char *label,
|
||||
size_t llen,
|
||||
const unsigned char *context,
|
||||
size_t contextlen);
|
||||
|
||||
int SSL_get_peer_signature_type_nid(const SSL *s, int *pnid);
|
||||
|
||||
int SSL_get_sigalgs(SSL *s, int idx,
|
||||
|
|
|
@ -2810,6 +2810,18 @@ int SSL_export_keying_material(SSL *s, unsigned char *out, size_t olen,
|
|||
contextlen, use_context);
|
||||
}
|
||||
|
||||
int SSL_export_keying_material_early(SSL *s, unsigned char *out, size_t olen,
|
||||
const char *label, size_t llen,
|
||||
const unsigned char *context,
|
||||
size_t contextlen)
|
||||
{
|
||||
if (s->version != TLS1_3_VERSION)
|
||||
return 0;
|
||||
|
||||
return tls13_export_keying_material_early(s, out, olen, label, llen,
|
||||
context, contextlen);
|
||||
}
|
||||
|
||||
static unsigned long ssl_session_hash(const SSL_SESSION *a)
|
||||
{
|
||||
const unsigned char *session_id = a->session_id;
|
||||
|
|
|
@ -1111,6 +1111,7 @@ struct ssl_st {
|
|||
unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE];
|
||||
unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE];
|
||||
unsigned char exporter_master_secret[EVP_MAX_MD_SIZE];
|
||||
unsigned char early_exporter_master_secret[EVP_MAX_MD_SIZE];
|
||||
EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
|
||||
unsigned char read_iv[EVP_MAX_IV_LENGTH]; /* TLSv1.3 static read IV */
|
||||
EVP_MD_CTX *read_hash; /* used for mac generation */
|
||||
|
@ -2406,6 +2407,11 @@ __owur int tls13_export_keying_material(SSL *s, unsigned char *out, size_t olen,
|
|||
const char *label, size_t llen,
|
||||
const unsigned char *context,
|
||||
size_t contextlen, int use_context);
|
||||
__owur int tls13_export_keying_material_early(SSL *s, unsigned char *out,
|
||||
size_t olen, const char *label,
|
||||
size_t llen,
|
||||
const unsigned char *context,
|
||||
size_t contextlen);
|
||||
__owur int tls1_alert_code(int code);
|
||||
__owur int tls13_alert_code(int code);
|
||||
__owur int ssl3_alert_code(int code);
|
||||
|
|
|
@ -951,3 +951,18 @@ int ossl_statem_export_allowed(SSL *s)
|
|||
return s->s3->previous_server_finished_len != 0
|
||||
&& s->statem.hand_state != TLS_ST_SW_FINISHED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1 if early TLS exporter is ready to export keying material,
|
||||
* or 0 if otherwise.
|
||||
*/
|
||||
int ossl_statem_export_early_allowed(SSL *s)
|
||||
{
|
||||
/*
|
||||
* The early exporter secret is only present on the server if we
|
||||
* have accepted early_data. It is present on the client as long
|
||||
* as we have sent early_data.
|
||||
*/
|
||||
return s->ext.early_data == SSL_EARLY_DATA_ACCEPTED
|
||||
|| (!s->server && s->ext.early_data != SSL_EARLY_DATA_NOT_SENT);
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ void ossl_statem_check_finish_init(SSL *s, int send);
|
|||
void ossl_statem_set_hello_verify_done(SSL *s);
|
||||
__owur int ossl_statem_app_data_allowed(SSL *s);
|
||||
__owur int ossl_statem_export_allowed(SSL *s);
|
||||
__owur int ossl_statem_export_early_allowed(SSL *s);
|
||||
|
||||
/* Flush the write BIO */
|
||||
int statem_flush(SSL *s);
|
||||
|
|
|
@ -365,6 +365,7 @@ int tls13_change_cipher_state(SSL *s, int which)
|
|||
static const unsigned char server_application_traffic[] = "s ap traffic";
|
||||
static const unsigned char exporter_master_secret[] = "exp master";
|
||||
static const unsigned char resumption_master_secret[] = "res master";
|
||||
static const unsigned char early_exporter_master_secret[] = "e exp master";
|
||||
unsigned char *iv;
|
||||
unsigned char secret[EVP_MAX_MD_SIZE];
|
||||
unsigned char hashval[EVP_MAX_MD_SIZE];
|
||||
|
@ -481,6 +482,16 @@ int tls13_change_cipher_state(SSL *s, int which)
|
|||
}
|
||||
hashlen = hashlenui;
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
|
||||
if (!tls13_hkdf_expand(s, md, insecret,
|
||||
early_exporter_master_secret,
|
||||
sizeof(early_exporter_master_secret) - 1,
|
||||
hashval, hashlen,
|
||||
s->early_exporter_master_secret, hashlen)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
} else if (which & SSL3_CC_HANDSHAKE) {
|
||||
insecret = s->handshake_secret;
|
||||
finsecret = s->client_finished_secret;
|
||||
|
@ -690,3 +701,62 @@ int tls13_export_keying_material(SSL *s, unsigned char *out, size_t olen,
|
|||
EVP_MD_CTX_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tls13_export_keying_material_early(SSL *s, unsigned char *out, size_t olen,
|
||||
const char *label, size_t llen,
|
||||
const unsigned char *context,
|
||||
size_t contextlen)
|
||||
{
|
||||
static const unsigned char exporterlabel[] = "exporter";
|
||||
unsigned char exportsecret[EVP_MAX_MD_SIZE];
|
||||
unsigned char hash[EVP_MAX_MD_SIZE], data[EVP_MAX_MD_SIZE];
|
||||
const EVP_MD *md;
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||
unsigned int hashsize, datalen;
|
||||
int ret = 0;
|
||||
const SSL_CIPHER *sslcipher;
|
||||
|
||||
if (ctx == NULL || !ossl_statem_export_early_allowed(s))
|
||||
goto err;
|
||||
|
||||
if (!s->server && s->max_early_data > 0
|
||||
&& s->session->ext.max_early_data == 0)
|
||||
sslcipher = SSL_SESSION_get0_cipher(s->psksession);
|
||||
else
|
||||
sslcipher = SSL_SESSION_get0_cipher(s->session);
|
||||
|
||||
md = ssl_md(sslcipher->algorithm2);
|
||||
|
||||
/*
|
||||
* Calculate the hash value and store it in |data|. The reason why
|
||||
* the empty string is used is that the definition of TLS-Exporter
|
||||
* is like so:
|
||||
*
|
||||
* TLS-Exporter(label, context_value, key_length) =
|
||||
* HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
|
||||
* "exporter", Hash(context_value), key_length)
|
||||
*
|
||||
* Derive-Secret(Secret, Label, Messages) =
|
||||
* HKDF-Expand-Label(Secret, Label,
|
||||
* Transcript-Hash(Messages), Hash.length)
|
||||
*
|
||||
* Here Transcript-Hash is the cipher suite hash algorithm.
|
||||
*/
|
||||
if (EVP_DigestInit_ex(ctx, md, NULL) <= 0
|
||||
|| EVP_DigestUpdate(ctx, context, contextlen) <= 0
|
||||
|| EVP_DigestFinal_ex(ctx, hash, &hashsize) <= 0
|
||||
|| EVP_DigestInit_ex(ctx, md, NULL) <= 0
|
||||
|| EVP_DigestFinal_ex(ctx, data, &datalen) <= 0
|
||||
|| !tls13_hkdf_expand(s, md, s->early_exporter_master_secret,
|
||||
(const unsigned char *)label, llen,
|
||||
data, datalen, exportsecret, hashsize)
|
||||
|| !tls13_hkdf_expand(s, md, exportsecret, exporterlabel,
|
||||
sizeof(exporterlabel) - 1, hash, hashsize,
|
||||
out, olen))
|
||||
goto err;
|
||||
|
||||
ret = 1;
|
||||
err:
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -3157,6 +3157,85 @@ static int test_export_key_mat(int tst)
|
|||
return testresult;
|
||||
}
|
||||
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
/*
|
||||
* Test that SSL_export_keying_material_early() produces expected
|
||||
* results. There are no test vectors so all we do is test that both
|
||||
* sides of the communication produce the same results for different
|
||||
* protocol versions.
|
||||
*/
|
||||
static int test_export_key_mat_early(int idx)
|
||||
{
|
||||
static const char label[] = "test label";
|
||||
static const unsigned char context[] = "context";
|
||||
int testresult = 0;
|
||||
SSL_CTX *cctx = NULL, *sctx = NULL;
|
||||
SSL *clientssl = NULL, *serverssl = NULL;
|
||||
SSL_SESSION *sess = NULL;
|
||||
const unsigned char *emptycontext = NULL;
|
||||
unsigned char ckeymat1[80], ckeymat2[80];
|
||||
unsigned char skeymat1[80], skeymat2[80];
|
||||
unsigned char buf[1];
|
||||
size_t readbytes, written;
|
||||
|
||||
if (!TEST_true(setupearly_data_test(&cctx, &sctx, &clientssl, &serverssl,
|
||||
&sess, idx)))
|
||||
goto end;
|
||||
|
||||
/* Here writing 0 length early data is enough. */
|
||||
if (!TEST_true(SSL_write_early_data(clientssl, NULL, 0, &written))
|
||||
|| !TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf),
|
||||
&readbytes),
|
||||
SSL_READ_EARLY_DATA_ERROR)
|
||||
|| !TEST_int_eq(SSL_get_early_data_status(serverssl),
|
||||
SSL_EARLY_DATA_ACCEPTED))
|
||||
goto end;
|
||||
|
||||
if (!TEST_int_eq(SSL_export_keying_material_early(
|
||||
clientssl, ckeymat1, sizeof(ckeymat1), label,
|
||||
sizeof(label) - 1, context, sizeof(context) - 1), 1)
|
||||
|| !TEST_int_eq(SSL_export_keying_material_early(
|
||||
clientssl, ckeymat2, sizeof(ckeymat2), label,
|
||||
sizeof(label) - 1, emptycontext, 0), 1)
|
||||
|| !TEST_int_eq(SSL_export_keying_material_early(
|
||||
serverssl, skeymat1, sizeof(skeymat1), label,
|
||||
sizeof(label) - 1, context, sizeof(context) - 1), 1)
|
||||
|| !TEST_int_eq(SSL_export_keying_material_early(
|
||||
serverssl, skeymat2, sizeof(skeymat2), label,
|
||||
sizeof(label) - 1, emptycontext, 0), 1)
|
||||
/*
|
||||
* Check that both sides created the same key material with the
|
||||
* same context.
|
||||
*/
|
||||
|| !TEST_mem_eq(ckeymat1, sizeof(ckeymat1), skeymat1,
|
||||
sizeof(skeymat1))
|
||||
/*
|
||||
* Check that both sides created the same key material with an
|
||||
* empty context.
|
||||
*/
|
||||
|| !TEST_mem_eq(ckeymat2, sizeof(ckeymat2), skeymat2,
|
||||
sizeof(skeymat2))
|
||||
/* Different contexts should produce different results */
|
||||
|| !TEST_mem_ne(ckeymat1, sizeof(ckeymat1), ckeymat2,
|
||||
sizeof(ckeymat2)))
|
||||
goto end;
|
||||
|
||||
testresult = 1;
|
||||
|
||||
end:
|
||||
if (sess != clientpsk)
|
||||
SSL_SESSION_free(sess);
|
||||
SSL_SESSION_free(clientpsk);
|
||||
SSL_SESSION_free(serverpsk);
|
||||
SSL_free(serverssl);
|
||||
SSL_free(clientssl);
|
||||
SSL_CTX_free(sctx);
|
||||
SSL_CTX_free(cctx);
|
||||
|
||||
return testresult;
|
||||
}
|
||||
#endif /* OPENSSL_NO_TLS1_3 */
|
||||
|
||||
static int test_ssl_clear(int idx)
|
||||
{
|
||||
SSL_CTX *cctx = NULL, *sctx = NULL;
|
||||
|
@ -3431,6 +3510,9 @@ int setup_tests(void)
|
|||
#endif
|
||||
ADD_ALL_TESTS(test_serverinfo, 8);
|
||||
ADD_ALL_TESTS(test_export_key_mat, 4);
|
||||
#ifndef OPENSSL_NO_TLS1_3
|
||||
ADD_ALL_TESTS(test_export_key_mat_early, 3);
|
||||
#endif
|
||||
ADD_ALL_TESTS(test_ssl_clear, 2);
|
||||
ADD_ALL_TESTS(test_max_fragment_len_ext, OSSL_NELEM(max_fragment_len_test));
|
||||
return 1;
|
||||
|
|
|
@ -217,6 +217,11 @@ int ossl_statem_export_allowed(SSL *s)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int ossl_statem_export_early_allowed(SSL *s)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* End of mocked out code */
|
||||
|
||||
static int test_secret(SSL *s, unsigned char *prk,
|
||||
|
|
|
@ -476,3 +476,4 @@ SSL_SESSION_get_max_fragment_length 476 1_1_1 EXIST::FUNCTION:
|
|||
SSL_stateless 477 1_1_1 EXIST::FUNCTION:
|
||||
SSL_verify_client_post_handshake 478 1_1_1 EXIST::FUNCTION:
|
||||
SSL_force_post_handshake_auth 479 1_1_1 EXIST::FUNCTION:
|
||||
SSL_export_keying_material_early 480 1_1_1 EXIST::FUNCTION:
|
||||
|
|
Loading…
Reference in a new issue