Skip to content

Commit b488d85

Browse files
Add support for the TLS13-KDF algorithm (#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 d1d9967 commit b488d85

File tree

3 files changed

+304
-4
lines changed

3 files changed

+304
-4
lines changed

const.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ const ( //checkheader:ignore
4848
_DigestNameSHA2_256 cString = "SHA2-256\x00"
4949

5050
// KDF names
51-
_OSSL_KDF_NAME_HKDF cString = "HKDF\x00"
52-
_OSSL_KDF_NAME_PBKDF2 cString = "PBKDF2\x00"
53-
_OSSL_KDF_NAME_TLS1_PRF cString = "TLS1-PRF\x00"
54-
_OSSL_MAC_NAME_HMAC cString = "HMAC\x00"
51+
_OSSL_KDF_NAME_HKDF cString = "HKDF\x00"
52+
_OSSL_KDF_NAME_PBKDF2 cString = "PBKDF2\x00"
53+
_OSSL_KDF_NAME_TLS1_PRF cString = "TLS1-PRF\x00"
54+
_OSSL_KDF_NAME_TLS13_KDF cString = "TLS13-KDF\x00"
55+
_OSSL_MAC_NAME_HMAC cString = "HMAC\x00"
5556

5657
// KDF parameters
5758
_OSSL_KDF_PARAM_DIGEST cString = "digest\x00"
@@ -62,6 +63,11 @@ const ( //checkheader:ignore
6263
_OSSL_KDF_PARAM_SALT cString = "salt\x00"
6364
_OSSL_KDF_PARAM_MODE cString = "mode\x00"
6465

66+
// TLS3-KDF parameters
67+
_OSSL_KDF_PARAM_PREFIX cString = "prefix\x00"
68+
_OSSL_KDF_PARAM_LABEL cString = "label\x00"
69+
_OSSL_KDF_PARAM_DATA cString = "data\x00"
70+
6571
// PKEY parameters
6672
_OSSL_PKEY_PARAM_PUB_KEY cString = "pub\x00"
6773
_OSSL_PKEY_PARAM_PRIV_KEY cString = "priv\x00"

hkdf.go

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

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

@@ -214,6 +228,31 @@ func ExpandHKDFOneShot(h func() hash.Hash, pseudorandomKey, info []byte, keyLeng
214228
return out, nil
215229
}
216230

231+
// ExpandTLS13KDF derives a key from the given hash, key, label and context. It will use
232+
// "TLS13-KDF" algorithm to do so.
233+
func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) {
234+
if !SupportsTLS13KDF() {
235+
return nil, errUnsupportedVersion()
236+
}
237+
238+
md, err := hashFuncToMD(h)
239+
if err != nil {
240+
return nil, err
241+
}
242+
243+
out := make([]byte, keyLength)
244+
245+
ctx, err := newTLS13KDFExpandCtx3(md, label, context, pseudorandomKey)
246+
if err != nil {
247+
return nil, err
248+
}
249+
defer ossl.EVP_KDF_CTX_free(ctx)
250+
if _, err := ossl.EVP_KDF_derive(ctx, base(out), keyLength, nil); err != nil {
251+
return nil, err
252+
}
253+
return out, nil
254+
}
255+
217256
func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) {
218257
if !SupportsHKDF() {
219258
return nil, errUnsupportedVersion()
@@ -261,6 +300,63 @@ func (c *hkdf3) finalize() {
261300
}
262301
}
263302

303+
// fetchTLS13_KDF fetches the TLS13-KDF algorithm.
304+
// It is safe to call this function concurrently.
305+
// The returned EVP_KDF_PTR shouldn't be freed.
306+
var fetchTLS13_KDF = sync.OnceValues(func() (ossl.EVP_KDF_PTR, error) {
307+
checkMajorVersion(3)
308+
309+
kdf, err := ossl.EVP_KDF_fetch(nil, _OSSL_KDF_NAME_TLS13_KDF.ptr(), nil)
310+
if err != nil {
311+
return nil, err
312+
}
313+
return kdf, nil
314+
})
315+
316+
// newTLS13KDFExpandCtx3 fetches the "TLS13-KDF" for TLS 1.3 handshakes.
317+
func newTLS13KDFExpandCtx3(md ossl.EVP_MD_PTR, label, context, pseudorandomKey []byte) (_ ossl.EVP_KDF_CTX_PTR, err error) {
318+
checkMajorVersion(3)
319+
320+
kdf, err := fetchTLS13_KDF()
321+
if err != nil {
322+
return nil, err
323+
}
324+
325+
ctx, err := ossl.EVP_KDF_CTX_new(kdf)
326+
if err != nil {
327+
return nil, err
328+
}
329+
defer func() {
330+
if err != nil {
331+
ossl.EVP_KDF_CTX_free(ctx)
332+
}
333+
}()
334+
335+
bld, err := newParamBuilder()
336+
if err != nil {
337+
return ctx, err
338+
}
339+
bld.addUTF8String(_OSSL_KDF_PARAM_DIGEST, ossl.EVP_MD_get0_name(md), 0)
340+
bld.addInt32(_OSSL_KDF_PARAM_MODE, int32(ossl.EVP_KDF_HKDF_MODE_EXPAND_ONLY))
341+
bld.addOctetString(_OSSL_KDF_PARAM_PREFIX, []byte("tls13 "))
342+
bld.addOctetString(_OSSL_KDF_PARAM_LABEL, label)
343+
bld.addOctetString(_OSSL_KDF_PARAM_DATA, context)
344+
if len(pseudorandomKey) > 0 {
345+
bld.addOctetString(_OSSL_KDF_PARAM_KEY, pseudorandomKey)
346+
}
347+
348+
params, err := bld.build()
349+
if err != nil {
350+
return ctx, err
351+
}
352+
defer ossl.OSSL_PARAM_free(params)
353+
354+
if _, err := ossl.EVP_KDF_CTX_set_params(ctx, params); err != nil {
355+
return ctx, err
356+
}
357+
return ctx, nil
358+
}
359+
264360
// fetchHKDF3 fetches the HKDF algorithm.
265361
// It is safe to call this function concurrently.
266362
// 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)