feat(crypto): HKDF-SHA256 for the TLS 1.3 key schedule (rv2 9 phase B)
- wo_hkdf_sha256_extract/expand (RFC 5869) + expand_label (RFC 8446 §7.1), internal C over the existing hmac_sha256; SHA-256 (mandatory-suite hash; SHA-384 a later add for the AES-256 suite) - no builtin, no compiler change -- no .wo consumer yet (the TLS handshake is the consumer); exposed for the C unit test - KAT-gated in test_crypto: RFC 5869 Test Case 1 (PRK + 42-byte OKM) and three HKDF-Expand-Label vectors (key/iv/derived-secret shape); 57/0, ASan/UBSan clean; runtime battery green - rv2 9 ladder: A (AEAD, = rv2 8) and B (HKDF) now done; next C X25519 (cherry picked from commit c8d27b6c89a80cd97a996ff7d8b64ff4895e4b26)
This commit is contained in:
parent
2db3766b66
commit
36ce2332ef
3 changed files with 104 additions and 0 deletions
|
|
@ -199,6 +199,63 @@ void wo_hmac_sha256(const uint8_t *key, size_t klen, const uint8_t *msg,
|
|||
wo_sha256(outer, 96, out);
|
||||
}
|
||||
|
||||
/* ---- HKDF-SHA256 (rv2 9 phase B: the TLS 1.3 key schedule) --------------
|
||||
* RFC 5869 (Extract/Expand) + RFC 8446 §7.1 (Expand-Label), built on the
|
||||
* existing HMAC-SHA256. Internal C consumed by the TLS handshake; no `.wo`
|
||||
* builtin until a `.wo` consumer exists. SHA-256 only — the hash of the
|
||||
* mandatory suites (TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256);
|
||||
* SHA-384 is a later addition for the AES-256 suite. */
|
||||
|
||||
void wo_hkdf_sha256_extract(const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *ikm, size_t ikmlen, uint8_t prk[32]) {
|
||||
uint8_t zero[32] = { 0 };
|
||||
if (!salt || saltlen == 0) { salt = zero; saltlen = 32; }
|
||||
wo_hmac_sha256(salt, saltlen, ikm, ikmlen, prk);
|
||||
}
|
||||
|
||||
/* OKM = T(1)||T(2)||…, T(i) = HMAC(PRK, T(i-1)||info||i). 0 ok, -1 on a
|
||||
* too-long request (>255*32) or OOM. */
|
||||
int wo_hkdf_sha256_expand(const uint8_t prk[32], const uint8_t *info,
|
||||
size_t infolen, uint8_t *okm, size_t okmlen) {
|
||||
if (okmlen > 255u * 32u) return -1;
|
||||
uint8_t t[32];
|
||||
size_t tlen = 0, done = 0;
|
||||
uint8_t counter = 1;
|
||||
while (done < okmlen) {
|
||||
size_t mlen = tlen + infolen + 1;
|
||||
uint8_t *m = (uint8_t *)malloc(mlen ? mlen : 1);
|
||||
if (!m) return -1;
|
||||
if (tlen) memcpy(m, t, tlen);
|
||||
if (infolen) memcpy(m + tlen, info, infolen);
|
||||
m[tlen + infolen] = counter;
|
||||
wo_hmac_sha256(prk, 32, m, mlen, t);
|
||||
free(m);
|
||||
tlen = 32;
|
||||
size_t n = okmlen - done < 32 ? okmlen - done : 32;
|
||||
memcpy(okm + done, t, n);
|
||||
done += n; counter++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RFC 8446 §7.1: HKDF-Expand-Label(secret, label, context, len) where
|
||||
* HkdfLabel = uint16 len || opaque("tls13 "+label) || opaque(context). */
|
||||
int wo_hkdf_sha256_expand_label(const uint8_t secret[32], const char *label,
|
||||
size_t labellen, const uint8_t *ctx,
|
||||
size_t ctxlen, uint8_t *out, size_t outlen) {
|
||||
if (labellen > 249 || ctxlen > 255 || outlen > 65535) return -1;
|
||||
uint8_t info[2 + 1 + 255 + 1 + 255];
|
||||
size_t p = 0;
|
||||
info[p++] = (uint8_t)(outlen >> 8);
|
||||
info[p++] = (uint8_t)outlen;
|
||||
info[p++] = (uint8_t)(6 + labellen);
|
||||
memcpy(info + p, "tls13 ", 6); p += 6;
|
||||
memcpy(info + p, label, labellen); p += labellen;
|
||||
info[p++] = (uint8_t)ctxlen;
|
||||
if (ctxlen) { memcpy(info + p, ctx, ctxlen); p += ctxlen; }
|
||||
return wo_hkdf_sha256_expand(secret, info, p, out, outlen);
|
||||
}
|
||||
|
||||
/* ---- ChaCha20-Poly1305 AEAD (rv2 8 phase A, RFC 8439) ------------------
|
||||
* Hand-rolled, libc-only, constant-time by construction (add/xor/rotate and
|
||||
* limb arithmetic; no data-dependent branches, no table lookups). The
|
||||
|
|
|
|||
|
|
@ -38,6 +38,16 @@ int wo_aes_gcm_open(const uint8_t *key, size_t keylen, const uint8_t nonce[12],
|
|||
const uint8_t *aad, size_t aadlen, const uint8_t *ct,
|
||||
size_t ctlen, const uint8_t tag[16], uint8_t *out);
|
||||
|
||||
/* HKDF-SHA256 (rv2 9 phase B: the TLS 1.3 key schedule). RFC 5869 + RFC 8446
|
||||
* §7.1. Internal to the runtime's crypto/TLS code (no `.wo` builtin yet). */
|
||||
void wo_hkdf_sha256_extract(const uint8_t *salt, size_t saltlen,
|
||||
const uint8_t *ikm, size_t ikmlen, uint8_t prk[32]);
|
||||
int wo_hkdf_sha256_expand(const uint8_t prk[32], const uint8_t *info,
|
||||
size_t infolen, uint8_t *okm, size_t okmlen);
|
||||
int wo_hkdf_sha256_expand_label(const uint8_t secret[32], const char *label,
|
||||
size_t labellen, const uint8_t *ctx,
|
||||
size_t ctxlen, uint8_t *out, size_t outlen);
|
||||
|
||||
int wo_builtin_crypto(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -251,5 +251,42 @@ int main(void) {
|
|||
"76fc6ece0f4e1768cddf8853bb2d551b");
|
||||
wo_aes_force_software = 0;
|
||||
|
||||
/* HKDF-SHA256 (rv2 9 phase B): RFC 5869 Test Case 1 (Extract + Expand). */
|
||||
{
|
||||
uint8_t ikm[22], salt[13], info[10], prk[32], okm[42];
|
||||
char got[85];
|
||||
memset(ikm, 0x0b, 22);
|
||||
for (int i = 0; i < 13; i++) salt[i] = (uint8_t)i;
|
||||
for (int i = 0; i < 10; i++) info[i] = (uint8_t)(0xf0 + i);
|
||||
wo_hkdf_sha256_extract(salt, 13, ikm, 22, prk);
|
||||
hex(prk, 32, got);
|
||||
T_CHECK(strcmp(got,
|
||||
"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5") == 0);
|
||||
T_CHECK(wo_hkdf_sha256_expand(prk, info, 10, okm, 42) == 0);
|
||||
hex(okm, 42, got);
|
||||
T_CHECK(strcmp(got,
|
||||
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf"
|
||||
"34007208d5b887185865") == 0);
|
||||
}
|
||||
/* HKDF-Expand-Label (RFC 8446 §7.1), reference values from a known-good
|
||||
* HKDF-Expand over the tls13 label struct. secret = 0x00..0x1f. */
|
||||
{
|
||||
uint8_t secret[32], out[32], h[32];
|
||||
char got[65];
|
||||
for (int i = 0; i < 32; i++) secret[i] = (uint8_t)i;
|
||||
T_CHECK(wo_hkdf_sha256_expand_label(secret, "key", 3, NULL, 0, out, 16) == 0);
|
||||
hex(out, 16, got);
|
||||
T_CHECK(strcmp(got, "9c9783cf77ea32d44f369da41f19f3cc") == 0);
|
||||
T_CHECK(wo_hkdf_sha256_expand_label(secret, "iv", 2, NULL, 0, out, 12) == 0);
|
||||
hex(out, 12, got);
|
||||
T_CHECK(strcmp(got, "2f41c846a431a163814bcd71") == 0);
|
||||
/* with a context = SHA-256("") (a Derive-Secret shape) */
|
||||
wo_sha256((const uint8_t *)"", 0, h);
|
||||
T_CHECK(wo_hkdf_sha256_expand_label(secret, "derived", 7, h, 32, out, 32) == 0);
|
||||
hex(out, 32, got);
|
||||
T_CHECK(strcmp(got,
|
||||
"a5b1caa258481fdf573ac069f281e534e4a2379ec9e457e0c8494c227efb40e6") == 0);
|
||||
}
|
||||
|
||||
return t_report("test_crypto");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue