-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
proposal: crypto/tls: SessionTicketWrapper and Forward Secrecy by default #19199
Comments
I have nothing useful to add crypto-wise, but will note that "-Wrapper" is a pretty gross suffix. |
Oh, yeah, for the record I don't like the name and have no attachment to it. Just couldn't come up with anything better. |
Sealer/Seal/Unseal could be an alternative. It's very close to crypto.AEAD, not sure if it's a good thing. Also: returning Also: the implementation might need to surface a need to re-encrypt the ticket (only meaningful in 1.2) and a ticket lifetime hint (only meaningful in 1.3). But not sure where to put them. |
If it's of any interest looking at some code in the wild, here's how Caddy does key rotation currently: https://github.com/mholt/caddy/blob/06873175bf9141531e5de2e6b061f2ba94014edc/caddytls/crypto.go#L259 And here's how it's initialized: https://github.com/mholt/caddy/blob/06873175bf9141531e5de2e6b061f2ba94014edc/caddyhttp/httpserver/server.go#L256 |
/cc @agl |
I definitely have a use case for this API so I'd like to offer my thoughts. As for the name I agree that Wrapper/Wrap/Unwrap is an awful naming scheme. It also seems confusing, what does 'wrapping' actually mean in this context. It sounds more like you're asking for some sort of binary framing to be added. Sealer/Seal/Unseal is much better and also clearer as to the intended purpose - to cryptographically seal the opaque data. Perhaps Sealer/Seal/Open is best though, both to be more consistent with I think the ticket lifetime (nit: it's not actually a hint any more in TLS 1.3 is it?) should be on the I also understand there to be overlap between the owners of crypto/tls and BoringSSL, in which case it seems wise to keep things at least broadly consistent with the BoringSSL API (see https://boringssl-review.googlesource.com/14144 & https://boringssl-review.googlesource.com/14205). I actually queried the inability to renew a ticket in that first change:
and @davidben stated that this was very much intentional:
I'd argue very strongly that TLS 1.2 ticket renewal should not be included in this API at all. [Although, it should go in separate issue, I actually want to suggest that ticket renewal be removed from crypto/tls entirely, for the reasons above]. The one real problem I have with this proposal is the func (s *Sealer) Clone() SessionTicketWrapper { return s } Instead of a Clone method, perhaps the |
Actually, I was also slightly confused by the addition of I'd like to propose a simpler API, just providing the following two methods: type SessionTicketSealer interface {
Seal(content []byte) (ticket []byte, err error)
Open(ticket []byte) (content []byte, success bool)
} I think it's nicer that the two methods are more symmetric as well. Perhaps the success return value should be eliminated as well, just returning nil seems more sensible. It doesn't make any sense to return That would just leave the following API, which seems cleaner: type SessionTicketSealer interface {
Seal(content []byte) (ticket []byte, err error)
Open(ticket []byte) (content []byte)
} If there was a need to act upon the Hell, you could even do something like this if you really wanted to: type sealer struct { chi *tls.ClientHelloInfo }
func main() {
// ...
config.GetConfigForClient = func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
config := config.Clone()
config.SessionTicketSealer = &sealer{chi}
return config, nil
}
// ...
}
func (s *sealer) Seal(content []byte) (ticket []byte, err error) {
// make decisions based upon s.chi here
return ...
}
func (s *sealer) Open(ticket []byte) (content []byte, success bool) {
// make decisions based upon s.chi here
return ...
} |
With the advent of TLS 1.3, stateful storage of Session IDs and encryption of session tickets are merging. We should try to make a common API out of both, so similarly to #25228, TBD in 1.3. |
Change https://go.dev/cl/496821 mentions this issue: |
By default a Go TLS server does not offer any Forward Secrecy in respect to the Session Ticket key, as that is fixed for the lifetime of the server. (And Session Tickets in 1.2 are awful: sent before the ChangeCipherSpec on every connection, with the ephemeral key material of the current connection.) Moreover, there is no way to abstract away the keys a-la-
crypto.Signer
. Finally, there are already too many ways to specify keys. This proposal tries to address all these concerns.The main interface would be
SessionTicketWrapper
:A simple
tls.Config
attribute would be the main hook.Three standard implementations would be offered, providing behind the scenes all current behaviors and the new default.
SessionTicketWrapperFixed
is instantiated under the hood whenConfig.SessionTicketKey
is set.SessionTicketWrapperManual
is instantiated under the hood whenConfig.SetSessionTicketKeys
is called.SessionTicketWrapperRotating
would be the default for a server that does not set any property, making the default have a 24-hours FS/resumption cliff.On
Clone
andGetConfigForClient
,SessionTicketWrapper.Clone
is called. TheFixed
andManual
implementations make deep copies, so that operations on the new ones don't affect the original, maintaining current behavior.By very strict reading, there might be a Go 1 violation: a server that starts without
SessionTicketKey
, does a handshake, then copies theSessionTicketKey
expecting it to be the fixed key will fail. IMHO it's not a significant breakage and worth it for FS./cc @agl
Supersedes #17432
The text was updated successfully, but these errors were encountered: