Skip to content

Commit 7d3a695

Browse files
committed
Implement TLS Flags and the request mTLS flag.
1 parent 8fe8844 commit 7d3a695

File tree

7 files changed

+222
-1
lines changed

7 files changed

+222
-1
lines changed

src/crypto/tls/common.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const (
126126
extensionRenegotiationInfo uint16 = 0xff01
127127
extensionECH uint16 = 0xfe0d // draft-ietf-tls-esni-13
128128
extensionECHOuterExtensions uint16 = 0xfd00 // draft-ietf-tls-esni-13
129+
extensionTLSFlags uint16 = 0xfe01 // draft-ietf-tls-tlsflags-12
129130
)
130131

131132
// TLS signaling cipher suite values
@@ -364,6 +365,17 @@ type ConnectionState struct {
364365
// This means the client has offered ECH or sent GREASE ECH.
365366
ECHOffered bool
366367

368+
// PeerTLSFlags is the set of TLS Flags sent by the Peer.
369+
PeerTLSFlags []TLSFlag
370+
371+
// AgreedTLSFlags is the set of TLS Flags mutually supported by the Client
372+
// and Server.
373+
AgreedTLSFlags []TLSFlag
374+
375+
// RequestClientCert is true if the server decided to request a client
376+
// certificate.
377+
RequestClientCert bool
378+
367379
// ekm is a closure exposed via ExportKeyingMaterial.
368380
ekm func(label string, context []byte, length int) ([]byte, error)
369381
}
@@ -469,6 +481,20 @@ const (
469481
ECDSAWithSHA1 SignatureScheme = 0x0203
470482
)
471483

484+
// TLSFlag is the index of a bit in the TLSFlags bit array that should be set to
485+
// send the flag. Because TLSFlags can have so many different uses, many yet to
486+
// be defined, this extension should not be copied into the ECH
487+
// ClientHelloOuter.
488+
type TLSFlag uint16
489+
490+
const (
491+
// ExperimentalFlagSupportMTLS is a flag that signals that a client supports
492+
// mTLS, and will be able to respond to CertificateRequest messages
493+
// appropriately.
494+
// https://datatracker.ietf.org/doc/draft-jhoyla-req-mtls-flag/
495+
ExperimentalFlagSupportMTLS TLSFlag = 0x50
496+
)
497+
472498
// ClientHelloInfo contains information from a ClientHello message in order to
473499
// guide application logic in the GetCertificate and GetConfigForClient callbacks.
474500
type ClientHelloInfo struct {
@@ -905,6 +931,12 @@ type Config struct {
905931
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts.
906932
SupportDelegatedCredential bool
907933

934+
// TLSFlagsSupported is the list of flags that the client or server is
935+
// willing to support. This is currently limited to the set of flags set in
936+
// the ClientHello, although the draft specifies various other messages
937+
// where they can appear.
938+
TLSFlagsSupported []TLSFlag
939+
908940
// mutex protects sessionTicketKeys and autoSessionTicketKeys.
909941
mutex sync.RWMutex
910942
// sessionTicketKeys contains zero or more ticket keys. If set, it means
@@ -995,6 +1027,7 @@ func (c *Config) Clone() *Config {
9951027
Renegotiation: c.Renegotiation,
9961028
KeyLogWriter: c.KeyLogWriter,
9971029
SupportDelegatedCredential: c.SupportDelegatedCredential,
1030+
TLSFlagsSupported: c.TLSFlagsSupported,
9981031
ECHEnabled: c.ECHEnabled,
9991032
ClientECHConfigs: c.ClientECHConfigs,
10001033
ServerECHProvider: c.ServerECHProvider,

src/crypto/tls/conn.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ type Conn struct {
143143
configId uint8 // The ECH config id
144144
maxNameLen int // maximum_name_len indicated by the ECH config
145145
}
146+
147+
agreedTLSFlags []TLSFlag
148+
peerTLSFlags []TLSFlag
149+
requestClientCert bool
146150
}
147151

148152
// Access to net.Conn methods.
@@ -1696,6 +1700,9 @@ func (c *Conn) connectionStateLocked() ConnectionState {
16961700
} else {
16971701
state.ekm = c.ekm
16981702
}
1703+
state.PeerTLSFlags = c.peerTLSFlags
1704+
state.AgreedTLSFlags = c.agreedTLSFlags
1705+
state.RequestClientCert = c.requestClientCert
16991706
return state
17001707
}
17011708

src/crypto/tls/handshake_client.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ func (c *Conn) makeClientHello(minVersion uint16) (*clientHelloMsg, clientKeySha
218218

219219
hello.delegatedCredentialSupported = config.SupportDelegatedCredential
220220
hello.supportedSignatureAlgorithmsDC = supportedSignatureAlgorithmsDC
221+
flagBytes, err := encodeFlags(config.TLSFlagsSupported)
222+
if err != nil {
223+
return nil, nil, err
224+
}
225+
hello.tlsFlags = flagBytes
221226
}
222227

223228
if c.quic != nil {
@@ -234,6 +239,23 @@ func (c *Conn) makeClientHello(minVersion uint16) (*clientHelloMsg, clientKeySha
234239
return hello, secret, nil
235240
}
236241

242+
const maxTLSFlag = TLSFlag(2040)
243+
244+
func encodeFlags(flags []TLSFlag) ([]byte, error) {
245+
flagBytes := make([]byte, 0, 255)
246+
for _, flag := range flags {
247+
if flag >= maxTLSFlag {
248+
return nil, fmt.Errorf("cannot encode TLS flags greater than %d", maxTLSFlag)
249+
}
250+
whichByte := int(flag) >> 3
251+
if whichByte >= len(flagBytes) {
252+
flagBytes = flagBytes[:whichByte+1]
253+
}
254+
flagBytes[whichByte] |= 1 << (flag % 8)
255+
}
256+
return flagBytes, nil
257+
}
258+
237259
func (c *Conn) clientHandshake(ctx context.Context) (err error) {
238260
if c.config == nil {
239261
c.config = defaultConfig()

src/crypto/tls/handshake_messages.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ type clientHelloMsg struct {
9090
alpnProtocols []string
9191
scts bool
9292
supportedVersions []uint16
93+
tlsFlags []byte
9394
cookie []byte
9495
keyShares []keyShare
9596
earlyData bool
@@ -239,6 +240,14 @@ func (m *clientHelloMsg) marshal() ([]byte, error) {
239240
})
240241
})
241242
}
243+
if len(m.tlsFlags) > 0 {
244+
exts.AddUint16(extensionTLSFlags)
245+
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
246+
exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) {
247+
exts.AddBytes(m.tlsFlags)
248+
})
249+
})
250+
}
242251
if len(m.cookie) > 0 {
243252
// RFC 8446, Section 4.2.2
244253
exts.AddUint16(extensionCookie)
@@ -577,6 +586,12 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
577586
}
578587
m.supportedVersions = append(m.supportedVersions, vers)
579588
}
589+
case extensionTLSFlags:
590+
var flagsList cryptobyte.String
591+
if !extData.ReadUint8LengthPrefixed(&flagsList) || flagsList.Empty() {
592+
return false
593+
}
594+
m.tlsFlags = flagsList
580595
case extensionCookie:
581596
// RFC 8446, Section 4.2.2
582597
if !readUint16LengthPrefixed(&extData, &m.cookie) ||

