| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- package mint
- import (
- "crypto/x509"
- "time"
- )
- // Marker interface for actions that an implementation should take based on
- // state transitions.
- type HandshakeAction interface{}
- type QueueHandshakeMessage struct {
- Message *HandshakeMessage
- }
- type SendQueuedHandshake struct{}
- type SendEarlyData struct{}
- type RekeyIn struct {
- epoch Epoch
- KeySet keySet
- }
- type RekeyOut struct {
- epoch Epoch
- KeySet keySet
- }
- type ResetOut struct {
- seq uint64
- }
- type StorePSK struct {
- PSK PreSharedKey
- }
- type HandshakeState interface {
- Next(handshakeMessageReader) (HandshakeState, []HandshakeAction, Alert)
- State() State
- }
- type AppExtensionHandler interface {
- Send(hs HandshakeType, el *ExtensionList) error
- Receive(hs HandshakeType, el *ExtensionList) error
- }
- // ConnectionOptions objects represent per-connection settings for a client
- // initiating a connection
- type ConnectionOptions struct {
- ServerName string
- NextProtos []string
- }
- // ConnectionParameters objects represent the parameters negotiated for a
- // connection.
- type ConnectionParameters struct {
- UsingPSK bool
- UsingDH bool
- ClientSendingEarlyData bool
- UsingEarlyData bool
- RejectedEarlyData bool
- UsingClientAuth bool
- CipherSuite CipherSuite
- ServerName string
- NextProto string
- }
- // Working state for the handshake.
- type HandshakeContext struct {
- timeoutMS uint32
- timers *timerSet
- recvdRecords []uint64
- sentFragments []*SentHandshakeFragment
- hIn, hOut *HandshakeLayer
- waitingNextFlight bool
- earlyData []byte
- }
- func (hc *HandshakeContext) SetVersion(version uint16) {
- if hc.hIn.conn != nil {
- hc.hIn.conn.SetVersion(version)
- }
- if hc.hOut.conn != nil {
- hc.hOut.conn.SetVersion(version)
- }
- }
- // stateConnected is symmetric between client and server
- type stateConnected struct {
- Params ConnectionParameters
- hsCtx *HandshakeContext
- isClient bool
- cryptoParams CipherSuiteParams
- resumptionSecret []byte
- clientTrafficSecret []byte
- serverTrafficSecret []byte
- exporterSecret []byte
- peerCertificates []*x509.Certificate
- verifiedChains [][]*x509.Certificate
- }
- var _ HandshakeState = &stateConnected{}
- func (state stateConnected) State() State {
- if state.isClient {
- return StateClientConnected
- }
- return StateServerConnected
- }
- func (state *stateConnected) KeyUpdate(request KeyUpdateRequest) ([]HandshakeAction, Alert) {
- var trafficKeys keySet
- if state.isClient {
- state.clientTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.clientTrafficSecret,
- labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size())
- trafficKeys = makeTrafficKeys(state.cryptoParams, state.clientTrafficSecret)
- } else {
- state.serverTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.serverTrafficSecret,
- labelServerApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size())
- trafficKeys = makeTrafficKeys(state.cryptoParams, state.serverTrafficSecret)
- }
- kum, err := state.hsCtx.hOut.HandshakeMessageFromBody(&KeyUpdateBody{KeyUpdateRequest: request})
- if err != nil {
- logf(logTypeHandshake, "[StateConnected] Error marshaling key update message: %v", err)
- return nil, AlertInternalError
- }
- toSend := []HandshakeAction{
- QueueHandshakeMessage{kum},
- SendQueuedHandshake{},
- RekeyOut{epoch: EpochUpdate, KeySet: trafficKeys},
- }
- return toSend, AlertNoAlert
- }
- func (state *stateConnected) NewSessionTicket(length int, lifetime, earlyDataLifetime uint32) ([]HandshakeAction, Alert) {
- tkt, err := NewSessionTicket(length, lifetime)
- if err != nil {
- logf(logTypeHandshake, "[StateConnected] Error generating NewSessionTicket: %v", err)
- return nil, AlertInternalError
- }
- err = tkt.Extensions.Add(&TicketEarlyDataInfoExtension{earlyDataLifetime})
- if err != nil {
- logf(logTypeHandshake, "[StateConnected] Error adding extension to NewSessionTicket: %v", err)
- return nil, AlertInternalError
- }
- resumptionKey := HkdfExpandLabel(state.cryptoParams.Hash, state.resumptionSecret,
- labelResumption, tkt.TicketNonce, state.cryptoParams.Hash.Size())
- newPSK := PreSharedKey{
- CipherSuite: state.cryptoParams.Suite,
- IsResumption: true,
- Identity: tkt.Ticket,
- Key: resumptionKey,
- NextProto: state.Params.NextProto,
- ReceivedAt: time.Now(),
- ExpiresAt: time.Now().Add(time.Duration(tkt.TicketLifetime) * time.Second),
- TicketAgeAdd: tkt.TicketAgeAdd,
- }
- tktm, err := state.hsCtx.hOut.HandshakeMessageFromBody(tkt)
- if err != nil {
- logf(logTypeHandshake, "[StateConnected] Error marshaling NewSessionTicket: %v", err)
- return nil, AlertInternalError
- }
- toSend := []HandshakeAction{
- StorePSK{newPSK},
- QueueHandshakeMessage{tktm},
- SendQueuedHandshake{},
- }
- return toSend, AlertNoAlert
- }
- // Next does nothing for this state.
- func (state stateConnected) Next(hr handshakeMessageReader) (HandshakeState, []HandshakeAction, Alert) {
- return state, nil, AlertNoAlert
- }
- func (state stateConnected) ProcessMessage(hm *HandshakeMessage) (HandshakeState, []HandshakeAction, Alert) {
- if hm == nil {
- logf(logTypeHandshake, "[StateConnected] Unexpected message")
- return nil, nil, AlertUnexpectedMessage
- }
- bodyGeneric, err := hm.ToBody()
- if err != nil {
- logf(logTypeHandshake, "[StateConnected] Error decoding message: %v", err)
- return nil, nil, AlertDecodeError
- }
- switch body := bodyGeneric.(type) {
- case *KeyUpdateBody:
- var trafficKeys keySet
- if !state.isClient {
- state.clientTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.clientTrafficSecret,
- labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size())
- trafficKeys = makeTrafficKeys(state.cryptoParams, state.clientTrafficSecret)
- } else {
- state.serverTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.serverTrafficSecret,
- labelServerApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size())
- trafficKeys = makeTrafficKeys(state.cryptoParams, state.serverTrafficSecret)
- }
- toSend := []HandshakeAction{RekeyIn{epoch: EpochUpdate, KeySet: trafficKeys}}
- // If requested, roll outbound keys and send a KeyUpdate
- if body.KeyUpdateRequest == KeyUpdateRequested {
- logf(logTypeHandshake, "Received key update, update requested", body.KeyUpdateRequest)
- moreToSend, alert := state.KeyUpdate(KeyUpdateNotRequested)
- if alert != AlertNoAlert {
- return nil, nil, alert
- }
- toSend = append(toSend, moreToSend...)
- }
- return state, toSend, AlertNoAlert
- case *NewSessionTicketBody:
- // XXX: Allow NewSessionTicket in both directions?
- if !state.isClient {
- return nil, nil, AlertUnexpectedMessage
- }
- resumptionKey := HkdfExpandLabel(state.cryptoParams.Hash, state.resumptionSecret,
- labelResumption, body.TicketNonce, state.cryptoParams.Hash.Size())
- psk := PreSharedKey{
- CipherSuite: state.cryptoParams.Suite,
- IsResumption: true,
- Identity: body.Ticket,
- Key: resumptionKey,
- NextProto: state.Params.NextProto,
- ReceivedAt: time.Now(),
- ExpiresAt: time.Now().Add(time.Duration(body.TicketLifetime) * time.Second),
- TicketAgeAdd: body.TicketAgeAdd,
- }
- toSend := []HandshakeAction{StorePSK{psk}}
- return state, toSend, AlertNoAlert
- }
- logf(logTypeHandshake, "[StateConnected] Unexpected message type %v", hm.msgType)
- return nil, nil, AlertUnexpectedMessage
- }
|