feat(tls): TLS 1.3 key schedule (rv2 9 phase F2)
- wo_tls_derive_handshake: Early/Handshake/Master secrets + client/server handshake-traffic secrets from the ECDHE shared secret and the ClientHello..ServerHello transcript hash (RFC 8446 §7.1) - wo_tls_derive_application: client/server application-traffic secrets from master_secret + the ClientHello..server-Finished transcript hash - wo_tls_traffic_keys: record key + IV via HKDF-Expand-Label "key"/"iv" - wo_tls_finished_verify: finished_key = Expand-Label(base,"finished"), verify_data = HMAC(finished_key, transcript_hash) - all over phase-B HKDF (Extract/Expand-Label) + Derive-Secret helper - KAT vs RFC 8448 §3 "Simple 1-RTT Handshake" byte-for-byte: c/s hs traffic, master, c/s ap traffic, server hs key+iv. Also validates the phase-B "tls13 " Expand-Label. test_tls 58 pass, ASan/UBSan clean Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 417fcc16f80c1dd31e84a0336944573902fde06e)
This commit is contained in:
parent
7e71c1a171
commit
25dda4cb2f
3 changed files with 135 additions and 0 deletions
|
|
@ -109,3 +109,61 @@ int wo_tls_record_open(int suite, const uint8_t *key, size_t keylen,
|
|||
*content_type = out[n - 1];
|
||||
return (int)(n - 1);
|
||||
}
|
||||
|
||||
/* ---- key schedule (phase F2, RFC 8446 §7.1) -----------------------------
|
||||
* Derive-Secret(Secret, Label, Messages)
|
||||
* = HKDF-Expand-Label(Secret, Label, Transcript-Hash(Messages), Hash.len)
|
||||
* with Hash = SHA-256 for the suites we implement. The whole schedule is a
|
||||
* chain of HKDF-Extract and Derive-Secret over the phase-B primitives. */
|
||||
|
||||
/* Transcript hash of the empty message list: SHA-256(""). */
|
||||
static void empty_hash(uint8_t out[32]) { wo_sha256((const uint8_t *)"", 0, out); }
|
||||
|
||||
/* Derive-Secret with an explicit 32-byte transcript hash. */
|
||||
static void derive_secret(const uint8_t secret[32], const char *label,
|
||||
const uint8_t transcript[32], uint8_t out[32]) {
|
||||
wo_hkdf_sha256_expand_label(secret, label, strlen(label), transcript, 32,
|
||||
out, 32);
|
||||
}
|
||||
|
||||
void wo_tls_derive_handshake(wo_tls_key_schedule *ks, const uint8_t *ecdhe,
|
||||
size_t ecdhe_len, const uint8_t hash_ch_sh[32]) {
|
||||
uint8_t zeros[32] = {0}, eh[32], early[32], derived[32];
|
||||
empty_hash(eh);
|
||||
|
||||
/* Early Secret = HKDF-Extract(0, 0) (no PSK). */
|
||||
wo_hkdf_sha256_extract(zeros, 32, zeros, 32, early);
|
||||
/* Handshake Secret = HKDF-Extract(Derive-Secret(early,"derived",""), ECDHE). */
|
||||
derive_secret(early, "derived", eh, derived);
|
||||
wo_hkdf_sha256_extract(derived, 32, ecdhe, ecdhe_len, ks->handshake_secret);
|
||||
|
||||
derive_secret(ks->handshake_secret, "c hs traffic", hash_ch_sh,
|
||||
ks->client_hs_traffic);
|
||||
derive_secret(ks->handshake_secret, "s hs traffic", hash_ch_sh,
|
||||
ks->server_hs_traffic);
|
||||
|
||||
/* Master Secret = HKDF-Extract(Derive-Secret(hs,"derived",""), 0). */
|
||||
derive_secret(ks->handshake_secret, "derived", eh, derived);
|
||||
wo_hkdf_sha256_extract(derived, 32, zeros, 32, ks->master_secret);
|
||||
}
|
||||
|
||||
void wo_tls_derive_application(wo_tls_key_schedule *ks,
|
||||
const uint8_t hash_ch_sf[32]) {
|
||||
derive_secret(ks->master_secret, "c ap traffic", hash_ch_sf,
|
||||
ks->client_ap_traffic);
|
||||
derive_secret(ks->master_secret, "s ap traffic", hash_ch_sf,
|
||||
ks->server_ap_traffic);
|
||||
}
|
||||
|
||||
void wo_tls_traffic_keys(const uint8_t traffic_secret[32], size_t key_len,
|
||||
uint8_t *key, uint8_t iv[12]) {
|
||||
wo_hkdf_sha256_expand_label(traffic_secret, "key", 3, NULL, 0, key, key_len);
|
||||
wo_hkdf_sha256_expand_label(traffic_secret, "iv", 2, NULL, 0, iv, 12);
|
||||
}
|
||||
|
||||
void wo_tls_finished_verify(const uint8_t base_key[32],
|
||||
const uint8_t transcript_hash[32], uint8_t out[32]) {
|
||||
uint8_t finished_key[32];
|
||||
wo_hkdf_sha256_expand_label(base_key, "finished", 8, NULL, 0, finished_key, 32);
|
||||
wo_hmac_sha256(finished_key, 32, transcript_hash, 32, out);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,4 +47,37 @@ int wo_tls_record_open(int suite, const uint8_t *key, size_t keylen,
|
|||
const uint8_t iv[12], uint64_t seq, const uint8_t *rec,
|
||||
size_t reclen, uint8_t *out, uint8_t *content_type);
|
||||
|
||||
/* ---- key schedule (phase F2, RFC 8446 §7.1, SHA-256) --------------------- */
|
||||
|
||||
/* The traffic secrets the handshake derives, in the order they become known. */
|
||||
typedef struct {
|
||||
uint8_t handshake_secret[32];
|
||||
uint8_t master_secret[32];
|
||||
uint8_t client_hs_traffic[32];
|
||||
uint8_t server_hs_traffic[32];
|
||||
uint8_t client_ap_traffic[32];
|
||||
uint8_t server_ap_traffic[32];
|
||||
} wo_tls_key_schedule;
|
||||
|
||||
/* Handshake-phase secrets from the ECDHE shared secret and the
|
||||
* ClientHello..ServerHello transcript hash. Fills handshake_secret, the two
|
||||
* hs-traffic secrets, and master_secret (which needs no further transcript). */
|
||||
void wo_tls_derive_handshake(wo_tls_key_schedule *ks, const uint8_t *ecdhe,
|
||||
size_t ecdhe_len, const uint8_t hash_ch_sh[32]);
|
||||
|
||||
/* Application-phase traffic secrets from master_secret (already in ks) and the
|
||||
* ClientHello..server-Finished transcript hash. */
|
||||
void wo_tls_derive_application(wo_tls_key_schedule *ks,
|
||||
const uint8_t hash_ch_sf[32]);
|
||||
|
||||
/* Per-direction AEAD key (key_len 16 or 32) and 12-byte IV from a traffic
|
||||
* secret (HKDF-Expand-Label "key"/"iv"). */
|
||||
void wo_tls_traffic_keys(const uint8_t traffic_secret[32], size_t key_len,
|
||||
uint8_t *key, uint8_t iv[12]);
|
||||
|
||||
/* Finished verify_data = HMAC(HKDF-Expand-Label(base_key,"finished","",32),
|
||||
* transcript_hash). Same routine builds and checks it (compare with ct_memeq). */
|
||||
void wo_tls_finished_verify(const uint8_t base_key[32],
|
||||
const uint8_t transcript_hash[32], uint8_t out[32]);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,6 +8,17 @@
|
|||
#include "t.h"
|
||||
#include "tls_record_vectors.h"
|
||||
|
||||
/* hex string -> bytes; returns the byte count. */
|
||||
static size_t unhex(const char *h, uint8_t *out) {
|
||||
size_t n = 0;
|
||||
for (; h[0] && h[1]; h += 2) {
|
||||
unsigned v;
|
||||
sscanf(h, "%2x", &v);
|
||||
out[n++] = (uint8_t)v;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
uint8_t iv[12], aeskey[16], chakey[32], pt[TLSREC_PTLEN];
|
||||
memcpy(iv, TLSREC_IV, 12);
|
||||
|
|
@ -89,5 +100,38 @@ int main(void) {
|
|||
out) == -1);
|
||||
}
|
||||
|
||||
/* Key schedule (phase F2) against RFC 8448 §3 "Simple 1-RTT Handshake". */
|
||||
{
|
||||
uint8_t ecdhe[32], hash_ch_sh[32], hash_ch_sf[32];
|
||||
uint8_t c_hs[32], s_hs[32], c_ap[32], s_ap[32], master[32];
|
||||
uint8_t s_hs_key[16], s_hs_iv[12];
|
||||
unhex("8bd4054fb55b9d63fdfbacf9f04b9f0d35e6d63f537563efd46272900f89492d", ecdhe);
|
||||
unhex("860c06edc07858ee8e78f0e7428c58edd6b43f2ca3e6e95f02ed063cf0e1cad8", hash_ch_sh);
|
||||
unhex("9608102a0f1ccc6db6250b7b7e417b1a000eaada3daae4777a7686c9ff83df13", hash_ch_sf);
|
||||
unhex("b3eddb126e067f35a780b3abf45e2d8f3b1a950738f52e9600746a0e27a55a21", c_hs);
|
||||
unhex("b67b7d690cc16c4e75e54213cb2d37b4e9c912bcded9105d42befd59d391ad38", s_hs);
|
||||
unhex("18df06843d13a08bf2a449844c5f8a478001bc4d4c627984d5a41da8d0402919", master);
|
||||
unhex("9e40646ce79a7f9dc05af8889bce6552875afa0b06df0087f792ebb7c17504a5", c_ap);
|
||||
unhex("a11af9f05531f856ad47116b45a950328204b4f44bfb6b3a4b4f1f3fcb631643", s_ap);
|
||||
unhex("3fce516009c21727d0f2e4e86ee403bc", s_hs_key);
|
||||
unhex("5d313eb2671276ee13000b30", s_hs_iv);
|
||||
|
||||
wo_tls_key_schedule ks;
|
||||
wo_tls_derive_handshake(&ks, ecdhe, 32, hash_ch_sh);
|
||||
T_CHECK(memcmp(ks.client_hs_traffic, c_hs, 32) == 0);
|
||||
T_CHECK(memcmp(ks.server_hs_traffic, s_hs, 32) == 0);
|
||||
T_CHECK(memcmp(ks.master_secret, master, 32) == 0);
|
||||
|
||||
wo_tls_derive_application(&ks, hash_ch_sf);
|
||||
T_CHECK(memcmp(ks.client_ap_traffic, c_ap, 32) == 0);
|
||||
T_CHECK(memcmp(ks.server_ap_traffic, s_ap, 32) == 0);
|
||||
|
||||
/* Traffic key + iv from the server hs traffic secret. */
|
||||
uint8_t k[16], vv[12];
|
||||
wo_tls_traffic_keys(ks.server_hs_traffic, 16, k, vv);
|
||||
T_CHECK(memcmp(k, s_hs_key, 16) == 0);
|
||||
T_CHECK(memcmp(vv, s_hs_iv, 12) == 0);
|
||||
}
|
||||
|
||||
return t_report("test_tls");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue