Skip to content

Commit aa53c55

Browse files
committed
Move TLS 1.3 HKDF-Expand-Label into the FIPS module
Introduces CRYPTO_tls13_hkdf_expand_label in crypto/fipsmodule/tls/kdf.c, mirroring the CRYPTO_tls1_prf pattern already in that file. The function builds the HkdfLabel structure (RFC 8446 §7.1) via CBB and calls HKDF_expand, bracketed by FIPS_service_indicator_lock_state / unlock_state and a post-success TLS13_KDF_verify_service_indicator call that approves SHA2-256 and SHA2-384 (matching what the ACVP config registers). Consumers now delegate: * ssl/tls13_enc.cc's hkdf_expand_label collapses to a thin wrapper. * The ACVP modulewrapper's HKDFExpandLabel delegates to the same function, so ACVP exercises the actual FIPS-module code path instead of a parallel label-construction reimplementation. A TLS13-KDF KAT (HKDF_extract -> CRYPTO_tls13_hkdf_expand_label, using the RFC 8448 early-secret / 'c e traffic' derivation) is added to the FIPS self-test, along with a matching break-kat.go entry. A new parameterised TLS13KDF_ServiceIndicatorTest covers the approval policy across SHA-1 / SHA-224 / SHA-256 / SHA-384 / SHA-512.
1 parent 455cb10 commit aa53c55

10 files changed

Lines changed: 208 additions & 50 deletions

File tree

crypto/fipsmodule/self_check/self_check.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2873,6 +2873,46 @@ static OPENSSL_NOINLINE int boringssl_self_test_fast(void) {
28732873
goto err;
28742874
}
28752875

