Files
boringssl-antelopeio/ssl/test/runner/ticket.go
T
Victor Tan 558960d1e1 Add support for the new ALPS codepoint
Old version Chrome with the existing ALPS codepoint can potentially cause network error due to an arithmetic overflow bug in Chrome ALPS decoder (We already fixed the issues starting from M100 in Chrome).

This CL add a new codepoint for ALPS extension in a way that can be enabled on individual connections., To support multiple versions of Chrome, we need to support both codepoints in BoringSSL.

For details: https://docs.google.com/document/d/16pysbV_ym_qAau_DBYnrw2A4h5ve2212wfcoYASt52U

Change-Id: Iea7822e757d23009648febc8eaff1c91b0f06e18
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/61125
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
2023-09-12 18:01:21 +00:00

222 lines
5.7 KiB
Go

// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runner
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"errors"
"io"
"time"
"golang.org/x/crypto/cryptobyte"
)
// sessionState contains the information that is serialized into a session
// ticket in order to later resume a connection.
type sessionState struct {
vers uint16
cipherSuite uint16
secret []byte
handshakeHash []byte
certificates [][]byte
extendedMasterSecret bool
earlyALPN []byte
ticketCreationTime time.Time
ticketExpiration time.Time
ticketFlags uint32
ticketAgeAdd uint32
hasApplicationSettings bool
localApplicationSettings []byte
peerApplicationSettings []byte
hasApplicationSettingsOld bool
localApplicationSettingsOld []byte
peerApplicationSettingsOld []byte
}
func (s *sessionState) marshal() []byte {
msg := cryptobyte.NewBuilder(nil)
msg.AddUint16(s.vers)
msg.AddUint16(s.cipherSuite)
addUint16LengthPrefixedBytes(msg, s.secret)
addUint16LengthPrefixedBytes(msg, s.handshakeHash)
msg.AddUint16(uint16(len(s.certificates)))
for _, cert := range s.certificates {
addUint24LengthPrefixedBytes(msg, cert)
}
if s.extendedMasterSecret {
msg.AddUint8(1)
} else {
msg.AddUint8(0)
}
if s.vers >= VersionTLS13 {
msg.AddUint64(uint64(s.ticketCreationTime.UnixNano()))
msg.AddUint64(uint64(s.ticketExpiration.UnixNano()))
msg.AddUint32(s.ticketFlags)
msg.AddUint32(s.ticketAgeAdd)
}
addUint16LengthPrefixedBytes(msg, s.earlyALPN)
if s.hasApplicationSettings {
msg.AddUint8(1)
addUint16LengthPrefixedBytes(msg, s.localApplicationSettings)
addUint16LengthPrefixedBytes(msg, s.peerApplicationSettings)
} else {
msg.AddUint8(0)
}
if s.hasApplicationSettingsOld {
msg.AddUint8(1)
addUint16LengthPrefixedBytes(msg, s.localApplicationSettingsOld)
addUint16LengthPrefixedBytes(msg, s.peerApplicationSettingsOld)
} else {
msg.AddUint8(0)
}
return msg.BytesOrPanic()
}
func readBool(reader *cryptobyte.String, out *bool) bool {
var value uint8
if !reader.ReadUint8(&value) {
return false
}
if value == 0 {
*out = false
return true
}
if value == 1 {
*out = true
return true
}
return false
}
func (s *sessionState) unmarshal(data []byte) bool {
reader := cryptobyte.String(data)
var numCerts uint16
if !reader.ReadUint16(&s.vers) ||
!reader.ReadUint16(&s.cipherSuite) ||
!readUint16LengthPrefixedBytes(&reader, &s.secret) ||
!readUint16LengthPrefixedBytes(&reader, &s.handshakeHash) ||
!reader.ReadUint16(&numCerts) {
return false
}
s.certificates = make([][]byte, int(numCerts))
for i := range s.certificates {
if !readUint24LengthPrefixedBytes(&reader, &s.certificates[i]) {
return false
}
}
if !readBool(&reader, &s.extendedMasterSecret) {
return false
}
if s.vers >= VersionTLS13 {
var ticketCreationTime, ticketExpiration uint64
if !reader.ReadUint64(&ticketCreationTime) ||
!reader.ReadUint64(&ticketExpiration) ||
!reader.ReadUint32(&s.ticketFlags) ||
!reader.ReadUint32(&s.ticketAgeAdd) {
return false
}
s.ticketCreationTime = time.Unix(0, int64(ticketCreationTime))
s.ticketExpiration = time.Unix(0, int64(ticketExpiration))
}
if !readUint16LengthPrefixedBytes(&reader, &s.earlyALPN) ||
!readBool(&reader, &s.hasApplicationSettings) {
return false
}
if s.hasApplicationSettings {
if !readUint16LengthPrefixedBytes(&reader, &s.localApplicationSettings) ||
!readUint16LengthPrefixedBytes(&reader, &s.peerApplicationSettings) {
return false
}
}
if !readBool(&reader, &s.hasApplicationSettingsOld) {
return false
}
if s.hasApplicationSettingsOld {
if !readUint16LengthPrefixedBytes(&reader, &s.localApplicationSettingsOld) ||
!readUint16LengthPrefixedBytes(&reader, &s.peerApplicationSettingsOld) {
return false
}
}
if len(reader) > 0 {
return false
}
return true
}
func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
key := c.config.SessionTicketKey[:]
if c.config.Bugs.EncryptSessionTicketKey != nil {
key = c.config.Bugs.EncryptSessionTicketKey[:]
}
serialized := state.marshal()
encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size)
iv := encrypted[:aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
return nil, err
}
block, err := aes.NewCipher(key[:16])
if err != nil {
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
}
cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized)
mac := hmac.New(sha256.New, key[16:32])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
mac.Sum(macBytes[:0])
return encrypted, nil
}
func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
if len(encrypted) < aes.BlockSize+sha256.Size {
return nil, false
}
iv := encrypted[:aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
expected := mac.Sum(nil)
if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
return nil, false
}
block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
if err != nil {
return nil, false
}
ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
plaintext := make([]byte, len(ciphertext))
cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
state := new(sessionState)
ok := state.unmarshal(plaintext)
return state, ok
}