src/crypto/tls/handshake_server_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"errors"
1717
"fmt"
1818
"io"
19+
math_rand "math/rand"
1920
"net"
2021
"os"
2122
"os/exec"
@@ -539,6 +540,90 @@ func testCrossVersionResume(t *testing.T, version uint16) {
539540
}
540541
}
541542

543+
func TestTLSFlags(t *testing.T) {
544+
serverConfig := &Config{
545+
Certificates: []Certificate{{
546+
Certificate: [][]byte{testRSACertificate},
547+
PrivateKey: testRSAPrivateKey,
548+
}},
549+
TLSFlagsSupported: []TLSFlag{0x50},
550+
}
551+
clientCert, err := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM))
552+
if err != nil {
553+
t.Fatalf("couldn't load client certs")
554+
}
555+
var flags = make([]TLSFlag, 10, 100)
556+
for i := 0; i < math_rand.Intn(100)+1; i++ {
557+
flags = append(flags, TLSFlag(math_rand.Intn(2040)))
558+
}
559+
clientConfig := &Config{
560+
TLSFlagsSupported: flags,
561+
InsecureSkipVerify: true,
562+
Certificates: []Certificate{clientCert},
563+
}
564+
state, _, err := testHandshake(t, clientConfig, serverConfig)
565+
if err != nil {
566+
t.Fatalf("handshake failed: %s", err)
567+
}
568+
found := false
569+
for _, flag := range state.PeerTLSFlags {
570+
found = found || (flag == 0x50)
571+
}
572+
if found && (state.AgreedTLSFlags[0] != TLSFlag(0x50)) {
573+
t.Fatalf("Failed to agree correct flags")
574+
}
575+
if !state.RequestClientCert == found {
576+
t.Fatalf("Failed to request client cert")
577+
}
578+
if (len(state.PeerCertificates) == 0) == found {
579+
t.Fatalf("Didn't receive correct client certs")
580+
}
581+
}
582+
583+
func TestTLSFlagsReqmTLS(t *testing.T) {
584+
serverConfig := &Config{
585+
Certificates: []Certificate{{
586+
Certificate: [][]byte{testRSACertificate},
587+
PrivateKey: testRSAPrivateKey,
588+
}},
589+
TLSFlagsSupported: []TLSFlag{0x50},
590+
}
591+
clientCert, err := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM))
592+
if err != nil {
593+
t.Fatalf("couldn't load client certs")
594+
}
595+
var flags = make([]TLSFlag, 10, 100)
596+
for i := 0; i < math_rand.Intn(100); i++ {
597+
flags = append(flags, TLSFlag(math_rand.Intn(2040)))
598+
}
599+
flags = append(flags, TLSFlag(0x50))
600+
clientConfig := &Config{
601+
TLSFlagsSupported: flags,
602+
InsecureSkipVerify: true,
603+
Certificates: []Certificate{clientCert},
604+
}
605+
state, _, err := testHandshake(t, clientConfig, serverConfig)
606+
if err != nil {
607+
t.Fatalf("handshake failed: %s", err)
608+
}
609+
found := false
610+
for _, flag := range state.PeerTLSFlags {
611+
found = found || (flag == 0x50)
612+
}
613+
if !found {
614+
t.Fatalf("req mTLS Flag not found")
615+
}
616+
if state.AgreedTLSFlags[0] != TLSFlag(0x50) {
617+
t.Fatalf("Failed to agree correct flags")
618+
}
619+
if !state.RequestClientCert {
620+
t.Fatalf("Failed to request client cert")
621+
}
622+
if len(state.PeerCertificates) == 0 {
623+
t.Fatalf("Didn't receive correct client certs")
624+
}
625+
}
626+
542627
// Note: see comment in handshake_test.go for details of how the reference
543628
// tests work.
544629