2876+
// TLS 1.3 KDF KAT: chain HKDF_extract -> CRYPTO_tls13_hkdf_expand_label to
2877+
// validate the TLS 1.3 HKDF-Expand-Label composite, derived from the sample
2878+
// early-secret / c-e-traffic chain in RFC 8448.
2879+
static const uint8_t kTLS13Secret[32] = {
2880+
0x02, 0x4a, 0x0d, 0x80, 0xf3, 0x57, 0xf2, 0x49, 0x9a, 0x12, 0x44,
2881+
0xda, 0xc2, 0x6d, 0xab, 0x66, 0xfc, 0x13, 0xed, 0x85, 0xfc, 0xa7,
2882+
0x1d, 0xac, 0xe1, 0x46, 0x21, 0x11, 0x19, 0x52, 0x58, 0x74,
2883+
};
2884+
static const uint8_t kTLS13Salt[16] = {
2885+
0x54, 0x61, 0x11, 0x36, 0x75, 0x91, 0xf0, 0xf8,
2886+
0x92, 0xec, 0x70, 0xbd, 0x78, 0x2a, 0xef, 0x61,
2887+
};
2888+
static const uint8_t kTLS13Label[] = "c e traffic";
2889+
static const uint8_t kTLS13ClientHelloHash[32] = {
2890+
0x1d, 0xe8, 0x67, 0xed, 0x93, 0x6a, 0x73, 0x65, 0x9b, 0x05, 0xcf,
2891+
0x8a, 0x22, 0x77, 0xb7, 0x37, 0x29, 0xf2, 0x44, 0x94, 0x81, 0x6a,
2892+
0x83, 0x33, 0x7f, 0x09, 0xbb, 0x6c, 0xc2, 0x6f, 0x48, 0x9c,
2893+
};
2894+
static const uint8_t kTLS13ExpandLabelOutput[32] = {
2895+
0x62, 0x91, 0x52, 0x90, 0x2e, 0xc9, 0xcf, 0x9c, 0x5f, 0x1e, 0x0a,
2896+
0xb7, 0x00, 0x33, 0x42, 0x24, 0xc4, 0xe3, 0xba, 0x01, 0x40, 0x32,
2897+
0x06, 0xab, 0x09, 0x23, 0x8a, 0xdd, 0x01, 0xa4, 0x05, 0xcd,
2898+
};
2899+
uint8_t tls13_extract_output[32];
2900+
size_t tls13_extract_output_len;
2901+
uint8_t tls13_expand_label_output[sizeof(kTLS13ExpandLabelOutput)];
2902+
if (!HKDF_extract(tls13_extract_output, &tls13_extract_output_len,
2903+
EVP_sha256(), kTLS13Secret, sizeof(kTLS13Secret),
2904+
kTLS13Salt, sizeof(kTLS13Salt)) ||
2905+
tls13_extract_output_len != sizeof(tls13_extract_output) ||
2906+
!CRYPTO_tls13_hkdf_expand_label(
2907+
tls13_expand_label_output, sizeof(tls13_expand_label_output),
2908+
EVP_sha256(), tls13_extract_output, sizeof(tls13_extract_output),
2909+
kTLS13Label, sizeof(kTLS13Label) - 1, kTLS13ClientHelloHash,
2910+
sizeof(kTLS13ClientHelloHash)) ||
2911+
!check_test(kTLS13ExpandLabelOutput, tls13_expand_label_output,
2912+
sizeof(kTLS13ExpandLabelOutput), "TLS13-KDF KAT")) {
2913+
goto err;
2914+
}
2915+
28762916
static const uint8_t kPBKDF2Password[] = {
28772917
'A', 'W', 'S', '-', 'L', 'C', 'F', 'I', 'P', 'S', 'p', 'a', 's', 's', 'w',
28782918
'o', 'r', 'd'

crypto/fipsmodule/service_indicator/internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void PBKDF2_verify_service_indicator(const EVP_MD *evp_md, size_t password_len,
5353
void SSHKDF_verify_service_indicator(const EVP_MD *evp_md);
5454
void TLSKDF_verify_service_indicator(const EVP_MD *dgst, const char *label,
5555
size_t label_len);
56+
void TLS13_KDF_verify_service_indicator(const EVP_MD *dgst);
5657
void SSKDF_digest_verify_service_indicator(const EVP_MD *dgst);
5758
void SSKDF_hmac_verify_service_indicator(const EVP_MD *dgst);
5859
void KBKDF_ctr_hmac_verify_service_indicator(const EVP_MD *dgst, size_t secret_len);
@@ -121,6 +122,9 @@ OPENSSL_INLINE void TLSKDF_verify_service_indicator(
121122
OPENSSL_UNUSED const char *label,
122123
OPENSSL_UNUSED size_t label_len) {}
123124

125+
OPENSSL_INLINE void TLS13_KDF_verify_service_indicator(
126+
OPENSSL_UNUSED const EVP_MD *dgst) {}
127+
124128
OPENSSL_INLINE void SSKDF_digest_verify_service_indicator(
125129
OPENSSL_UNUSED const EVP_MD *dgst) {}
126130

crypto/fipsmodule/service_indicator/service_indicator.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,20 @@ void TLSKDF_verify_service_indicator(const EVP_MD *dgst, const char *label,
597597
}
598598
}
599599

600+
void TLS13_KDF_verify_service_indicator(const EVP_MD *dgst) {
601+
// Per RFC 8446 and the corresponding FIPS validation, the TLS 1.3 KDF
602+
// (HKDF-Expand-Label) is approved with SHA2-256 and SHA2-384 as the
603+
// underlying HMAC hash.
604+
switch (dgst->type) {
605+
case NID_sha256:
606+
case NID_sha384:
607+
FIPS_service_indicator_update_state();
608+
break;
609+
default:
610+
break;
611+
}
612+
}
613+
600614
// "Whenever a hash function is employed (including as the primitive used by HMAC), an
601615
// approved hash function shall be used. FIPS 180 and FIPS 202 specify approved hash
602616
// functions"

crypto/fipsmodule/service_indicator/service_indicator_test.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3397,6 +3397,42 @@ TEST_P(KDF_ServiceIndicatorTest, TLSKDF) {
33973397
EXPECT_EQ(approved, test.expect_approved);
33983398
}
33993399

3400+
// TLS 1.3 KDF (HKDF-Expand-Label) is approved under SHA2-256 and SHA2-384, and
3401+
// not approved for any other digest. Label / context contents do not affect
3402+
// approval state for TLS 1.3.
3403+
static const struct TLS13KDFTestVector {
3404+
const EVP_MD *(*func)();
3405+
const FIPSStatus expect_approved;
3406+
} kTLS13KDFTestVectors[] = {
3407+
{EVP_sha1, AWSLC_NOT_APPROVED},
3408+
{EVP_sha224, AWSLC_NOT_APPROVED},
3409+
{EVP_sha256, AWSLC_APPROVED},
3410+
{EVP_sha384, AWSLC_APPROVED},
3411+
{EVP_sha512, AWSLC_NOT_APPROVED},
3412+
};
3413+
3414+
class TLS13KDF_ServiceIndicatorTest
3415+
: public TestWithNoErrors<TLS13KDFTestVector> {};
3416+
3417+
INSTANTIATE_TEST_SUITE_P(All, TLS13KDF_ServiceIndicatorTest,
3418+
testing::ValuesIn(kTLS13KDFTestVectors));
3419+
3420+
TEST_P(TLS13KDF_ServiceIndicatorTest, HKDFExpandLabel) {
3421+
const TLS13KDFTestVector &test = GetParam();
3422+
3423+
static const uint8_t kLabel[] = "c e traffic";
3424+
static const uint8_t kHash[32] = {0};
3425+
FIPSStatus approved = AWSLC_NOT_APPROVED;
3426+
3427+
uint8_t output[32];
3428+
CALL_SERVICE_AND_CHECK_APPROVED(
3429+
approved,
3430+
ASSERT_TRUE(CRYPTO_tls13_hkdf_expand_label(
3431+
output, sizeof(output), test.func(), kTLSSecret, sizeof(kTLSSecret),
3432+
kLabel, sizeof(kLabel) - 1, kHash, sizeof(kHash))));
3433+
EXPECT_EQ(approved, test.expect_approved);
3434+
}
3435+
34003436
// PBKDF2 test data from RFC 6070.
34013437
//
34023438
// Set 1 - short password/salt; these are too short for FIPS, so they'll

crypto/fipsmodule/tls/kdf.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
#include <assert.h>
55

6+
#include <openssl/bytestring.h>
67
#include <openssl/digest.h>
78
#include <openssl/hkdf.h>
89
#include <openssl/hmac.h>
10+
#include <openssl/kdf.h>
911
#include <openssl/mem.h>
1012

1113
#include "../../internal.h"
@@ -127,3 +129,67 @@ int CRYPTO_tls1_prf(const EVP_MD *digest,
127129
}
128130
return ret;
129131
}
132+
133+
int CRYPTO_tls13_hkdf_expand_label(uint8_t *out, size_t out_len,
134+
const EVP_MD *digest,
135+
const uint8_t *secret, size_t secret_len,
136+
const uint8_t *label, size_t label_len,
137+
const uint8_t *hash, size_t hash_len) {
138+
// Lock the FIPS service indicator so the underlying HKDF-Expand (which
139+
// itself locks via HMAC) does not bump the indicator counter. We release and
140+
// update based on the digest at the end.
141+
FIPS_service_indicator_lock_state();
142+
SET_DIT_AUTO_RESET;
143+
144+
static const uint8_t kProtocolLabel[] = "tls13 ";
145+
static const size_t kProtocolLabelLen = sizeof(kProtocolLabel) - 1;
146+
147+
CBB cbb, child;
148+
uint8_t *hkdf_label = NULL;
149+
size_t hkdf_label_len = 0;
150+
int ret = 0;
151+
152+
CBB_zero(&cbb);
153+
// RFC 8446 Section 7.1 constrains the HkdfLabel.length field to a uint16 and
154+
// the HkdfLabel.label field to |opaque label<7..255>|. Since |kProtocolLabel|
155+
// ("tls13 ") is 6 bytes, the caller-provided |label| must be at least 1 byte
156+
// for the combined label to satisfy the RFC lower bound. The upper bound
157+
// (combined label <= 255) and the |hash_len| bound are enforced implicitly by
158+
// the CBB length-prefixed calls below.
159+
if (out_len > UINT16_MAX || label_len == 0) {
160+
goto end;
161+
}
162+
163+
// Construct the HkdfLabel structure per RFC 8446 Section 7.1:
164+
// struct {
165+
// uint16 length = Length;
166+
// opaque label<7..255> = "tls13 " + Label;
167+
// opaque context<0..255> = Context;
168+
// };
169+
// The CBB_add_u16 / CBB_add_u8_length_prefixed calls implicitly enforce the
170+
// RFC-mandated length upper bounds and will fail if |out_len| does not fit
171+
// in a |uint16_t|, if the "tls13 "-prefixed label exceeds 255 bytes, or if
172+
// |hash| exceeds 255 bytes.
173+
if (!CBB_init(&cbb, 2 + 1 + kProtocolLabelLen + label_len + 1 + hash_len) ||
174+
!CBB_add_u16(&cbb, out_len) ||
175+
!CBB_add_u8_length_prefixed(&cbb, &child) ||
176+
!CBB_add_bytes(&child, kProtocolLabel, kProtocolLabelLen) ||
177+
!CBB_add_bytes(&child, label, label_len) ||
178+
!CBB_add_u8_length_prefixed(&cbb, &child) ||
179+
!CBB_add_bytes(&child, hash, hash_len) ||
180+
!CBB_finish(&cbb, &hkdf_label, &hkdf_label_len)) {
181+
goto end;
182+
}
183+
184+
ret = HKDF_expand(out, out_len, digest, secret, secret_len, hkdf_label,
185+
hkdf_label_len);
186+
187+
end:
188+
CBB_cleanup(&cbb);
189+
OPENSSL_free(hkdf_label);
190+
FIPS_service_indicator_unlock_state();
191+
if (ret) {
192+
TLS13_KDF_verify_service_indicator(digest);
193+
}
194+
return ret;
195+
}

