Added implementation of keying material export (RFC 5705) (API for PRF implementations changed, to handle chunked seeds).

This commit is contained in:
Thomas Pornin 2017-07-04 20:43:39 +02:00
parent 2b738493bd
commit ea95d8264c
15 changed files with 256 additions and 77 deletions

View file

@ -509,8 +509,8 @@ void br_sha2small_round(const unsigned char *buf, uint32_t *val);
*/
void br_tls_phash(void *dst, size_t len,
const br_hash_class *dig,
const void *secret, size_t secret_len,
const char *label, const void *seed, size_t seed_len);
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed);
/*
* Copy all configured hash implementations from a multihash context

View file

@ -28,14 +28,14 @@
void
br_tls_phash(void *dst, size_t len,
const br_hash_class *dig,
const void *secret, size_t secret_len,
const char *label, const void *seed, size_t seed_len)
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed)
{
unsigned char *buf;
unsigned char tmp[64], a[64];
br_hmac_key_context kc;
br_hmac_context hc;
size_t label_len, hlen;
size_t label_len, hlen, u;
if (len == 0) {
return;
@ -46,15 +46,17 @@ br_tls_phash(void *dst, size_t len,
br_hmac_key_init(&kc, dig, secret, secret_len);
br_hmac_init(&hc, &kc, 0);
br_hmac_update(&hc, label, label_len);
br_hmac_update(&hc, seed, seed_len);
for (u = 0; u < seed_num; u ++) {
br_hmac_update(&hc, seed[u].data, seed[u].len);
}
br_hmac_out(&hc, a);
for (;;) {
size_t u;
br_hmac_init(&hc, &kc, 0);
br_hmac_update(&hc, a, hlen);
br_hmac_update(&hc, label, label_len);
br_hmac_update(&hc, seed, seed_len);
for (u = 0; u < seed_num; u ++) {
br_hmac_update(&hc, seed[u].data, seed[u].len);
}
br_hmac_out(&hc, tmp);
for (u = 0; u < hlen && u < len; u ++) {
buf[u] ^= tmp[u];

View file

@ -27,8 +27,8 @@
/* see bearssl.h */
void
br_tls10_prf(void *dst, size_t len,
const void *secret, size_t secret_len,
const char *label, const void *seed, size_t seed_len)
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed)
{
const unsigned char *s1;
size_t slen;
@ -37,7 +37,7 @@ br_tls10_prf(void *dst, size_t len,
slen = (secret_len + 1) >> 1;
memset(dst, 0, len);
br_tls_phash(dst, len, &br_md5_vtable,
s1, slen, label, seed, seed_len);
s1, slen, label, seed_num, seed);
br_tls_phash(dst, len, &br_sha1_vtable,
s1 + secret_len - slen, slen, label, seed, seed_len);
s1 + secret_len - slen, slen, label, seed_num, seed);
}

View file

@ -27,10 +27,10 @@
/* see bearssl.h */
void
br_tls12_sha256_prf(void *dst, size_t len,
const void *secret, size_t secret_len,
const char *label, const void *seed, size_t seed_len)
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed)
{
memset(dst, 0, len);
br_tls_phash(dst, len, &br_sha256_vtable,
secret, secret_len, label, seed, seed_len);
secret, secret_len, label, seed_num, seed);
}

View file

@ -27,10 +27,10 @@
/* see bearssl.h */
void
br_tls12_sha384_prf(void *dst, size_t len,
const void *secret, size_t secret_len,
const char *label, const void *seed, size_t seed_len)
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed)
{
memset(dst, 0, len);
br_tls_phash(dst, len, &br_sha384_vtable,
secret, secret_len, label, seed, seed_len);
secret, secret_len, label, seed_num, seed);
}

View file

@ -1335,13 +1335,14 @@ br_ssl_engine_compute_master(br_ssl_engine_context *cc,
int prf_id, const void *pms, size_t pms_len)
{
br_tls_prf_impl iprf;
unsigned char seed[64];
br_tls_prf_seed_chunk seed[2] = {
{ cc->client_random, sizeof cc->client_random },
{ cc->server_random, sizeof cc->server_random }
};
iprf = br_ssl_engine_get_PRF(cc, prf_id);
memcpy(seed, cc->client_random, 32);
memcpy(seed + 32, cc->server_random, 32);
iprf(cc->session.master_secret, sizeof cc->session.master_secret,
pms, pms_len, "master secret", seed, sizeof seed);
pms, pms_len, "master secret", 2, seed);
}
/*
@ -1352,14 +1353,15 @@ compute_key_block(br_ssl_engine_context *cc, int prf_id,
size_t half_len, unsigned char *kb)
{
br_tls_prf_impl iprf;
unsigned char seed[64];
br_tls_prf_seed_chunk seed[2] = {
{ cc->server_random, sizeof cc->server_random },
{ cc->client_random, sizeof cc->client_random }
};
iprf = br_ssl_engine_get_PRF(cc, prf_id);
memcpy(seed, cc->server_random, 32);
memcpy(seed + 32, cc->client_random, 32);
iprf(kb, half_len << 1,
cc->session.master_secret, sizeof cc->session.master_secret,
"key expansion", seed, sizeof seed);
"key expansion", 2, seed);
}
/* see inner.h */