src/crypto/tls/handshake_server_tls13.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type serverHandshakeStateTLS13 struct {
4343
transcript hash.Hash
4444
clientFinished []byte
4545
certReq *certificateRequestMsgTLS13
46+
peerTLSFlags []TLSFlag
47+
tlsFlags []TLSFlag
4648

4749
hsTimings CFEventTLS13ServerHandshakeTimingInfo
4850
}
@@ -132,7 +134,9 @@ func (hs *serverHandshakeStateTLS13) handshake() error {
132134

133135
c.handleCFEvent(hs.hsTimings)
134136
c.isHandshakeComplete.Store(true)
135-
137+
c.agreedTLSFlags = hs.tlsFlags
138+
c.peerTLSFlags = hs.peerTLSFlags
139+
c.requestClientCert = hs.requestClientCert()
136140
return nil
137141
}
138142

@@ -317,6 +321,34 @@ GroupSelection:
317321
c.sendAlert(alertIllegalParameter)
318322
return errors.New("tls: invalid client key share")
319323
}
324+
if len(hs.clientHello.tlsFlags) != 0 {
325+
if hs.clientHello.tlsFlags[len(hs.clientHello.tlsFlags)-1] == 0 {
326+
c.sendAlert(alertIllegalParameter)
327+
return errors.New("tls: invalid client TLS flags")
328+
}
329+
supportedFlags, err := encodeFlags(hs.c.config.TLSFlagsSupported)
330+
if err != nil {
331+
c.sendAlert(alertInternalError)
332+
return errors.New("tls: invalid server flags")
333+
}
334+
var mutuallySupportedFlags []byte
335+
for i, sFB := range supportedFlags {
336+
if i >= len(hs.clientHello.tlsFlags) {
337+
break
338+
}
339+
mutuallySupportedFlags = append(mutuallySupportedFlags, hs.clientHello.tlsFlags[i]&sFB)
340+
}
341+
342+
peerTLSFlags, err := decodeFlags(hs.clientHello.tlsFlags)
343+
if err == nil {
344+
hs.peerTLSFlags = peerTLSFlags
345+
}
346+
347+
tlsFlags, err := decodeFlags(mutuallySupportedFlags)
348+
if err == nil {
349+
hs.tlsFlags = tlsFlags
350+
}
351+
}
320352

321353
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil)
322354
if err != nil {
@@ -356,6 +388,24 @@ GroupSelection:
356388
return nil
357389
}
358390

391+
func decodeFlags(flagBytes []byte) ([]TLSFlag, error) {
392+
var flags []TLSFlag
393+
if len(flagBytes) > int(maxTLSFlag>>3) {
394+
return nil, fmt.Errorf("TLS flags extension malformed (too long)")
395+
}
396+
397+
for byteIndex, b := range flagBytes {
398+
for i := 0; !(b == 0); i++ {
399+
if (b & 1) == 1 {
400+
flagNo := byteIndex*8 + i
401+
flags = append(flags, TLSFlag(flagNo))
402+
}
403+
b >>= 1
404+
}
405+
}
406+
return flags, nil
407+
}
408+
359409
func (hs *serverHandshakeStateTLS13) checkForResumption() error {
360410
c := hs.c
361411

@@ -892,6 +942,11 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
892942
}
893943

894944
func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
945+
for _, flag := range hs.tlsFlags {
946+
if flag == ExperimentalFlagSupportMTLS {
947+
return true
948+
}
949+
}
895950
return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
896951
}
897952

src/crypto/tls/tls_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,10 @@ func TestCloneNonFuncFields(t *testing.T) {
877877
f.Set(reflect.ValueOf([]ECHConfig{ECHConfig{}}))
878878
case "ECHEnabled":
879879
f.Set(reflect.ValueOf(true))
880+
case "PeerTLSFlags", "AgreedTLSFlags", "TLSFlagsSupported":
881+
f.Set(reflect.ValueOf([]TLSFlag{}))
882+
case "RequestClientCert":
883+
f.Set(reflect.ValueOf(true))
880884
default:
881885
t.Errorf("all fields must be accounted for, but saw unknown field %q", fn)
882886
}

0 commit comments

Comments
 (0)