include/openssl/kdf.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ OPENSSL_EXPORT int CRYPTO_tls1_prf(const EVP_MD *digest,
2525
const uint8_t *seed1, size_t seed1_len,
2626
const uint8_t *seed2, size_t seed2_len);
2727

28+
// CRYPTO_tls13_hkdf_expand_label computes the TLS 1.3 HKDF-Expand-Label
29+
// function as defined in RFC 8446, Section 7.1. It derives |out_len| bytes of
30+
// output into |out| using |digest| as the underlying HMAC hash, |secret| as
31+
// the HKDF pseudorandom key, |label| (which is prefixed with "tls13 "
32+
// internally) and |hash| (the context/transcript hash). |out_len| must fit in
33+
// a |uint16_t|, |label_len| must be between 1 and 249 inclusive so that the
34+
// "tls13 "-prefixed label satisfies the RFC 8446 |opaque label<7..255>|
35+
// bounds, and |hash_len| must be at most 255. Under FIPS, the operation is
36+
// approved when |digest| is SHA2-256 or SHA2-384. It returns one on success
37+
// and zero on error.
38+
OPENSSL_EXPORT int CRYPTO_tls13_hkdf_expand_label(
39+
uint8_t *out, size_t out_len, const EVP_MD *digest,
40+
const uint8_t *secret, size_t secret_len,
41+
const uint8_t *label, size_t label_len,
42+
const uint8_t *hash, size_t hash_len);
43+
2844
// SSKDF_digest computes the One-step key derivation using the
2945
// provided digest algorithm as the backing PRF. This algorithm
3046
// may be referred to as "Single-Step KDF" or "NIST Concatenation KDF" by other

