Skip to content

Commit ea66efa

Browse files
committed
crypto/tls: add new X25519Kyber768Draft00 code point
* Point tls.X25519Kyber768Draft00 to the new 0x6399 identifier while the old 0xfe31 identifier is available as tls.X25519Kyber768Draft00Old. * Make sure that the kem.PrivateKey can always be mapped to the CurveID that was linked to it. This is needed since we now have two ID aliasing to the same scheme, and clients need to be able to detect whether the key share presented by the server actually matches the key share that the client originally sent. * Update tests, add the new identifier and remove unnecessary code. Link: https://mailarchive.ietf.org/arch/msg/tls/HAWpNpgptl--UZNSYuvsjB-Pc2k/ Link: https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/02/
1 parent 8284e39 commit ea66efa

File tree

4 files changed

+47
-75
lines changed

4 files changed

+47
-75
lines changed

src/crypto/tls/cfkem.go

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
// To enable set CurvePreferences with the desired scheme as the first element:
77
//
88
// import (
9-
// "github.com/cloudflare/circl/kem/tls"
10-
// "github.com/cloudflare/circl/kem/hybrid"
9+
// "crypto/tls"
1110
//
1211
// [...]
1312
//
1413
// config.CurvePreferences = []tls.CurveID{
15-
// hybrid.X25519Kyber512Draft00().(tls.TLSScheme).TLSCurveID(),
14+
// tls.X25519Kyber768Draft00,
1615
// tls.X25519,
1716
// tls.P256,
1817
// }
@@ -29,38 +28,27 @@ import (
2928
"github.com/cloudflare/circl/kem/hybrid"
3029
)
3130

32-
// Either ecdheParameters or kem.PrivateKey
31+
// Either *ecdh.PrivateKey or *kemPrivateKey
3332
type clientKeySharePrivate interface{}
3433

34+
type kemPrivateKey struct {
35+
secretKey kem.PrivateKey
36+
curveID CurveID
37+
}
38+
3539
var (
36-
X25519Kyber512Draft00 = CurveID(0xfe30)
37-
X25519Kyber768Draft00 = CurveID(0xfe31)
38-
P256Kyber768Draft00 = CurveID(0xfe32)
39-
invalidCurveID = CurveID(0)
40+
X25519Kyber512Draft00 = CurveID(0xfe30)
41+
X25519Kyber768Draft00 = CurveID(0x6399)
42+
X25519Kyber768Draft00Old = CurveID(0xfe31)
43+
P256Kyber768Draft00 = CurveID(0xfe32)
44+
invalidCurveID = CurveID(0)
4045
)
4146

