Skip to content

Commit

Permalink
handshake: validate HKDF-Expand-Label against crypto/tls implementati…
Browse files Browse the repository at this point in the history
…on (#4311)

* handshake: validate HKDF-Expand-Label against crypto/tls implementation

* handshake: add a benchmark for HKDF-Expand-Label
  • Loading branch information
marten-seemann committed Feb 5, 2024
1 parent dc49f56 commit c22a3c8
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 20 deletions.
2 changes: 1 addition & 1 deletion internal/handshake/hkdf.go
Expand Up @@ -7,7 +7,7 @@ import (
"golang.org/x/crypto/hkdf"
)

// hkdfExpandLabel HKDF expands a label.
// hkdfExpandLabel HKDF expands a label as defined in RFC 8446, section 7.1.
// Since this implementation avoids using a cryptobyte.Builder, it is about 15% faster than the
// hkdfExpandLabel in the standard library.
func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
Expand Down
65 changes: 58 additions & 7 deletions internal/handshake/hkdf_test.go
Expand Up @@ -2,16 +2,67 @@ package handshake

import (
"crypto"
"crypto/cipher"
"crypto/tls"
"testing"
_ "unsafe"

"golang.org/x/exp/rand"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Initial AEAD using AES-GCM", func() {
// Result generated by running in qtls:
// cipherSuiteTLS13ByID(TLS_AES_128_GCM_SHA256).expandLabel([]byte("secret"), []byte("context"), "label", 42)
It("gets the same results as qtls", func() {
expanded := hkdfExpandLabel(crypto.SHA256, []byte("secret"), []byte("context"), "label", 42)
Expect(expanded).To(Equal([]byte{0x78, 0x87, 0x6a, 0xb5, 0x84, 0xa2, 0x26, 0xb7, 0x8, 0x5a, 0x7b, 0x3a, 0x4c, 0xbb, 0x1e, 0xbc, 0x2f, 0x9b, 0x67, 0xd0, 0x6a, 0xa2, 0x24, 0xb4, 0x7d, 0x29, 0x3c, 0x7a, 0xce, 0xc7, 0xc3, 0x74, 0xcd, 0x59, 0x7a, 0xa8, 0x21, 0x5e, 0xe7, 0xca, 0x1, 0xda}))
})
type cipherSuiteTLS13 struct {
ID uint16
KeyLen int
AEAD func(key, fixedNonce []byte) cipher.AEAD
Hash crypto.Hash
}

//go:linkname cipherSuiteTLS13ByID crypto/tls.cipherSuiteTLS13ByID
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13

//go:linkname expandLabel crypto/tls.(*cipherSuiteTLS13).expandLabel
func expandLabel(cs *cipherSuiteTLS13, secret []byte, label string, context []byte, length int) []byte

var _ = Describe("HKDF", func() {
DescribeTable("gets the same results as crypto/tls",
func(cipherSuite uint16, secret, context []byte, label string, length int) {
cs := cipherSuiteTLS13ByID(cipherSuite)
expected := expandLabel(cs, secret, label, context, length)
expanded := hkdfExpandLabel(cs.Hash, secret, context, label, length)
Expect(expanded).To(Equal(expected))
},
Entry("TLS_AES_128_GCM_SHA256", tls.TLS_AES_128_GCM_SHA256, []byte("secret"), []byte("context"), "label", 42),
Entry("TLS_AES_256_GCM_SHA384", tls.TLS_AES_256_GCM_SHA384, []byte("secret"), []byte("context"), "label", 100),
Entry("TLS_CHACHA20_POLY1305_SHA256", tls.TLS_CHACHA20_POLY1305_SHA256, []byte("secret"), []byte("context"), "label", 77),
)
})

func BenchmarkHKDFExpandLabelStandardLibrary(b *testing.B) {
b.Run("TLS_AES_128_GCM_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_128_GCM_SHA256, true) })
b.Run("TLS_AES_256_GCM_SHA384", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_256_GCM_SHA384, true) })
b.Run("TLS_CHACHA20_POLY1305_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_CHACHA20_POLY1305_SHA256, true) })
}

func BenchmarkHKDFExpandLabelOptimized(b *testing.B) {
b.Run("TLS_AES_128_GCM_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_128_GCM_SHA256, false) })
b.Run("TLS_AES_256_GCM_SHA384", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_256_GCM_SHA384, false) })
b.Run("TLS_CHACHA20_POLY1305_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_CHACHA20_POLY1305_SHA256, false) })
}

func benchmarkHKDFExpandLabel(b *testing.B, cipherSuite uint16, useStdLib bool) {
b.ReportAllocs()
cs := cipherSuiteTLS13ByID(cipherSuite)
secret := make([]byte, 32)
rand.Read(secret)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if useStdLib {
expandLabel(cs, secret, "label", []byte("context"), 42)
} else {
hkdfExpandLabel(cs.Hash, secret, []byte("context"), "label", 42)
}
}
}
12 changes: 0 additions & 12 deletions internal/qtls/cipher_suite.go
@@ -1,23 +1,11 @@
package qtls

import (
"crypto"
"crypto/cipher"
"crypto/tls"
"fmt"
"unsafe"
)

type cipherSuiteTLS13 struct {
ID uint16
KeyLen int
AEAD func(key, fixedNonce []byte) cipher.AEAD
Hash crypto.Hash
}

//go:linkname cipherSuiteTLS13ByID crypto/tls.cipherSuiteTLS13ByID
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13

//go:linkname cipherSuitesTLS13 crypto/tls.cipherSuitesTLS13
var cipherSuitesTLS13 []unsafe.Pointer

Expand Down

0 comments on commit c22a3c8

Please sign in to comment.