ssl/tls13_enc.cc

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <openssl/digest.h>
1515
#include <openssl/hkdf.h>
1616
#include <openssl/hmac.h>
17+
#include <openssl/kdf.h>
1718
#include <openssl/mem.h>
1819

1920
#include "../crypto/internal.h"
@@ -84,27 +85,10 @@ static bool hkdf_expand_label(Span<uint8_t> out, const EVP_MD *digest,
8485
Span<const uint8_t> secret,
8586
Span<const char> label,
8687
Span<const uint8_t> hash) {
87-
Span<const char> protocol_label = label_to_span("tls13 ");
88-
ScopedCBB cbb;
89-
CBB child;
90-
Array<uint8_t> hkdf_label;
91-
if (!CBB_init(cbb.get(), 2 + 1 + protocol_label.size() + label.size() + 1 +
92-
hash.size()) ||
93-
!CBB_add_u16(cbb.get(), out.size()) ||
94-
!CBB_add_u8_length_prefixed(cbb.get(), &child) ||
95-
!CBB_add_bytes(&child,
96-
reinterpret_cast<const uint8_t *>(protocol_label.data()),
97-
protocol_label.size()) ||
98-
!CBB_add_bytes(&child, reinterpret_cast<const uint8_t *>(label.data()),
99-
label.size()) ||
100-
!CBB_add_u8_length_prefixed(cbb.get(), &child) ||
101-
!CBB_add_bytes(&child, hash.data(), hash.size()) ||
102-
!CBBFinishArray(cbb.get(), &hkdf_label)) {
103-
return false;
104-
}
105-
106-
return HKDF_expand(out.data(), out.size(), digest, secret.data(),
107-
secret.size(), hkdf_label.data(), hkdf_label.size());
88+
return CRYPTO_tls13_hkdf_expand_label(
89+
out.data(), out.size(), digest, secret.data(), secret.size(),
90+
reinterpret_cast<const uint8_t *>(label.data()), label.size(),
91+
hash.data(), hash.size()) == 1;
10892
}
10993

11094
static const char kTLS13LabelDerived[] = "derived";

util/fipstools/acvp/modulewrapper/modulewrapper.cc

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,39 +3278,22 @@ static bool HKDFExpandLabel(const Span<const uint8_t> args[],
32783278
}
32793279
memcpy(&out_len, out_len_bytes.data(), sizeof(out_len));
32803280

3281-
// Construct the HkdfLabel structure per RFC 8446 Section 7.1:
3282-
// struct {
3283-
// uint16 length = Length;
3284-
// opaque label<7..255> = "tls13 " + Label;
3285-
// opaque context<0..255> = Context;
3286-
// };
3287-
static const char kProtocolLabel[] = "tls13 ";
3288-
const size_t protocol_label_len = sizeof(kProtocolLabel) - 1;
3289-
const size_t full_label_len = protocol_label_len + label.size();
3290-
3291-
// RFC 8446 constrains |length| to uint16, |label| to 7..255 bytes, and
3292-
// |context| to 0..255 bytes. Reject anything that would not round-trip
3293-
// through the 1- and 2-byte length fields below.
3294-
if (out_len > 0xffff || full_label_len > 255 ||
3295-
transcript_hash.size() > 255) {
3281+
// RFC 8446 Section 7.1 caps HkdfLabel.length at a uint16_t; reject oversized
3282+
// requests up front so we don't attempt a multi-GiB |std::vector| allocation
3283+
// only to have CRYPTO_tls13_hkdf_expand_label reject the size internally.
3284+
if (out_len > 0xFFFF) {
32963285
return false;
32973286
}
32983287

3299-
std::vector<uint8_t> info;
3300-
info.push_back(static_cast<uint8_t>(out_len >> 8));
3301-
info.push_back(static_cast<uint8_t>(out_len));
3302-
info.push_back(static_cast<uint8_t>(full_label_len));
3303-
info.insert(info.end(), kProtocolLabel, kProtocolLabel + protocol_label_len);
3304-
info.insert(info.end(), label.data(), label.data() + label.size());
3305-
info.push_back(static_cast<uint8_t>(transcript_hash.size()));
3306-
info.insert(info.end(), transcript_hash.data(),
3307-
transcript_hash.data() + transcript_hash.size());
3308-
33093288
std::vector<uint8_t> out(out_len);
3310-
// Qualify with :: to unambiguously select the C library function and avoid
3311-
// colliding with the same-namespace HKDF_expand template defined below.
3312-
if (!::HKDF_expand(out.data(), out_len, md, secret.data(), secret.size(),
3313-
info.data(), info.size())) {
3289+
// Delegate the "tls13 " prefix + HkdfLabel construction (RFC 8446 §7.1)
3290+
// and the RFC-mandated length-bound enforcement to the FIPS-module-internal
3291+
// CRYPTO_tls13_hkdf_expand_label so ACVP exercises the same code path used
3292+
// by ssl/tls13_enc.cc.
3293+
if (!CRYPTO_tls13_hkdf_expand_label(out.data(), out_len, md, secret.data(),
3294+
secret.size(), label.data(),
3295+
label.size(), transcript_hash.data(),
3296+
transcript_hash.size())) {
33143297
return false;
33153298
}
33163299

util/fipstools/break-kat.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ var (
2727
"SHA3-256": "d83c721ee51b060c5a41438a8221e040",
2828
"HKDF-SHA-256": "ca5e6410e7a52332fe0ab3601212a7d3dbdf55a162af42a5daf38b94f24523477e880dd711508684cc21",
2929
"TLS-KDF": "abc3657b094c7628a0b282996fe75a75f4984fd94d4ecc2fcf53a2c469a3f731",
30+
"TLS13-KDF": "024a0d80f357f2499a1244dac26dab66fc13ed85fca71dace146211119525874",
3031
"PBKDF2": "4157532d4c434649505370617373776f7264",
3132
"SSKDF": "39a1e2b3899e87efecf6271282d8f8008f252686dd35bfc39a0f71478da48c691565cee431254dd50cab7462c6cf199be9bf5c",
3233
"KBKDF": "dd1d91b7d90b2bd3138533ce92b272fbf8a369316aefe242e659cc0ae238afe0",

util/fipstools/test_fips.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,20 @@ int main(int argc, char **argv) {
461461
printf(" got ");
462462
hexdump(tls_output, sizeof(tls_output));
463463

464+
/* TLS 1.3 KDF (HKDF-Expand-Label) */
465+
printf("About to run TLS 1.3 KDF\n");
466+
uint8_t tls13_output[32];
467+
static const uint8_t kTLS13Label[] = "c e traffic";
468+
if (!CRYPTO_tls13_hkdf_expand_label(
469+
tls13_output, sizeof(tls13_output), EVP_sha256(), kAESKey,
470+
sizeof(kAESKey), kTLS13Label, sizeof(kTLS13Label) - 1,
471+
kPlaintextSHA256, sizeof(kPlaintextSHA256))) {
472+
fprintf(stderr, "TLS 1.3 KDF failed.\n");
473+
goto err;
474+
}
475+
printf(" got ");
476+
hexdump(tls13_output, sizeof(tls13_output));
477+
464478
/* FFDH */
465479
printf("About to compute FFDH key-agreement:\n");
466480
DH *dh = DH_get_rfc7919_2048();

0 commit comments

Comments
 (0)