Skip to content

Commit 6020143

Browse files
nicholasberlinqmuntalsamiponkanenssh
authored
Backport TLS13-KDF/Extract fixes to 1.24 support branch (#276)
* 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 <[email protected]> * Update hkdf.go Co-authored-by: Quim Muntal <[email protected]> * Be explicit, not clever. * Do not ignore testdata/ * panic for unknown openssl versions --------- Co-authored-by: Quim Muntal <[email protected]> * Fix up cherry-pick * hkdf: Replace nil salt with a slice of a preallocated all zeros buffer (#260) * hkdf: Replace nil salt with a slice of a preallocated all zeros buffer. This fixes HKDF when using KeyPair FIPS Provider for OpenSSL 3 * hkdf: fixed PR review comments * hkdf: second round of PR comment fixes * hkdf: third round of PR comment fixes --------- Co-authored-by: Quim Muntal <[email protected]> Co-authored-by: samiponkanenssh <[email protected]>
1 parent f45e536 commit 6020143

File tree

3 files changed

+321
-0
lines changed

3 files changed

+321
-0
lines changed

hkdf.go

Lines changed: 120 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

@@ -109,6 +123,14 @@ func (c *hkdf1) Read(p []byte) (int, error) {
109123
return n, nil
110124
}
111125

126+
// hkdfAllZerosSalt is a preallocated buffer of zeros used in ExtractHKDF().
127+
// The size should be kept as large as the output length of any hash algorithm
128+
// used with HKDF.
129+
var hkdfAllZerosSalt [64]byte
130+
131+
// ExtractHDKF implements the HDKF extract step.
132+
// If salt is nil, then this function replaces it internally with a buffer of
133+
// zeros whose length equals the output length of the specified hash algorithm.
112134
func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) {
113135
if !SupportsHKDF() {
114136
return nil, errUnsupportedVersion()
@@ -119,6 +141,20 @@ func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) {
119141
return nil, err
120142
}
121143

144+
// If calling code specifies nil salt, replace it with a buffer of hashLen
145+
// zeros, as specified in RFC 5896 and as OpenSSL EVP_KDF-HKDF documentation
146+
// instructs. Take a slice of a preallocated buffer to avoid allocating new
147+
// buffer per call, but fall back to allocating a buffer if preallocated
148+
// buffer is not large enough.
149+
if salt == nil {
150+
hlen := h().Size()
151+
if hlen > len(hkdfAllZerosSalt) {
152+
salt = make([]byte, hlen)
153+
} else {
154+
salt = hkdfAllZerosSalt[:hlen]
155+
}
156+
}
157+
122158
switch vMajor {
123159
case 1:
124160
ctx, err := newHKDFCtx1(md, C.GO_EVP_KDF_HKDF_MODE_EXTRACT_ONLY, secret, salt, nil, nil)
@@ -188,6 +224,31 @@ func ExpandHKDFOneShot(h func() hash.Hash, pseudorandomKey, info []byte, keyLeng
188224
return out, nil
189225
}
190226

227+
// ExpandTLS13KDF derives a key from the given hash, key, label and context. It will use
228+
// "TLS13-KDF" algorithm to do so.
229+
func ExpandTLS13KDF(h func() hash.Hash, pseudorandomKey, label, context []byte, keyLength int) ([]byte, error) {
230+
if !SupportsTLS13KDF() {
231+
return nil, errUnsupportedVersion()
232+
}
233+
234+
md, err := hashFuncToMD(h)
235+
if err != nil {
236+
return nil, err
237+
}
238+
239+
out := make([]byte, keyLength)
240+
241+
ctx, err := newTLS13KDFExpandCtx3(md, label, context, pseudorandomKey)
242+
if err != nil {
243+
return nil, err
244+
}
245+
defer C.go_openssl_EVP_KDF_CTX_free(ctx)
246+
if _, err := C.go_openssl_EVP_KDF_derive(ctx, base(out), C.size_t(keyLength), nil); err != nil {
247+
return nil, err
248+
}
249+
return out, nil
250+
}
251+
191252
func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) {
192253
if !SupportsHKDF() {
193254
return nil, errUnsupportedVersion()
@@ -233,6 +294,65 @@ func (c *hkdf3) finalize() {
233294
}
234295
}
235296

297+
// fetchTLS13_KDF fetches the TLS13-KDF algorithm.
298+
// It is safe to call this function concurrently.
299+
// The returned EVP_KDF_PTR shouldn't be freed.
300+
var fetchTLS13_KDF = sync.OnceValues(func() (C.GO_EVP_KDF_PTR, error) {
301+
checkMajorVersion(3)
302+
303+
name := C.CString("TLS13-KDF")
304+
kdf := C.go_openssl_EVP_KDF_fetch(nil, name, nil)
305+
C.free(unsafe.Pointer(name))
306+
if kdf == nil {
307+
return nil, newOpenSSLError("EVP_KDF_fetch")
308+
}
309+
return kdf, nil
310+
})
311+
312+
// newTLS13KDFExpandCtx3 fetches the "TLS13-KDF" for TLS 1.3 handshakes.
313+
func newTLS13KDFExpandCtx3(md C.GO_EVP_MD_PTR, label, context, pseudorandomKey []byte) (_ C.GO_EVP_KDF_CTX_PTR, err error) {
314+
checkMajorVersion(3)
315+
316+
kdf, err := fetchTLS13_KDF()
317+
if err != nil {
318+
return nil, err
319+
}
320+
321+
ctx, err := C.go_openssl_EVP_KDF_CTX_new(kdf)
322+
if err != nil {
323+
return nil, err
324+
}
325+
defer func() {
326+
if err != nil {
327+
C.go_openssl_EVP_KDF_CTX_free(ctx)
328+
}
329+
}()
330+
331+
bld, err := newParamBuilder()
332+
if err != nil {
333+
return ctx, err
334+
}
335+
bld.addUTF8String(_OSSL_KDF_PARAM_DIGEST, C.go_openssl_EVP_MD_get0_name(md), 0)
336+
bld.addInt32(_OSSL_KDF_PARAM_MODE, int32(C.GO_EVP_KDF_HKDF_MODE_EXPAND_ONLY))
337+
bld.addOctetString(_OSSL_KDF_PARAM_PREFIX, []byte("tls13 "))
338+
bld.addOctetString(_OSSL_KDF_PARAM_LABEL, label)
339+
bld.addOctetString(_OSSL_KDF_PARAM_DATA, context)
340+
if len(pseudorandomKey) > 0 {
341+
bld.addOctetString(_OSSL_KDF_PARAM_KEY, pseudorandomKey)
342+
}
343+
344+
params, err := bld.build()
345+
if err != nil {
346+
return ctx, err
347+
}
348+
defer C.go_openssl_OSSL_PARAM_free(params)
349+
350+
if _, err := C.go_openssl_EVP_KDF_CTX_set_params(ctx, params); err != nil {
351+
return ctx, err
352+
}
353+
return ctx, nil
354+
}
355+
236356
// fetchHKDF3 fetches the HKDF algorithm.
237357
// It is safe to call this function concurrently.
238358
// 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+
}

params.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ var (
1818
_OSSL_KDF_PARAM_INFO = C.CString("info")
1919
_OSSL_KDF_PARAM_SALT = C.CString("salt")
2020
_OSSL_KDF_PARAM_MODE = C.CString("mode")
21+
_OSSL_KDF_PARAM_PREFIX = C.CString("prefix")
22+
_OSSL_KDF_PARAM_LABEL = C.CString("label")
23+
_OSSL_KDF_PARAM_DATA = C.CString("data")
2124

2225
// PKEY parameters
2326
_OSSL_PKEY_PARAM_PUB_KEY = C.CString("pub")

0 commit comments

Comments
 (0)