Skip to content

Commit 6175391

Browse files
Add support for the TLS13-KDF algorithm (golang-fips#272)
* Add support for the TLS13-KDF algorithm * Change to opt-in * Code review suggestions - Don't panic if there's no support - Don't export the parse function - Move unit tests to hkdf_interal_test.go which use the same package - Rework the parse function to return a boolean at the end * Purge parsing Reduce to only an Expand function based on TLS13-KDF Remove associated cruft Add new unit tests * Update hkdf.go Co-authored-by: Quim Muntal <quimmuntal@gmail.com> * Update hkdf.go Co-authored-by: Quim Muntal <quimmuntal@gmail.com> * Be explicit, not clever. * Do not ignore testdata/ * panic for unknown openssl versions --------- Co-authored-by: Quim Muntal <quimmuntal@gmail.com>
1 parent f45e536 commit 6175391

File tree

2 files changed

+294
-0
lines changed

2 files changed

+294
-0
lines changed

hkdf.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ func SupportsHKDF() bool {
2626
}
2727
}
2828

29+
// SupprtsTLS13KDF reports whether the current OpenSSL version supports TLS13-KDF.
30+
func SupportsTLS13KDF() bool {
31+
switch vMajor {
32+
case 1:
33+
return false
34+
case 3:
35+
// TLS13-KDF is available in OpenSSL 3.0.0 and later.
36+
_, err := fetchTLS13_KDF()
37+
return err == nil
38+
default:
39+
panic(errUnsupportedVersion())
40+
}
41+
}
42+
2943
func newHKDFCtx1(md C.GO_EVP_MD_PTR, mode C.int, secret, salt, pseudorandomKey, info []byte) (ctx C.GO_EVP_PKEY_CTX_PTR, err error) {
3044
checkMajorVersion(1)
3145

@@ -188,6 +202,31 @@ func ExpandHKDFOneShot(h func() hash.Hash, pseudorandomKey, info []byte, keyLeng
188202
return out, nil
189203
}
190204

205+
// ExpandTLS13KDF derives a key from the given hash, key, label and context. It will use
206+
// "TLS13-KDF" algorithm to do so.
207+
func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) {
208+
if !SupportsTLS13KDF() {
209+
return nil, errUnsupportedVersion()
210+
}
211+
212+
md, err := hashFuncToMD(h)
213+
if err != nil {
214+
return nil, err
215+
}
216+
217+
out := make([]byte, keyLength)
218+
219+
ctx, err := newTLS13KDFExpandCtx3(md, label, context, pseudorandomKey)
220+
if err != nil {
221+
return nil, err
222+
}
223+
defer ossl.EVP_KDF_CTX_free(ctx)
224+
if _, err := ossl.EVP_KDF_derive(ctx, base(out), keyLength, nil); err != nil {
225+
return nil, err
226+
}
227+
return out, nil
228+
}
229+
191230
func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) {
192231
if !SupportsHKDF() {
193232
return nil, errUnsupportedVersion()
@@ -233,6 +272,63 @@ func (c *hkdf3) finalize() {
233272
}
234273
}
235274

275+
// fetchTLS13_KDF fetches the TLS13-KDF algorithm.
276+
// It is safe to call this function concurrently.
277+
// The returned EVP_KDF_PTR shouldn't be freed.
278+
var fetchTLS13_KDF = sync.OnceValues(func() (ossl.EVP_KDF_PTR, error) {
279+
checkMajorVersion(3)
280+
281+
kdf, err := ossl.EVP_KDF_fetch(nil, _OSSL_KDF_NAME_TLS13_KDF.ptr(), nil)
282+
if err != nil {
283+
return nil, err
284+
}
285+
return kdf, nil
286+
})
287+
288+
// newTLS13KDFExpandCtx3 fetches the "TLS13-KDF" for TLS 1.3 handshakes.
289+
func newTLS13KDFExpandCtx3(md ossl.EVP_MD_PTR, label, context, pseudorandomKey []byte) (_ ossl.EVP_KDF_CTX_PTR, err error) {
290+
checkMajorVersion(3)
291+
292+
kdf, err := fetchTLS13_KDF()
293+
if err != nil {
294+
return nil, err
295+
}
296+
297+
ctx, err := ossl.EVP_KDF_CTX_new(kdf)
298+
if err != nil {
299+
return nil, err
300+
}
301+
defer func() {
302+
if err != nil {
303+
ossl.EVP_KDF_CTX_free(ctx)
304+
}
305+
}()
306+
307+
bld, err := newParamBuilder()
308+
if err != nil {
309+
return ctx, err
310+
}
311+
bld.addUTF8String(_OSSL_KDF_PARAM_DIGEST, ossl.EVP_MD_get0_name(md), 0)
312+
bld.addInt32(_OSSL_KDF_PARAM_MODE, int32(ossl.EVP_KDF_HKDF_MODE_EXPAND_ONLY))
313+
bld.addOctetString(_OSSL_KDF_PARAM_PREFIX, []byte("tls13 "))
314+
bld.addOctetString(_OSSL_KDF_PARAM_LABEL, label)
315+
bld.addOctetString(_OSSL_KDF_PARAM_DATA, context)
316+
if len(pseudorandomKey) > 0 {
317+
bld.addOctetString(_OSSL_KDF_PARAM_KEY, pseudorandomKey)
318+
}
319+
320+
params, err := bld.build()
321+
if err != nil {
322+
return ctx, err
323+
}
324+
defer ossl.OSSL_PARAM_free(params)
325+
326+
if _, err := ossl.EVP_KDF_CTX_set_params(ctx, params); err != nil {
327+
return ctx, err
328+
}
329+
return ctx, nil
330+
}
331+
236332
// fetchHKDF3 fetches the HKDF algorithm.
237333
// It is safe to call this function concurrently.
238334
// The returned EVP_KDF_PTR shouldn't be freed.

hkdf_test.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,3 +448,201 @@ func TestExpandHKDFOneShotLimit(t *testing.T) {
448448
t.Errorf("expected error for key expansion overflow")
449449
}
450450
}
451+
452+
type tls13kdfTest struct {
453+
hash func() hash.Hash
454+
prk []byte
455+
label []byte
456+
ctx []byte
457+
out []byte
458+
}
459+
460+
var tls13kdfTests = []tls13kdfTest{
461+
{
462+
openssl.NewSHA256,
463+
[]byte{
464+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
465+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
466+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
467+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
468+
},
469+
[]byte("res binder") ,
470+
[]byte{},
471+
[]byte{
472+
0x10, 0x6d, 0x4e, 0xea, 0x65, 0x19, 0x16, 0xc7,
473+
0xff, 0x7d, 0xd1, 0x2f, 0x24, 0x04, 0x6a, 0x46,
474+
0x60, 0x11, 0x40, 0x8b, 0xed, 0x37, 0x06, 0x49,
475+
0x73, 0x84, 0x05, 0x79, 0x94, 0x15, 0x00, 0x3b,
476+
0xce, 0x9d, 0xa1, 0x04, 0x78, 0xae, 0xd3, 0x4f,
477+
0xe2, 0x0c,
478+
},
479+
},
480+
{
481+
openssl.NewSHA256,
482+
[]byte{
483+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
484+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
485+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
486+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
487+
},
488+
[]byte("c e traffic") ,
489+
[]byte{},
490+
[]byte{
491+
0x7e, 0xb6, 0x59, 0x96, 0x14, 0xf4, 0x1a, 0x27,
492+
0x09, 0x8d, 0x7a, 0x26, 0xdf, 0x32, 0x6a, 0x0d,
493+
0xf8, 0xd2, 0xad, 0xd5, 0x2a, 0x46, 0xa5, 0x37,
494+
0xa7, 0x25, 0x16, 0x01, 0xb8, 0x8e, 0x30, 0x61,
495+
0x45, 0x40, 0x83, 0x76, 0xbf, 0xcc, 0xb8, 0xae,
496+
0xba, 0x0f,
497+
},
498+
},
499+
{
500+
openssl.NewSHA256,
501+
[]byte{
502+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
503+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
504+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
505+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
506+
},
507+
[]byte("c hs traffic") ,
508+
[]byte{},
509+
[]byte{
510+
0x1f, 0xf4, 0xeb, 0xec, 0xca, 0x5b, 0x6f, 0x1c,
511+
0x98, 0x7f, 0xd0, 0xc1, 0x74, 0x4e, 0x4f, 0x1f,
512+
0x46, 0xf5, 0x27, 0x06, 0xa8, 0x30, 0xb3, 0x72,
513+
0x06, 0xe2, 0x7f, 0x23, 0xdb, 0x8e, 0xc0, 0xc2,
514+
0xea, 0x26, 0xdf, 0xf4, 0x8d, 0x73, 0xc7, 0x01,
515+
0x20, 0x20,
516+
},
517+
},
518+
{
519+
openssl.NewSHA256,
520+
[]byte{
521+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
522+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
523+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
524+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
525+
},
526+
[]byte("s hs traffic") ,
527+
[]byte{},
528+
[]byte{
529+
0xe9, 0xe4, 0x51, 0x4b, 0xe9, 0x0d, 0xb0, 0x44,
530+
0x07, 0x42, 0x6c, 0x52, 0x77, 0xdf, 0x8c, 0x7f,
531+
0x19, 0x38, 0xcb, 0x72, 0x76, 0x97, 0x0a, 0x66,
532+
0x2b, 0x58, 0x7e, 0xee, 0x8a, 0xdd, 0x0d, 0xe2,
533+
0x15, 0xe8, 0x60, 0x37, 0x3d, 0x16, 0x9d, 0xdc,
534+
0x74, 0xb7,
535+
},
536+
},
537+
{
538+
openssl.NewSHA256,
539+
[]byte{
540+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
541+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
542+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
543+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
544+
},
545+
[]byte("c ap traffic") ,
546+
[]byte{},
547+
[]byte{
548+
0x51, 0xee, 0xb6, 0x24, 0x50, 0x5f, 0x88, 0xdb,
549+
0x61, 0x9c, 0x10, 0x25, 0x6f, 0xa5, 0xa0, 0xbc,
550+
0x0e, 0x5f, 0x81, 0xde, 0xf6, 0x59, 0x2d, 0x99,
551+
0xc9, 0x73, 0x1a, 0x3e, 0x4e, 0x11, 0x93, 0x0c,
552+
0xae, 0x51, 0xa1, 0xf8, 0x42, 0x42, 0x45, 0xbe,
553+
0x52, 0x50,
554+
},
555+
},
556+
{
557+
openssl.NewSHA256,
558+
[]byte{
559+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
560+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
561+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
562+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
563+
},
564+
[]byte("s ap traffic") ,
565+
[]byte{},
566+
[]byte{
567+
0x08, 0x8c, 0xff, 0x31, 0xa0, 0xa1, 0x64, 0xca,
568+
0x88, 0x1a, 0xc1, 0xde, 0xef, 0xa2, 0x38, 0xed,
569+
0x43, 0x02, 0x68, 0x7f, 0xe9, 0x59, 0xc9, 0x81,
570+
0xc2, 0xc1, 0x42, 0xfc, 0xa5, 0xad, 0xee, 0xc9,
571+
0xbb, 0xfa, 0x6e, 0xb9, 0x9a, 0x4d, 0xd3, 0x3d,
572+
0x11, 0x52,
573+
},
574+
},
575+
{
576+
openssl.NewSHA256,
577+
[]byte{
578+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
579+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
580+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
581+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
582+
},
583+
[]byte("e exp master") ,
584+
[]byte{},
585+
[]byte{
586+
0x32, 0x59, 0x33, 0x2c, 0xf3, 0xb0, 0xc2, 0x0d,
587+
0x96, 0xe0, 0x38, 0x01, 0x8c, 0xb1, 0xd1, 0xeb,
588+
0x9d, 0xbd, 0x64, 0xba, 0xb2, 0x54, 0x3e, 0xe5,
589+
0xca, 0x33, 0xe8, 0x17, 0xc3, 0x62, 0x7e, 0x62,
590+
0x45, 0x9f, 0x96, 0xdd, 0x81, 0x51, 0x55, 0xef,
591+
0x1b, 0xb6,
592+
},
593+
},
594+
{
595+
openssl.NewSHA256,
596+
[]byte{
597+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
598+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
599+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
600+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
601+
},
602+
[]byte("exp master") ,
603+
[]byte{},
604+
[]byte{
605+
0x50, 0x6a, 0x2e, 0xe3, 0x95, 0x77, 0x6c, 0xfb,
606+
0x77, 0x8c, 0x5a, 0xe3, 0x22, 0x32, 0x35, 0xd2,
607+
0x73, 0x81, 0x50, 0x85, 0x0e, 0x51, 0x59, 0x01,
608+
0xa8, 0x99, 0x4f, 0xea, 0xfa, 0x6d, 0x22, 0x83,
609+
0xf2, 0x5d, 0x9f, 0xba, 0xc5, 0xc9, 0xb6, 0x4b,
610+
0xdc, 0x8d,
611+
},
612+
},
613+
{
614+
openssl.NewSHA256,
615+
[]byte{
616+
0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
617+
0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
618+
0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
619+
0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
620+
},
621+
[]byte("res master") ,
622+
[]byte{},
623+
[]byte{
624+
0xc1, 0x37, 0xab, 0x0f, 0x7b, 0x58, 0xb7, 0xe9,
625+
0xdd, 0xf9, 0xff, 0xb4, 0x0d, 0x1e, 0xaa, 0xa8,
626+
0x67, 0x6c, 0x15, 0xdf, 0xdb, 0xff, 0x7c, 0x0b,
627+
0xc3, 0xcb, 0xd9, 0x21, 0x3e, 0x95, 0xcd, 0xbb,
628+
0xe1, 0x70, 0xdd, 0x37, 0x8a, 0xae, 0x45, 0x89,
629+
0xb0, 0x68,
630+
},
631+
},
632+
}
633+
634+
func TestExpandTLS13KDF(t *testing.T) {
635+
if !openssl.SupportsTLS13KDF() {
636+
t.Skip("TLS13-KDF is not supported")
637+
}
638+
for i, tt := range tls13kdfTests {
639+
out, err := openssl.ExpandTLS13KDF(tt.hash, tt.prk, tt.label, tt.ctx, len(tt.out))
640+
if err != nil {
641+
t.Errorf("test %d (label: %s): error expanding TLS13-KDF: %v.", i, tt.label, err)
642+
continue
643+
}
644+
if !bytes.Equal(out, tt.out) {
645+
t.Errorf("test %d (label: %s): incorrect output from ExpandTLS13KDF: have %v, need %v.", i, tt.label, out, tt.out)
646+
}
647+
}
648+
}

0 commit comments

Comments
 (0)