uquic/packet_packer.go
Marten Seemann 8c5e7818a0
retransmit the diversification nonce in the packet carrying the SHLO
The packet containing the SHLO is the only packet that is sent with
initial encryption. If it is lost, we need to make sure that the
diversification nonce is included in the PublicHeader, otherwise the
client will not be able to derive the keys for the forward-secure
encryption.
2017-03-01 15:11:01 +07:00

266 lines
9.4 KiB
Go

package quic
import (
"bytes"
"errors"
"fmt"
"github.com/lucas-clemente/quic-go/ackhandler"
"github.com/lucas-clemente/quic-go/frames"
"github.com/lucas-clemente/quic-go/handshake"
"github.com/lucas-clemente/quic-go/protocol"
"github.com/lucas-clemente/quic-go/qerr"
)
type packedPacket struct {
number protocol.PacketNumber
raw []byte
frames []frames.Frame
encryptionLevel protocol.EncryptionLevel
}
type packetPacker struct {
connectionID protocol.ConnectionID
perspective protocol.Perspective
version protocol.VersionNumber
cryptoSetup handshake.CryptoSetup
// as long as packets are not sent with forward-secure encryption, we limit the MaxPacketSize such that they can be retransmitted as a whole
isForwardSecure bool
packetNumberGenerator *packetNumberGenerator
connectionParameters handshake.ConnectionParametersManager
streamFramer *streamFramer
controlFrames []frames.Frame
}
func newPacketPacker(connectionID protocol.ConnectionID, cryptoSetup handshake.CryptoSetup, connectionParameters handshake.ConnectionParametersManager, streamFramer *streamFramer, perspective protocol.Perspective, version protocol.VersionNumber) *packetPacker {
return &packetPacker{
cryptoSetup: cryptoSetup,
connectionID: connectionID,
connectionParameters: connectionParameters,
perspective: perspective,
version: version,
streamFramer: streamFramer,
packetNumberGenerator: newPacketNumberGenerator(protocol.SkipPacketAveragePeriodLength),
}
}
// PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame
func (p *packetPacker) PackConnectionClose(ccf *frames.ConnectionCloseFrame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
// in case the connection is closed, all queued control frames aren't of any use anymore
// discard them and queue the ConnectionCloseFrame
p.controlFrames = []frames.Frame{ccf}
return p.packPacket(nil, leastUnacked, nil)
}
// RetransmitNonForwardSecurePacket retransmits a handshake packet, that was sent with less than forward-secure encryption
func (p *packetPacker) RetransmitNonForwardSecurePacket(stopWaitingFrame *frames.StopWaitingFrame, packet *ackhandler.Packet) (*packedPacket, error) {
if packet.EncryptionLevel == protocol.EncryptionForwardSecure {
return nil, errors.New("PacketPacker BUG: forward-secure encrypted handshake packets don't need special treatment")
}
if stopWaitingFrame == nil {
return nil, errors.New("PacketPacker BUG: Handshake retransmissions must contain a StopWaitingFrame")
}
return p.packPacket(stopWaitingFrame, 0, packet)
}
// PackPacket packs a new packet
// the stopWaitingFrame is *guaranteed* to be included in the next packet
// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise
func (p *packetPacker) PackPacket(stopWaitingFrame *frames.StopWaitingFrame, controlFrames []frames.Frame, leastUnacked protocol.PacketNumber) (*packedPacket, error) {
p.controlFrames = append(p.controlFrames, controlFrames...)
return p.packPacket(stopWaitingFrame, leastUnacked, nil)
}
func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, leastUnacked protocol.PacketNumber, packetToRetransmit *ackhandler.Packet) (*packedPacket, error) {
// packetToRetransmit is only set for handshake retransmissions
isHandshakeRetransmission := (packetToRetransmit != nil)
// cryptoSetup needs to be locked here, so that the AEADs are not changed between
// calling DiversificationNonce() and Seal().
p.cryptoSetup.LockForSealing()
defer p.cryptoSetup.UnlockForSealing()
currentPacketNumber := p.packetNumberGenerator.Peek()
packetNumberLen := protocol.GetPacketNumberLengthForPublicHeader(currentPacketNumber, leastUnacked)
responsePublicHeader := &PublicHeader{
ConnectionID: p.connectionID,
PacketNumber: currentPacketNumber,
PacketNumberLen: packetNumberLen,
TruncateConnectionID: p.connectionParameters.TruncateConnectionID(),
}
if p.perspective == protocol.PerspectiveServer {
force := isHandshakeRetransmission && (packetToRetransmit.EncryptionLevel == protocol.EncryptionSecure)
responsePublicHeader.DiversificationNonce = p.cryptoSetup.DiversificationNonce(force)
}
if p.perspective == protocol.PerspectiveClient && !p.cryptoSetup.HandshakeComplete() {
responsePublicHeader.VersionFlag = true
responsePublicHeader.VersionNumber = p.version
}
publicHeaderLength, err := responsePublicHeader.GetLength(p.perspective)
if err != nil {
return nil, err
}
if stopWaitingFrame != nil {
stopWaitingFrame.PacketNumber = currentPacketNumber
stopWaitingFrame.PacketNumberLen = packetNumberLen
}
// we're packing a ConnectionClose, don't add any StreamFrames
var isConnectionClose bool
if len(p.controlFrames) == 1 {
_, isConnectionClose = p.controlFrames[0].(*frames.ConnectionCloseFrame)
}
var payloadFrames []frames.Frame
if isHandshakeRetransmission {
payloadFrames = append(payloadFrames, stopWaitingFrame)
// don't retransmit Acks and StopWaitings
for _, f := range packetToRetransmit.Frames {
switch f.(type) {
case *frames.AckFrame:
continue
case *frames.StopWaitingFrame:
continue
}
payloadFrames = append(payloadFrames, f)
}
} else if isConnectionClose {
payloadFrames = []frames.Frame{p.controlFrames[0]}
} else {
maxSize := protocol.MaxFrameAndPublicHeaderSize - publicHeaderLength
if !p.isForwardSecure {
maxSize -= protocol.NonForwardSecurePacketSizeReduction
}
payloadFrames, err = p.composeNextPacket(stopWaitingFrame, maxSize)
if err != nil {
return nil, err
}
}
// Check if we have enough frames to send
if len(payloadFrames) == 0 {
return nil, nil
}
// Don't send out packets that only contain a StopWaitingFrame
if len(payloadFrames) == 1 && stopWaitingFrame != nil {
return nil, nil
}
raw := getPacketBuffer()
buffer := bytes.NewBuffer(raw)
if err = responsePublicHeader.Write(buffer, p.version, p.perspective); err != nil {
return nil, err
}
payloadStartIndex := buffer.Len()
var hasNonCryptoStreamData bool // does this frame contain any stream frame on a stream > 1
for _, frame := range payloadFrames {
if sf, ok := frame.(*frames.StreamFrame); ok && sf.StreamID != 1 {
hasNonCryptoStreamData = true
}
err = frame.Write(buffer, p.version)
if err != nil {
return nil, err
}
}
if protocol.ByteCount(buffer.Len()+12) > protocol.MaxPacketSize {
return nil, errors.New("PacketPacker BUG: packet too large")
}
raw = raw[0:buffer.Len()]
var encryptionLevel protocol.EncryptionLevel
if isHandshakeRetransmission {
var err error
_, encryptionLevel, err = p.cryptoSetup.SealWith(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex], packetToRetransmit.EncryptionLevel)
if err != nil {
return nil, err
}
} else {
_, encryptionLevel = p.cryptoSetup.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], currentPacketNumber, raw[:payloadStartIndex])
}
raw = raw[0 : buffer.Len()+12]
if hasNonCryptoStreamData && encryptionLevel <= protocol.EncryptionUnencrypted {
return nil, qerr.AttemptToSendUnencryptedStreamData
}
num := p.packetNumberGenerator.Pop()
if num != currentPacketNumber {
return nil, errors.New("PacketPacker BUG: Peeked and Popped packet numbers do not match.")
}
return &packedPacket{
number: currentPacketNumber,
raw: raw,
frames: payloadFrames,
encryptionLevel: encryptionLevel,
}, nil
}
func (p *packetPacker) composeNextPacket(stopWaitingFrame *frames.StopWaitingFrame, maxFrameSize protocol.ByteCount) ([]frames.Frame, error) {
var payloadLength protocol.ByteCount
var payloadFrames []frames.Frame
if stopWaitingFrame != nil {
payloadFrames = append(payloadFrames, stopWaitingFrame)
minLength, err := stopWaitingFrame.MinLength(p.version)
if err != nil {
return nil, err
}
payloadLength += minLength
}
for len(p.controlFrames) > 0 {
frame := p.controlFrames[len(p.controlFrames)-1]
minLength, _ := frame.MinLength(p.version) // controlFrames does not contain any StopWaitingFrames. So it will *never* return an error
if payloadLength+minLength > maxFrameSize {
break
}
payloadFrames = append(payloadFrames, frame)
payloadLength += minLength
p.controlFrames = p.controlFrames[:len(p.controlFrames)-1]
}
if payloadLength > maxFrameSize {
return nil, fmt.Errorf("Packet Packer BUG: packet payload (%d) too large (%d)", payloadLength, maxFrameSize)
}
// temporarily increase the maxFrameSize by 2 bytes
// this leads to a properly sized packet in all cases, since we do all the packet length calculations with StreamFrames that have the DataLen set
// however, for the last StreamFrame in the packet, we can omit the DataLen, thus saving 2 bytes and yielding a packet of exactly the correct size
maxFrameSize += 2
fs := p.streamFramer.PopStreamFrames(maxFrameSize - payloadLength)
if len(fs) != 0 {
fs[len(fs)-1].DataLenPresent = false
}
// TODO: Simplify
for _, f := range fs {
payloadFrames = append(payloadFrames, f)
}
for b := p.streamFramer.PopBlockedFrame(); b != nil; b = p.streamFramer.PopBlockedFrame() {
p.controlFrames = append(p.controlFrames, b)
}
return payloadFrames, nil
}
func (p *packetPacker) QueueControlFrameForNextPacket(f frames.Frame) {
p.controlFrames = append(p.controlFrames, f)
}
func (p *packetPacker) SetForwardSecure() {
p.isForwardSecure = true
}