Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement TLS 1.3 key schedule using crypto/hkdf #1472

Merged
merged 2 commits into from
Jan 11, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
294 changes: 278 additions & 16 deletions patches/0002-Add-crypto-backend-foundation.patch
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,23 @@ Subject: [PATCH] Add crypto backend foundation
src/crypto/tls/cipher_suites.go | 2 +-
src/crypto/tls/fipsonly/fipsonly.go | 2 +-
src/crypto/tls/fipsonly/fipsonly_test.go | 2 +-
src/crypto/tls/handshake_client.go | 10 +-
src/crypto/tls/handshake_client_tls13.go | 14 +-
src/crypto/tls/handshake_client.go | 12 +-
src/crypto/tls/handshake_client_tls13.go | 16 +-
src/crypto/tls/handshake_server.go | 10 +-
src/crypto/tls/handshake_server_tls13.go | 25 +-
src/crypto/tls/internal/fips140tls/fipstls.go | 3 +-
src/crypto/tls/handshake_server_tls13.go | 27 ++-
src/crypto/tls/internal/fips140tls/fipstls.go | 3
src/crypto/tls/internal/tls13/doc.go | 18 ++
src/crypto/tls/internal/tls13/tls13.go | 182 ++++++++++++++
src/crypto/tls/key_schedule.go | 2 +-
src/crypto/tls/prf.go | 41 +++
src/go/build/deps_test.go | 8 +-
src/go/build/deps_test.go | 9 +-
src/hash/boring_test.go | 9 +
src/hash/example_test.go | 2 +
src/hash/marshal_test.go | 9 +
src/hash/notboring_test.go | 9 +
src/net/smtp/smtp_test.go | 72 ++++--
src/runtime/runtime_boring.go | 5 +
72 files changed, 1221 insertions(+), 89 deletions(-)
75 files changed, 1426 insertions(+), 93 deletions(-)
create mode 100644 src/crypto/dsa/boring.go
create mode 100644 src/crypto/dsa/notboring.go
create mode 100644 src/crypto/ed25519/boring.go
Expand All @@ -90,6 +93,8 @@ Subject: [PATCH] Add crypto backend foundation
create mode 100644 src/crypto/internal/backend/fips140/nosystemcrypto.go
create mode 100644 src/crypto/internal/backend/nobackend.go
create mode 100644 src/crypto/internal/backend/stub.s
create mode 100644 src/crypto/tls/internal/tls13/doc.go
create mode 100644 src/crypto/tls/internal/tls13/tls13.go
create mode 100644 src/hash/boring_test.go
create mode 100644 src/hash/notboring_test.go

Expand Down Expand Up @@ -2193,9 +2198,22 @@ index 027bc22c33c921..eba08da985f832 100644
package fipsonly

diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go
index 38bd417a0dca72..9eec202234fa94 100644
index 38bd417a0dca72..8c1ae3c7ec5485 100644
--- a/src/crypto/tls/handshake_client.go
+++ b/src/crypto/tls/handshake_client.go
@@ -11,11 +11,11 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/internal/fips140/mlkem"
- "crypto/internal/fips140/tls13"
"crypto/internal/hpke"
"crypto/rsa"
"crypto/subtle"
"crypto/tls/internal/fips140tls"
+ "crypto/tls/internal/tls13"
"crypto/x509"
"errors"
"fmt"
@@ -549,7 +549,15 @@ func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {

// Does the handshake, either a full one or resumes old session. Requires hs.c,
Expand All @@ -2214,19 +2232,24 @@ index 38bd417a0dca72..9eec202234fa94 100644

isResume, err := hs.processServerHello()
diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go
index c0396e75796add..819bc54822f0cb 100644
index c0396e75796add..2c00e119cb66e4 100644
--- a/src/crypto/tls/handshake_client_tls13.go
+++ b/src/crypto/tls/handshake_client_tls13.go
@@ -8,8 +8,8 @@ import (
@@ -8,12 +8,12 @@ import (
"bytes"
"context"
"crypto"
+ "crypto/hkdf"
"crypto/hmac"
- "crypto/internal/fips140/hkdf"
"crypto/internal/fips140/mlkem"
"crypto/internal/fips140/tls13"
- "crypto/internal/fips140/tls13"
"crypto/rsa"
"crypto/subtle"
+ "crypto/tls/internal/tls13"
"errors"
"hash"
"slices"
@@ -90,8 +90,12 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
confTranscript.Write(hs.serverHello.original[:30])
confTranscript.Write(make([]byte, 8))
Expand Down Expand Up @@ -2277,10 +2300,10 @@ index 7c75977ad3ffb2..b9db95ca7b9d5a 100644

if err := hs.processClientHello(); err != nil {
diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go
index 76fff6974e7403..bccf52fb28fcc5 100644
index 76fff6974e7403..3ef8b56e5c7898 100644
--- a/src/crypto/tls/handshake_server_tls13.go
+++ b/src/crypto/tls/handshake_server_tls13.go
@@ -8,8 +8,9 @@ import (
@@ -8,13 +8,14 @@ import (
"bytes"
"context"
"crypto"
Expand All @@ -2289,8 +2312,14 @@ index 76fff6974e7403..bccf52fb28fcc5 100644
- "crypto/internal/fips140/hkdf"
+ boring "crypto/internal/backend"
"crypto/internal/fips140/mlkem"
"crypto/internal/fips140/tls13"
- "crypto/internal/fips140/tls13"
"crypto/internal/hpke"
"crypto/rsa"
"crypto/tls/internal/fips140tls"
+ "crypto/tls/internal/tls13"
"errors"
"hash"
"internal/byteorder"
@@ -470,15 +471,19 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error {
// interfaces implemented by standard library hashes to clone the state of in
// to a new instance of h. It returns nil if the operation fails.
Expand Down Expand Up @@ -2364,6 +2393,231 @@ index 24d78d60cf5b64..a6bfd3f17c1911 100644
Force()
}
}
diff --git a/src/crypto/tls/internal/tls13/doc.go b/src/crypto/tls/internal/tls13/doc.go
new file mode 100644
index 00000000000000..1adf3098356307
--- /dev/null
+++ b/src/crypto/tls/internal/tls13/doc.go
@@ -0,0 +1,18 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Microsoft Go only package.
+
+TLS 1.3 Key Schedule is normally not part of the FIPS boundary, but upstream Go
+decided to include it in the FIPS boundary to facilitate the FIPS 140-3 certification
+process.
+
+The problem is that crypto/internal/fips140/tls13 uses crypto/internal/fips140/hkdf,
+which can't be patched to use other backends.
+
+To solve this problem, we created this package, which is a direct copy of
+crypto/internal/fips140/tls13, but uses crypto/hkdf instead of crypto/internal/fips140/hkdf.
+*/
+package tls13
diff --git a/src/crypto/tls/internal/tls13/tls13.go b/src/crypto/tls/internal/tls13/tls13.go
new file mode 100644
index 00000000000000..573896b9c1e6a8
--- /dev/null
+++ b/src/crypto/tls/internal/tls13/tls13.go
@@ -0,0 +1,182 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package tls13 implements the TLS 1.3 Key Schedule as specified in RFC 8446,
+// Section 7.1 and allowed by FIPS 140-3 IG 2.4.B Resolution 7.
+package tls13
+
+import (
+ "crypto/hkdf"
+ "hash"
+ "internal/byteorder"
+)
+
+// We don't set the service indicator in this package but we delegate that to
+// the underlying functions because the TLS 1.3 KDF does not have a standard of
+// its own.
+
+// ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
+func ExpandLabel[H hash.Hash](hash func() H, secret []byte, label string, context []byte, length int) []byte {
+ if len("tls13 ")+len(label) > 255 || len(context) > 255 {
+ // It should be impossible for this to panic: labels are fixed strings,
+ // and context is either a fixed-length computed hash, or parsed from a
+ // field which has the same length limitation.
+ //
+ // Another reasonable approach might be to return a randomized slice if
+ // we encounter an error, which would break the connection, but avoid
+ // panicking. This would perhaps be safer but significantly more
+ // confusing to users.
+ panic("tls13: label or context too long")
+ }
+ hkdfLabel := make([]byte, 0, 2+1+len("tls13 ")+len(label)+1+len(context))
+ hkdfLabel = byteorder.BEAppendUint16(hkdfLabel, uint16(length))
+ hkdfLabel = append(hkdfLabel, byte(len("tls13 ")+len(label)))
+ hkdfLabel = append(hkdfLabel, "tls13 "...)
+ hkdfLabel = append(hkdfLabel, label...)
+ hkdfLabel = append(hkdfLabel, byte(len(context)))
+ hkdfLabel = append(hkdfLabel, context...)
+ key, err := hkdf.Expand(hash, secret, string(hkdfLabel), length)
+ if err != nil {
+ panic(err)
+ }
+ return key
+}
+
+func extract[H hash.Hash](hash func() H, newSecret, currentSecret []byte) []byte {
+ if newSecret == nil {
+ newSecret = make([]byte, hash().Size())
+ }
+ prk, err := hkdf.Extract(hash, newSecret, currentSecret)
+ if err != nil {
+ panic(err)
+ }
+ return prk
+}
+
+func deriveSecret[H hash.Hash](hash func() H, secret []byte, label string, transcript hash.Hash) []byte {
+ if transcript == nil {
+ transcript = hash()
+ }
+ return ExpandLabel(hash, secret, label, transcript.Sum(nil), transcript.Size())
+}
+
+const (
+ resumptionBinderLabel = "res binder"
+ clientEarlyTrafficLabel = "c e traffic"
+ clientHandshakeTrafficLabel = "c hs traffic"
+ serverHandshakeTrafficLabel = "s hs traffic"
+ clientApplicationTrafficLabel = "c ap traffic"
+ serverApplicationTrafficLabel = "s ap traffic"
+ earlyExporterLabel = "e exp master"
+ exporterLabel = "exp master"
+ resumptionLabel = "res master"
+)
+
+type EarlySecret struct {
+ secret []byte
+ hash func() hash.Hash
+}
+
+func NewEarlySecret[H hash.Hash](h func() H, psk []byte) *EarlySecret {
+ return &EarlySecret{
+ secret: extract(h, psk, nil),
+ hash: func() hash.Hash { return h() },
+ }
+}
+
+func (s *EarlySecret) ResumptionBinderKey() []byte {
+ return deriveSecret(s.hash, s.secret, resumptionBinderLabel, nil)
+}
+
+// ClientEarlyTrafficSecret derives the client_early_traffic_secret from the
+// early secret and the transcript up to the ClientHello.
+func (s *EarlySecret) ClientEarlyTrafficSecret(transcript hash.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, clientEarlyTrafficLabel, transcript)
+}
+
+type HandshakeSecret struct {
+ secret []byte
+ hash func() hash.Hash
+}
+
+func (s *EarlySecret) HandshakeSecret(sharedSecret []byte) *HandshakeSecret {
+ derived := deriveSecret(s.hash, s.secret, "derived", nil)
+ return &HandshakeSecret{
+ secret: extract(s.hash, sharedSecret, derived),
+ hash: s.hash,
+ }
+}
+
+// ClientHandshakeTrafficSecret derives the client_handshake_traffic_secret from
+// the handshake secret and the transcript up to the ServerHello.
+func (s *HandshakeSecret) ClientHandshakeTrafficSecret(transcript hash.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, clientHandshakeTrafficLabel, transcript)
+}
+
+// ServerHandshakeTrafficSecret derives the server_handshake_traffic_secret from
+// the handshake secret and the transcript up to the ServerHello.
+func (s *HandshakeSecret) ServerHandshakeTrafficSecret(transcript hash.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, serverHandshakeTrafficLabel, transcript)
+}
+
+type MasterSecret struct {
+ secret []byte
+ hash func() hash.Hash
+}
+
+func (s *HandshakeSecret) MasterSecret() *MasterSecret {
+ derived := deriveSecret(s.hash, s.secret, "derived", nil)
+ return &MasterSecret{
+ secret: extract(s.hash, nil, derived),
+ hash: s.hash,
+ }
+}
+
+// ClientApplicationTrafficSecret derives the client_application_traffic_secret_0
+// from the master secret and the transcript up to the server Finished.
+func (s *MasterSecret) ClientApplicationTrafficSecret(transcript hash.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, clientApplicationTrafficLabel, transcript)
+}
+
+// ServerApplicationTrafficSecret derives the server_application_traffic_secret_0
+// from the master secret and the transcript up to the server Finished.
+func (s *MasterSecret) ServerApplicationTrafficSecret(transcript hash.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, serverApplicationTrafficLabel, transcript)
+}
+
+// ResumptionMasterSecret derives the resumption_master_secret from the master secret
+// and the transcript up to the client Finished.
+func (s *MasterSecret) ResumptionMasterSecret(transcript hash.Hash) []byte {
+ return deriveSecret(s.hash, s.secret, resumptionLabel, transcript)
+}
+
+type ExporterMasterSecret struct {
+ secret []byte
+ hash func() hash.Hash
+}
+
+// ExporterMasterSecret derives the exporter_master_secret from the master secret
+// and the transcript up to the server Finished.
+func (s *MasterSecret) ExporterMasterSecret(transcript hash.Hash) *ExporterMasterSecret {
+ return &ExporterMasterSecret{
+ secret: deriveSecret(s.hash, s.secret, exporterLabel, transcript),
+ hash: s.hash,
+ }
+}
+
+// EarlyExporterMasterSecret derives the exporter_master_secret from the early secret
+// and the transcript up to the ClientHello.
+func (s *EarlySecret) EarlyExporterMasterSecret(transcript hash.Hash) *ExporterMasterSecret {
+ return &ExporterMasterSecret{
+ secret: deriveSecret(s.hash, s.secret, earlyExporterLabel, transcript),
+ hash: s.hash,
+ }
+}
+
+func (s *ExporterMasterSecret) Exporter(label string, context []byte, length int) []byte {
+ secret := deriveSecret(s.hash, s.secret, label, nil)
+ h := s.hash()
+ h.Write(context)
+ return ExpandLabel(s.hash, secret, "exporter", h.Sum(nil), length)
+}
diff --git a/src/crypto/tls/key_schedule.go b/src/crypto/tls/key_schedule.go
index 38d6d3f7be1673..b236ae44a34030 100644
--- a/src/crypto/tls/key_schedule.go
+++ b/src/crypto/tls/key_schedule.go
@@ -8,7 +8,7 @@ import (
"crypto/ecdh"
"crypto/hmac"
"crypto/internal/fips140/mlkem"
- "crypto/internal/fips140/tls13"
+ "crypto/tls/internal/tls13"
"errors"
"hash"
"io"
diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go
index e7369542a73270..ff52175e4ac636 100644
--- a/src/crypto/tls/prf.go
Expand Down Expand Up @@ -2434,7 +2688,7 @@ index e7369542a73270..ff52175e4ac636 100644
}
}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index a62a5173b9c6bc..b39db00839ab43 100644
index a62a5173b9c6bc..9ef5b8ea058783 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -493,6 +493,8 @@ var depsRules = `
Expand Down Expand Up @@ -2462,15 +2716,23 @@ index a62a5173b9c6bc..b39db00839ab43 100644
< crypto/boring
< crypto/aes,
crypto/des,
@@ -534,6 +539,7 @@ var depsRules = `
@@ -526,6 +531,7 @@ var depsRules = `
crypto/pbkdf2,
crypto/ecdh,
crypto/mlkem
+ < crypto/tls/internal/tls13
< CRYPTO;

CGO, fmt, net !< CRYPTO;
@@ -534,6 +540,7 @@ var depsRules = `

CRYPTO, FMT, math/big
< crypto/internal/boring/bbig
+ < crypto/internal/backend/bbig
< crypto/rand
< crypto/ed25519 # depends on crypto/rand.Reader
< encoding/asn1
@@ -547,7 +553,7 @@ var depsRules = `
@@ -547,7 +554,7 @@ var depsRules = `

# TLS, Prince of Dependencies.

Expand Down
Loading