View file

@ -1192,21 +1192,22 @@ br_ssl_hs_client_run(void *t0ctx)
int prf_id = T0_POP();
int from_client = T0_POPi();
unsigned char seed[48];
size_t seed_len;
unsigned char tmp[48];
br_tls_prf_seed_chunk seed;
br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
seed.data = tmp;
if (ENG->session.version >= BR_TLS12) {
seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
} else {
br_multihash_out(&ENG->mhash, br_md5_ID, seed);
br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
seed_len = 36;
br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
seed.len = 36;
}
prf(ENG->pad, 12, ENG->session.master_secret,
sizeof ENG->session.master_secret,
from_client ? "client finished" : "server finished",
seed, seed_len);
1, &seed);
}
break;

View file

@ -754,6 +754,10 @@ cc: mkrand ( addr len -- ) {
\ -- PRF for TLS-1.2:
\ 4 with SHA-256
\ 5 with SHA-384
\
\ WARNING: if adding a new cipher suite that does not use SHA-256 for the
\ PRF (with TLS 1.2), be sure to check the suites_sha384[] array defined
\ in ssl/ssl_keyexport.c
data: cipher-suite-def
@ -1019,21 +1023,22 @@ cc: switch-chapol-in ( is_client prf_id -- ) {
cc: compute-Finished-inner ( from_client prf_id -- ) {
int prf_id = T0_POP();
int from_client = T0_POPi();
unsigned char seed[48];
size_t seed_len;
unsigned char tmp[48];
br_tls_prf_seed_chunk seed;
br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
seed.data = tmp;
if (ENG->session.version >= BR_TLS12) {
seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
} else {
br_multihash_out(&ENG->mhash, br_md5_ID, seed);
br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
seed_len = 36;
br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
seed.len = 36;
}
prf(ENG->pad, 12, ENG->session.master_secret,
sizeof ENG->session.master_secret,
from_client ? "client finished" : "server finished",
seed, seed_len);
1, &seed);
}
\ Receive ChangeCipherSpec and Finished from the peer.

View file

@ -1250,21 +1250,22 @@ br_ssl_hs_server_run(void *t0ctx)
int prf_id = T0_POP();
int from_client = T0_POPi();
unsigned char seed[48];
size_t seed_len;
unsigned char tmp[48];
br_tls_prf_seed_chunk seed;
br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id);
seed.data = tmp;
if (ENG->session.version >= BR_TLS12) {
seed_len = br_multihash_out(&ENG->mhash, prf_id, seed);
seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp);
} else {
br_multihash_out(&ENG->mhash, br_md5_ID, seed);
br_multihash_out(&ENG->mhash, br_sha1_ID, seed + 16);
seed_len = 36;
br_multihash_out(&ENG->mhash, br_md5_ID, tmp);
br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16);
seed.len = 36;
}
prf(ENG->pad, 12, ENG->session.master_secret,
sizeof ENG->session.master_secret,
from_client ? "client finished" : "server finished",
seed, seed_len);
1, &seed);
}
break;

83
src/ssl/ssl_keyexport.c Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "inner.h"
/*
* Supported cipher suites that use SHA-384 for the PRF when selected
* for TLS 1.2. All other cipher suites are deemed to use SHA-256.
*/
static const uint16_t suites_sha384[] = {
BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
};
/* see bearssl_ssl.h */
int
br_ssl_key_export(br_ssl_engine_context *cc,
void *dst, size_t len, const char *label,
const void *context, size_t context_len)
{
br_tls_prf_seed_chunk chunks[4];
br_tls_prf_impl iprf;
size_t num_chunks, u;
unsigned char tmp[2];
int prf_id;
if (cc->application_data != 1) {
return 0;
}
chunks[0].data = cc->client_random;
chunks[0].len = sizeof cc->client_random;
chunks[1].data = cc->server_random;
chunks[1].len = sizeof cc->server_random;
if (context != NULL) {
br_enc16be(tmp, (unsigned)context_len);
chunks[2].data = tmp;
chunks[2].len = 2;
chunks[3].data = context;
chunks[3].len = context_len;
num_chunks = 4;
} else {
num_chunks = 2;
}
prf_id = BR_SSLPRF_SHA256;
for (u = 0; u < (sizeof suites_sha384) / sizeof(uint16_t); u ++) {
if (suites_sha384[u] == cc->session.cipher_suite) {
prf_id = BR_SSLPRF_SHA384;
}
}
iprf = br_ssl_engine_get_PRF(cc, prf_id);
iprf(dst, len,
cc->session.master_secret, sizeof cc->session.master_secret,
label, num_chunks, chunks);
return 1;
}