42-
func kemSchemeKeyToCurveID(s kem.Scheme) CurveID {
43-
switch s.Name() {
44-
case "Kyber512-X25519":
45-
return X25519Kyber512Draft00
46-
case "Kyber768-X25519":
47-
return X25519Kyber768Draft00
48-
case "P256Kyber768Draft00":
49-
return P256Kyber768Draft00
50-
default:
51-
return invalidCurveID
52-
}
53-
}
54-
5547
// Extract CurveID from clientKeySharePrivate
5648
func clientKeySharePrivateCurveID(ks clientKeySharePrivate) CurveID {
5749
switch v := ks.(type) {
58-
case kem.PrivateKey:
59-
ret := kemSchemeKeyToCurveID(v.Scheme())
60-
if ret == invalidCurveID {
61-
panic("cfkem: internal error: don't know CurveID for this KEM")
62-
}
63-
return ret
50+
case *kemPrivateKey:
51+
return v.curveID
6452
case *ecdh.PrivateKey:
6553
ret, ok := curveIDForCurve(v.Curve())
6654
if !ok {
@@ -77,7 +65,7 @@ func curveIdToCirclScheme(id CurveID) kem.Scheme {
7765
switch id {
7866
case X25519Kyber512Draft00:
7967
return hybrid.Kyber512X25519()
80-
case X25519Kyber768Draft00:
68+
case X25519Kyber768Draft00, X25519Kyber768Draft00Old:
8169
return hybrid.Kyber768X25519()
8270
case P256Kyber768Draft00:
8371
return hybrid.P256Kyber768Draft00()
@@ -102,12 +90,12 @@ func encapsulateForKem(scheme kem.Scheme, rnd io.Reader, ppk []byte) (
10290
}
10391

10492
// Generate a new keypair using randomness from rnd.
105-
func generateKemKeyPair(scheme kem.Scheme, rnd io.Reader) (
106-
kem.PublicKey, kem.PrivateKey, error) {
93+
func generateKemKeyPair(scheme kem.Scheme, curveID CurveID, rnd io.Reader) (
94+
kem.PublicKey, *kemPrivateKey, error) {
10795
seed := make([]byte, scheme.SeedSize())
10896
if _, err := io.ReadFull(rnd, seed); err != nil {
10997
return nil, nil, err
11098
}
11199
pk, sk := scheme.DeriveKeyPair(seed)
112-
return pk, sk, nil
100+
return pk, &kemPrivateKey{sk, curveID}, nil
113101
}

src/crypto/tls/cfkem_test.go

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,16 @@ import (
77
"context"
88
"fmt"
99
"testing"
10-
11-
"github.com/cloudflare/circl/kem"
12-
"github.com/cloudflare/circl/kem/hybrid"
1310
)
1411

15-
func testHybridKEX(t *testing.T, scheme kem.Scheme, clientPQ, serverPQ,
12+
func testHybridKEX(t *testing.T, curveID CurveID, clientPQ, serverPQ,
1613
clientTLS12, serverTLS12 bool) {
1714
var clientSelectedKEX *CurveID
1815
var retry bool
1916

20-
rsaCert := Certificate{
21-
Certificate: [][]byte{testRSACertificate},
22-
PrivateKey: testRSAPrivateKey,
23-
}
24-
serverCerts := []Certificate{rsaCert}
25-
2617
clientConfig := testConfig.Clone()
2718
if clientPQ {
28-
clientConfig.CurvePreferences = []CurveID{
29-
kemSchemeKeyToCurveID(scheme),
30-
X25519,
31-
}
19+
clientConfig.CurvePreferences = []CurveID{curveID, X25519}
3220
}
3321
clientCFEventHandler := func(ev CFEvent) {
3422
switch e := ev.(type) {
@@ -44,15 +32,13 @@ func testHybridKEX(t *testing.T, scheme kem.Scheme, clientPQ, serverPQ,
4432

4533
serverConfig := testConfig.Clone()
4634
if serverPQ {
47-
serverConfig.CurvePreferences = []CurveID{
48-
kemSchemeKeyToCurveID(scheme),
49-
X25519,
50-
}
35+
serverConfig.CurvePreferences = []CurveID{curveID, X25519}
36+
} else {
37+
serverConfig.CurvePreferences = []CurveID{X25519}
5138
}
5239
if serverTLS12 {
5340
serverConfig.MaxVersion = VersionTLS12
5441
}
55-
serverConfig.Certificates = serverCerts
5642

5743
c, s := localPipe(t)
5844
done := make(chan error)
@@ -78,44 +64,43 @@ func testHybridKEX(t *testing.T, scheme kem.Scheme, clientPQ, serverPQ,
7864
var expectedRetry bool
7965

8066
if clientPQ && serverPQ && !clientTLS12 && !serverTLS12 {
81-
expectedKEX = kemSchemeKeyToCurveID(scheme)
67+
expectedKEX = curveID
8268
} else {
8369
expectedKEX = X25519
8470
}
8571
if !clientTLS12 && clientPQ && !serverPQ {
8672
expectedRetry = true
8773
}
8874

75+
if expectedRetry != retry {
76+
t.Errorf("Expected retry=%v, got retry=%v", expectedRetry, retry)
77+
}
8978
if clientSelectedKEX == nil {
9079
t.Error("No KEX happened?")
91-
}
92-
93-
if *clientSelectedKEX != expectedKEX {
80+
} else if *clientSelectedKEX != expectedKEX {
9481
t.Errorf("failed to negotiate: expected %d, got %d",
9582
expectedKEX, *clientSelectedKEX)
9683
}
97-
if expectedRetry != retry {
98-
t.Errorf("Expected retry=%v, got retry=%v", expectedRetry, retry)
99-
}
10084
}
10185

10286
func TestHybridKEX(t *testing.T) {
103-
run := func(scheme kem.Scheme, clientPQ, serverPQ, clientTLS12, serverTLS12 bool) {
104-
t.Run(fmt.Sprintf("%s serverPQ:%v clientPQ:%v serverTLS12:%v clientTLS12:%v", scheme.Name(),
87+
run := func(curveID CurveID, clientPQ, serverPQ, clientTLS12, serverTLS12 bool) {
88+
t.Run(fmt.Sprintf("%#04x serverPQ:%v clientPQ:%v serverTLS12:%v clientTLS12:%v", uint16(curveID),
10589
serverPQ, clientPQ, serverTLS12, clientTLS12), func(t *testing.T) {
106-
testHybridKEX(t, scheme, clientPQ, serverPQ, clientTLS12, serverTLS12)
90+
testHybridKEX(t, curveID, clientPQ, serverPQ, clientTLS12, serverTLS12)
10791
})
10892
}
109-
for _, scheme := range []kem.Scheme{
110-
hybrid.Kyber512X25519(),
111-
hybrid.Kyber768X25519(),
112-
hybrid.P256Kyber768Draft00(),
93+
for _, curveID := range []CurveID{
94+
X25519Kyber512Draft00,
95+
X25519Kyber768Draft00,
96+
X25519Kyber768Draft00Old,
97+
P256Kyber768Draft00,
11398
} {
114-
run(scheme, true, true, false, false)
115-
run(scheme, true, false, false, false)
116-
run(scheme, false, true, false, false)
117-
run(scheme, true, true, true, false)
118-
run(scheme, true, true, false, true)
119-
run(scheme, true, true, true, true)
99+
run(curveID, true, true, false, false)
100+
run(curveID, true, false, false, false)
101+
run(curveID, false, true, false, false)
102+
run(curveID, true, true, true, false)
103+
run(curveID, true, true, false, true)
104+
run(curveID, true, true, true, true)
120105
}
121106
}

src/crypto/tls/handshake_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (c *Conn) makeClientHello(minVersion uint16) (*clientHelloMsg, clientKeySha
136136

137137
curveID := config.curvePreferences()[0]
138138
if scheme := curveIdToCirclScheme(curveID); scheme != nil {
139-
pk, sk, err := generateKemKeyPair(scheme, config.rand())
139+
pk, sk, err := generateKemKeyPair(scheme, curveID, config.rand())
140140
if err != nil {
141141
return nil, nil, fmt.Errorf("generateKemKeyPair %s: %w",
142142
scheme.Name(), err)

src/crypto/tls/handshake_client_tls13.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import (
1616
"fmt"
1717
"hash"
1818
"time"
19-
20-
circlKem "github.com/cloudflare/circl/kem"
2119
)
2220

2321
type clientHandshakeStateTLS13 struct {
@@ -382,7 +380,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
382380
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
383381
}
384382
if scheme := curveIdToCirclScheme(curveID); scheme != nil {
385-
pk, sk, err := generateKemKeyPair(scheme, c.config.rand())
383+
pk, sk, err := generateKemKeyPair(scheme, curveID, c.config.rand())
386384
if err != nil {
387385
c.sendAlert(alertInternalError)
388386
return fmt.Errorf("HRR generateKemKeyPair %s: %w",
@@ -610,7 +608,8 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
610608
if err == nil {
611609
sharedKey, _ = key.ECDH(peerKey)
612610
}
613-
} else if sk, ok := hs.keySharePrivate.(circlKem.PrivateKey); ok {
611+
} else if key, ok := hs.keySharePrivate.(*kemPrivateKey); ok {
612+
sk := key.secretKey
614613
sharedKey, err = sk.Scheme().Decapsulate(sk, hs.serverHello.serverShare.data)
615614
if err != nil {
616615
c.sendAlert(alertIllegalParameter)

0 commit comments

Comments
 (0)