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 TLSFlags extension #151

Open
wants to merge 1 commit into
base: cf
Choose a base branch
from
Open

Implement TLSFlags extension #151

wants to merge 1 commit into from

Conversation

jhoyla
Copy link
Contributor

@jhoyla jhoyla commented Aug 7, 2023

This PR implements the first half of the TLS Flags extension.

@bwesterb bwesterb self-requested a review August 7, 2023 20:30
@bwesterb
Copy link
Member

bwesterb commented Aug 7, 2023

It needs some tests.

Copy link
Contributor

@cjpatton cjpatton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to tests. We should at least test that, when the flags are enabled by the client in crypto/tls.Config, the server sees the value in the ClientHello.

Another high-level question: How does this interact with ECH?

  1. Is the the extension present in both the inner and outer handshake?
  2. If so, should it appear only in the inner handshake?

@@ -851,6 +858,8 @@ type Config struct {
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts.
SupportDelegatedCredential bool

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document here what TLSFlagSet is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TLSFlagsSupported still needs to be documented. It appears that it controls both the client and server-supported flags.

You should document that this is currently limited to controlling TLS Flags in the Server Hello. The spec also allows EncryptedExtensions, Certificate, Hello Retry Request.

type TLSFlag uint16

const (
FlagSupportMTLS TLSFlag = 0x50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document what this is. IIRC this is not yet a codepoint that's documented? If so, we should mark it as experimental in the API.

Suggested change
FlagSupportMTLS TLSFlag = 0x50
/// ExperimentalFlagSuppportMTLS ...
ExperimentalFlagSupportMTLS TLSFlag = 0x50

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional that this flag occupies 11 bytes? 0x50 is 80 decimal, and since we start counting at 0, this occupies ceil(81 / 8) = 11 bytes.

@bwesterb
Copy link
Member

bwesterb commented Aug 7, 2023

Another high-level question: How does this interact with ECH?

Very good question.

@jhoyla jhoyla changed the title Implement client side of TLSFlags extension Implement TLSFlags extension Aug 7, 2023
@jhoyla
Copy link
Contributor Author

jhoyla commented Aug 7, 2023

Added code for server side.
Tests to come tomorrow.

@cjpatton
Copy link
Contributor

cjpatton commented Aug 8, 2023

Another high-level question: How does this interact with ECH?

1. Is the the extension present in both the inner and outer handshake?

2. If so, should it appear only in the inner handshake?

Currently the extension will be added to the inner handshake only: https://github.com/cloudflare/go/blob/cf/src/crypto/tls/ech.go#L81-L85

Only specific extensions are copied into the outer handshake: https://github.com/cloudflare/go/blob/cf/src/crypto/tls/ech.go#L92-L106

In my opinion, this is the correct behavior, assuming conservatively that the value of the TLS flags extension is privacy sensitive: https://www.ietf.org/archive/id/draft-ietf-tls-esni-16.html#name-outer-clienthello

Note that since it appears in the inner handshake, it will be used by the server to terminate the connection. OTOH, if ECH is rejected, then it won't be used by the server to terminate the connection.

@Lekensteyn
Copy link
Contributor

(Rebased on the cf branch based on Go 1.21.1, no other changes.)

return false
}
m.tlsFlags = append(m.tlsFlags, flagByte)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this whole block can be simplified to:

var flagsList cryptobyte.String
if !extData.ReadUint8LengthPrefixed(&flagsList) || flagsList.Empty() {
    return false
}
m.tlsFlags = flagsList

That would save some unnecessary memory allocations as well by avoiding the append. A cryptobyte.String is an alias for []byte: https://pkg.go.dev/golang.org/x/crypto/cryptobyte#String

@@ -318,6 +319,23 @@ GroupSelection:
c.sendAlert(alertIllegalParameter)
return errors.New("tls: invalid client key share")
}
if len(hs.clientHello.tlsFlags) != 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing check: hs.clientHello.tlsFlags[0] != 0 or else you need to c.sendAlert(alertIllegalParameter).

To comply with: "An implementation that receives an all-zero value for this extension or a value that contains trailing zero bytes MUST generate a fatal illegal_parameter alert."

if len(hs.clientHello.tlsFlags) != 0 {
supportedFlags, err := encodeFlags(hs.c.config.TLSFlagsSupported)
if err != nil {
return errors.New("tls: invalid server flags")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you should also send an internal error alert here?

@@ -465,6 +466,12 @@ const (
ECDSAWithSHA1 SignatureScheme = 0x0203
)

type TLSFlag uint16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make clear that this is the flag number (index) rather than the raw representation:

Suggested change
type TLSFlag uint16
// TLSFlag represents the flag number.
type TLSFlag uint16

@@ -125,6 +125,7 @@ const (
extensionRenegotiationInfo uint16 = 0xff01
extensionECH uint16 = 0xfe0d // draft-ietf-tls-esni-13
extensionECHOuterExtensions uint16 = 0xfd00 // draft-ietf-tls-esni-13
extensionTLSFlags uint16 = 0xfe01 // draft-ietf-tls-tlsflags-12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0xfe01 (65025) belongs to the "Unassigned" namespace. Unless we have it assigned, we should probably stick to the "Reserved for Private Use" namespace which has a range starting at 0xff02 (65282).

https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1

@@ -851,6 +858,8 @@ type Config struct {
// See https://tools.ietf.org/html/draft-ietf-tls-subcerts.
SupportDelegatedCredential bool

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TLSFlagsSupported still needs to be documented. It appears that it controls both the client and server-supported flags.

You should document that this is currently limited to controlling TLS Flags in the Server Hello. The spec also allows EncryptedExtensions, Certificate, Hello Retry Request.

flagNo := byteIndex*8 + i
if flagNo >= int(maxTLSFlag) {
return nil, fmt.Errorf("TLS flag is out of range: %d", flagNo)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimization: This check can be removed by checking outside the outer loop. Fail on len(flagBytes) > 255 or alternatively len(flagBytes) * 8 > maxTLSFlag.

type TLSFlag uint16

const (
FlagSupportMTLS TLSFlag = 0x50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional that this flag occupies 11 bytes? 0x50 is 80 decimal, and since we start counting at 0, this occupies ceil(81 / 8) = 11 bytes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants