fix application bug

This commit is contained in:
Jeff
2019-05-13 11:19:18 +08:00
committed by zryfish
parent 996d6fe4c5
commit 5462f51e65
717 changed files with 87703 additions and 53426 deletions

View File

@@ -6,22 +6,70 @@ import (
"github.com/lucas-clemente/quic-go/internal/protocol"
)
var bufferPool sync.Pool
type packetBuffer struct {
Slice []byte
func getPacketBuffer() *[]byte {
return bufferPool.Get().(*[]byte)
// refCount counts how many packets the Slice is used in.
// It doesn't support concurrent use.
// It is > 1 when used for coalesced packet.
refCount int
}
func putPacketBuffer(buf *[]byte) {
if cap(*buf) != int(protocol.MaxReceivePacketSize) {
// Split increases the refCount.
// It must be called when a packet buffer is used for more than one packet,
// e.g. when splitting coalesced packets.
func (b *packetBuffer) Split() {
b.refCount++
}
// Decrement decrements the reference counter.
// It doesn't put the buffer back into the pool.
func (b *packetBuffer) Decrement() {
b.refCount--
if b.refCount < 0 {
panic("negative packetBuffer refCount")
}
}
// MaybeRelease puts the packet buffer back into the pool,
// if the reference counter already reached 0.
func (b *packetBuffer) MaybeRelease() {
// only put the packetBuffer back if it's not used any more
if b.refCount == 0 {
b.putBack()
}
}
// Release puts back the packet buffer into the pool.
// It should be called when processing is definitely finished.
func (b *packetBuffer) Release() {
b.Decrement()
if b.refCount != 0 {
panic("packetBuffer refCount not zero")
}
b.putBack()
}
func (b *packetBuffer) putBack() {
if cap(b.Slice) != int(protocol.MaxReceivePacketSize) {
panic("putPacketBuffer called with packet of wrong size!")
}
bufferPool.Put(buf)
bufferPool.Put(b)
}
var bufferPool sync.Pool
func getPacketBuffer() *packetBuffer {
buf := bufferPool.Get().(*packetBuffer)
buf.refCount = 1
buf.Slice = buf.Slice[:protocol.MaxReceivePacketSize]
return buf
}
func init() {
bufferPool.New = func() interface{} {
b := make([]byte, 0, protocol.MaxReceivePacketSize)
return &b
return &packetBuffer{
Slice: make([]byte, 0, protocol.MaxReceivePacketSize),
}
}
}

View File

@@ -1,20 +1,16 @@
package quic
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"sync"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/handshake"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
"github.com/lucas-clemente/quic-go/qerr"
)
type client struct {
@@ -27,24 +23,22 @@ type client struct {
packetHandlers packetHandlerManager
token []byte
versionNegotiated bool // has the server accepted our version
versionNegotiated utils.AtomicBool // has the server accepted our version
receivedVersionNegotiationPacket bool
negotiatedVersions []protocol.VersionNumber // the list of versions from the version negotiation packet
tlsConf *tls.Config
mintConf *mint.Config
config *Config
tlsConf *tls.Config
config *Config
srcConnID protocol.ConnectionID
destConnID protocol.ConnectionID
initialPacketNumber protocol.PacketNumber
initialVersion protocol.VersionNumber
version protocol.VersionNumber
handshakeChan chan struct{}
closeCallback func(protocol.ConnectionID)
session quicSession
@@ -57,11 +51,10 @@ var (
// make it possible to mock connection ID generation in the tests
generateConnectionID = protocol.GenerateConnectionID
generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial
errCloseSessionForNewVersion = errors.New("closing session in order to recreate it with a new version")
errCloseSessionForRetry = errors.New("closing session in response to a stateless retry")
)
// DialAddr establishes a new QUIC connection to a server.
// It uses a new UDP connection and closes this connection when the QUIC session is closed.
// The hostname for SNI is taken from the given address.
func DialAddr(
addr string,
@@ -72,7 +65,7 @@ func DialAddr(
}
// DialAddrContext establishes a new QUIC connection to a server using the provided context.
// The hostname for SNI is taken from the given address.
// See DialAddr for details.
func DialAddrContext(
ctx context.Context,
addr string,
@@ -91,6 +84,8 @@ func DialAddrContext(
}
// Dial establishes a new QUIC connection to a server using a net.PacketConn.
// The same PacketConn can be used for multiple calls to Dial and Listen,
// QUIC connection IDs are used for demultiplexing the different connections.
// The host parameter is used for SNI.
func Dial(
pconn net.PacketConn,
@@ -103,7 +98,7 @@ func Dial(
}
// DialContext establishes a new QUIC connection to a server using a net.PacketConn using the provided context.
// The host parameter is used for SNI.
// See Dial for details.
func DialContext(
ctx context.Context,
pconn net.PacketConn,
@@ -125,18 +120,11 @@ func dialContext(
createdPacketConn bool,
) (Session, error) {
config = populateClientConfig(config, createdPacketConn)
if !createdPacketConn {
for _, v := range config.Versions {
if v == protocol.Version44 {
return nil, errors.New("Cannot multiplex connections using gQUIC 44, see https://groups.google.com/a/chromium.org/forum/#!topic/proto-quic/pE9NlLLjizE. Please disable gQUIC 44 in the quic.Config, or use DialAddr")
}
}
}
packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength)
packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength, config.StatelessResetKey)
if err != nil {
return nil, err
}
c, err := newClient(pconn, remoteAddr, config, tlsConf, host, packetHandlers.Remove, createdPacketConn)
c, err := newClient(pconn, remoteAddr, config, tlsConf, host, createdPacketConn)
if err != nil {
return nil, err
}
@@ -153,7 +141,6 @@ func newClient(
config *Config,
tlsConf *tls.Config,
host string,
closeCallback func(protocol.ConnectionID),
createdPacketConn bool,
) (*client, error) {
if tlsConf == nil {
@@ -175,21 +162,27 @@ func newClient(
}
}
}
onClose := func(protocol.ConnectionID) {}
if closeCallback != nil {
onClose = closeCallback
srcConnID, err := generateConnectionID(config.ConnectionIDLength)
if err != nil {
return nil, err
}
destConnID, err := generateConnectionIDForInitial()
if err != nil {
return nil, err
}
c := &client{
srcConnID: srcConnID,
destConnID: destConnID,
conn: &conn{pconn: pconn, currentAddr: remoteAddr},
createdPacketConn: createdPacketConn,
tlsConf: tlsConf,
config: config,
version: config.Versions[0],
handshakeChan: make(chan struct{}),
closeCallback: onClose,
logger: utils.DefaultLogger.WithPrefix("client"),
}
return c, c.generateConnectionIDs()
return c, nil
}
// populateClientConfig populates fields in the quic.Config with their default values, if none are set
@@ -214,11 +207,11 @@ func populateClientConfig(config *Config, createdPacketConn bool) *Config {
maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow
if maxReceiveStreamFlowControlWindow == 0 {
maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindowClient
maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindow
}
maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow
if maxReceiveConnectionFlowControlWindow == 0 {
maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindowClient
maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindow
}
maxIncomingStreams := config.MaxIncomingStreams
if maxIncomingStreams == 0 {
@@ -236,96 +229,29 @@ func populateClientConfig(config *Config, createdPacketConn bool) *Config {
if connIDLen == 0 && !createdPacketConn {
connIDLen = protocol.DefaultConnectionIDLength
}
for _, v := range versions {
if v == protocol.Version44 {
connIDLen = 0
}
}
return &Config{
Versions: versions,
HandshakeTimeout: handshakeTimeout,
IdleTimeout: idleTimeout,
RequestConnectionIDOmission: config.RequestConnectionIDOmission,
ConnectionIDLength: connIDLen,
MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow,
MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,
MaxIncomingStreams: maxIncomingStreams,
MaxIncomingUniStreams: maxIncomingUniStreams,
KeepAlive: config.KeepAlive,
StatelessResetKey: config.StatelessResetKey,
}
}
func (c *client) generateConnectionIDs() error {
connIDLen := protocol.ConnectionIDLenGQUIC
if c.version.UsesTLS() {
connIDLen = c.config.ConnectionIDLength
}
srcConnID, err := generateConnectionID(connIDLen)
if err != nil {
return err
}
destConnID := srcConnID
if c.version.UsesTLS() {
destConnID, err = generateConnectionIDForInitial()
if err != nil {
return err
}
}
c.srcConnID = srcConnID
c.destConnID = destConnID
if c.version == protocol.Version44 {
c.srcConnID = nil
}
return nil
}
func (c *client) dial(ctx context.Context) error {
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
var err error
if c.version.UsesTLS() {
err = c.dialTLS(ctx)
} else {
err = c.dialGQUIC(ctx)
}
return err
}
func (c *client) dialGQUIC(ctx context.Context) error {
if err := c.createNewGQUICSession(); err != nil {
if err := c.createNewTLSSession(c.version); err != nil {
return err
}
err := c.establishSecureConnection(ctx)
if err == errCloseSessionForNewVersion {
return c.dial(ctx)
}
return err
}
func (c *client) dialTLS(ctx context.Context) error {
params := &handshake.TransportParameters{
StreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
IdleTimeout: c.config.IdleTimeout,
OmitConnectionID: c.config.RequestConnectionIDOmission,
MaxBidiStreams: uint16(c.config.MaxIncomingStreams),
MaxUniStreams: uint16(c.config.MaxIncomingUniStreams),
DisableMigration: true,
}
extHandler := handshake.NewExtensionHandlerClient(params, c.initialVersion, c.config.Versions, c.version, c.logger)
mintConf, err := tlsToMintConfig(c.tlsConf, protocol.PerspectiveClient)
if err != nil {
return err
}
mintConf.ExtensionHandler = extHandler
c.mintConf = mintConf
if err := c.createNewTLSSession(extHandler.GetPeerParams(), c.version); err != nil {
return err
}
err = c.establishSecureConnection(ctx)
if err == errCloseSessionForRetry || err == errCloseSessionForNewVersion {
if err == errCloseForRecreating {
return c.dial(ctx)
}
return err
@@ -333,17 +259,16 @@ func (c *client) dialTLS(ctx context.Context) error {
// establishSecureConnection runs the session, and tries to establish a secure connection
// It returns:
// - errCloseSessionForNewVersion when the server sends a version negotiation packet
// - handshake.ErrCloseSessionForRetry when the server performs a stateless retry (for IETF QUIC)
// - errCloseForRecreating when the server sends a version negotiation packet
// - any other error that might occur
// - when the connection is secure (for gQUIC), or forward-secure (for IETF QUIC)
// - when the connection is forward-secure
func (c *client) establishSecureConnection(ctx context.Context) error {
errorChan := make(chan error, 1)
go func() {
err := c.session.run() // returns as soon as the session is closed
if err != errCloseSessionForRetry && err != errCloseSessionForNewVersion && c.createdPacketConn {
c.conn.Close()
if err != errCloseForRecreating && c.createdPacketConn {
c.packetHandlers.Close()
}
errorChan <- err
}()
@@ -362,102 +287,50 @@ func (c *client) establishSecureConnection(ctx context.Context) error {
}
func (c *client) handlePacket(p *receivedPacket) {
if err := c.handlePacketImpl(p); err != nil {
c.logger.Errorf("error handling packet: %s", err)
}
}
func (c *client) handlePacketImpl(p *receivedPacket) error {
c.mutex.Lock()
defer c.mutex.Unlock()
// handle Version Negotiation Packets
if p.header.IsVersionNegotiation {
err := c.handleVersionNegotiationPacket(p.header)
if err != nil {
c.session.destroy(err)
}
// version negotiation packets have no payload
return err
}
if !c.version.UsesIETFHeaderFormat() {
connID := p.header.DestConnectionID
// reject packets with truncated connection id if we didn't request truncation
if !c.config.RequestConnectionIDOmission && connID.Len() == 0 {
return errors.New("received packet with truncated connection ID, but didn't request truncation")
}
// reject packets with the wrong connection ID
if connID.Len() > 0 && !connID.Equal(c.srcConnID) {
return fmt.Errorf("received a packet with an unexpected connection ID (%s, expected %s)", connID, c.srcConnID)
}
if p.header.ResetFlag {
return c.handlePublicReset(p)
}
} else {
// reject packets with the wrong connection ID
if !p.header.DestConnectionID.Equal(c.srcConnID) {
return fmt.Errorf("received a packet with an unexpected connection ID (%s, expected %s)", p.header.DestConnectionID, c.srcConnID)
}
}
if p.header.IsLongHeader {
switch p.header.Type {
case protocol.PacketTypeRetry:
c.handleRetryPacket(p.header)
return nil
case protocol.PacketTypeHandshake, protocol.PacketType0RTT:
default:
return fmt.Errorf("Received unsupported packet type: %s", p.header.Type)
}
if wire.IsVersionNegotiationPacket(p.data) {
go c.handleVersionNegotiationPacket(p)
return
}
// this is the first packet we are receiving
// since it is not a Version Negotiation Packet, this means the server supports the suggested version
if !c.versionNegotiated {
c.versionNegotiated = true
if !c.versionNegotiated.Get() {
c.versionNegotiated.Set(true)
}
c.session.handlePacket(p)
return nil
}
func (c *client) handlePublicReset(p *receivedPacket) error {
cr := c.conn.RemoteAddr()
// check if the remote address and the connection ID match
// otherwise this might be an attacker trying to inject a PUBLIC_RESET to kill the connection
if cr.Network() != p.remoteAddr.Network() || cr.String() != p.remoteAddr.String() || !p.header.DestConnectionID.Equal(c.srcConnID) {
return errors.New("Received a spoofed Public Reset")
}
pr, err := wire.ParsePublicReset(bytes.NewReader(p.data))
func (c *client) handleVersionNegotiationPacket(p *receivedPacket) {
c.mutex.Lock()
defer c.mutex.Unlock()
hdr, _, _, err := wire.ParsePacket(p.data, 0)
if err != nil {
return fmt.Errorf("Received a Public Reset. An error occurred parsing the packet: %s", err)
c.logger.Debugf("Error parsing Version Negotiation packet: %s", err)
return
}
c.session.closeRemote(qerr.Error(qerr.PublicReset, fmt.Sprintf("Received a Public Reset for packet number %#x", pr.RejectedPacketNumber)))
c.logger.Infof("Received Public Reset, rejected packet number: %#x", pr.RejectedPacketNumber)
return nil
}
func (c *client) handleVersionNegotiationPacket(hdr *wire.Header) error {
// ignore delayed / duplicated version negotiation packets
if c.receivedVersionNegotiationPacket || c.versionNegotiated {
c.logger.Debugf("Received a delayed Version Negotiation Packet.")
return nil
if c.receivedVersionNegotiationPacket || c.versionNegotiated.Get() {
c.logger.Debugf("Received a delayed Version Negotiation packet.")
return
}
for _, v := range hdr.SupportedVersions {
if v == c.version {
// the version negotiation packet contains the version that we offered
// this might be a packet sent by an attacker (or by a terribly broken server implementation)
// ignore it
return nil
// The Version Negotiation packet contains the version that we offered.
// This might be a packet sent by an attacker (or by a terribly broken server implementation).
return
}
}
c.logger.Infof("Received a Version Negotiation Packet. Supported Versions: %s", hdr.SupportedVersions)
c.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", hdr.SupportedVersions)
newVersion, ok := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions)
if !ok {
return qerr.InvalidVersion
c.session.destroy(fmt.Errorf("No compatible QUIC version found. We support %s, server offered %s", c.config.Versions, hdr.SupportedVersions))
c.logger.Debugf("No compatible QUIC version found.")
return
}
c.receivedVersionNegotiationPacket = true
c.negotiatedVersions = hdr.SupportedVersions
@@ -465,87 +338,40 @@ func (c *client) handleVersionNegotiationPacket(hdr *wire.Header) error {
// switch to negotiated version
c.initialVersion = c.version
c.version = newVersion
if err := c.generateConnectionIDs(); err != nil {
return err
}
c.logger.Infof("Switching to QUIC version %s. New connection ID: %s", newVersion, c.destConnID)
c.session.destroy(errCloseSessionForNewVersion)
return nil
c.initialPacketNumber = c.session.closeForRecreating()
}
func (c *client) handleRetryPacket(hdr *wire.Header) {
c.logger.Debugf("<- Received Retry")
hdr.Log(c.logger)
if !hdr.OrigDestConnectionID.Equal(c.destConnID) {
c.logger.Debugf("Ignoring spoofed Retry. Original Destination Connection ID: %s, expected: %s", hdr.OrigDestConnectionID, c.destConnID)
return
func (c *client) createNewTLSSession(version protocol.VersionNumber) error {
params := &handshake.TransportParameters{
InitialMaxStreamDataBidiRemote: protocol.InitialMaxStreamData,
InitialMaxStreamDataBidiLocal: protocol.InitialMaxStreamData,
InitialMaxStreamDataUni: protocol.InitialMaxStreamData,
InitialMaxData: protocol.InitialMaxData,
IdleTimeout: c.config.IdleTimeout,
MaxBidiStreams: uint64(c.config.MaxIncomingStreams),
MaxUniStreams: uint64(c.config.MaxIncomingUniStreams),
AckDelayExponent: protocol.AckDelayExponent,
DisableMigration: true,
}
if hdr.SrcConnectionID.Equal(c.destConnID) {
c.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.")
return
}
// If a token is already set, this means that we already received a Retry from the server.
// Ignore this Retry packet.
if len(c.token) > 0 {
c.logger.Debugf("Ignoring Retry, since a Retry was already received.")
return
}
c.destConnID = hdr.SrcConnectionID
c.token = hdr.Token
c.session.destroy(errCloseSessionForRetry)
}
func (c *client) createNewGQUICSession() error {
c.mutex.Lock()
defer c.mutex.Unlock()
runner := &runner{
packetHandlerManager: c.packetHandlers,
onHandshakeCompleteImpl: func(_ Session) { close(c.handshakeChan) },
removeConnectionIDImpl: c.closeCallback,
}
sess, err := newClientSession(
c.conn,
runner,
c.version,
c.destConnID,
c.srcConnID,
c.config,
c.tlsConf,
c.config,
c.initialPacketNumber,
params,
c.initialVersion,
c.negotiatedVersions,
c.logger,
)
if err != nil {
return err
}
c.session = sess
c.packetHandlers.Add(c.srcConnID, c)
if c.config.RequestConnectionIDOmission {
c.packetHandlers.Add(protocol.ConnectionID{}, c)
}
return nil
}
func (c *client) createNewTLSSession(
paramsChan <-chan handshake.TransportParameters,
version protocol.VersionNumber,
) error {
c.mutex.Lock()
defer c.mutex.Unlock()
runner := &runner{
onHandshakeCompleteImpl: func(_ Session) { close(c.handshakeChan) },
removeConnectionIDImpl: c.closeCallback,
}
sess, err := newTLSClientSession(
c.conn,
runner,
c.token,
c.destConnID,
c.srcConnID,
c.config,
c.mintConf,
paramsChan,
1,
c.logger,
c.version,
)
@@ -582,6 +408,6 @@ func (c *client) GetVersion() protocol.VersionNumber {
return v
}
func (c *client) GetPerspective() protocol.Perspective {
func (c *client) getPerspective() protocol.Perspective {
return protocol.PerspectiveClient
}

View File

@@ -1,42 +1,135 @@
package quic
import (
"errors"
"fmt"
"io"
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
)
type cryptoStream interface {
StreamID() protocol.StreamID
io.Reader
// for receiving data
HandleCryptoFrame(*wire.CryptoFrame) error
GetCryptoData() []byte
Finish() error
// for sending data
io.Writer
handleStreamFrame(*wire.StreamFrame) error
hasData() bool
popStreamFrame(protocol.ByteCount) (*wire.StreamFrame, bool)
closeForShutdown(error)
setReadOffset(protocol.ByteCount)
// methods needed for flow control
getWindowUpdate() protocol.ByteCount
handleMaxStreamDataFrame(*wire.MaxStreamDataFrame)
HasData() bool
PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame
}
type postHandshakeCryptoStream struct {
cryptoStream
framer framer
}
func newPostHandshakeCryptoStream(framer framer) cryptoStream {
return &postHandshakeCryptoStream{
cryptoStream: newCryptoStream(),
framer: framer,
}
}
// Write writes post-handshake messages.
// For simplicity, post-handshake crypto messages are treated as control frames.
// The framer functions as a stack (LIFO), so if there are multiple writes,
// they will be returned in the opposite order.
// This is acceptable, since post-handshake crypto messages are very rare.
func (s *postHandshakeCryptoStream) Write(p []byte) (int, error) {
n, err := s.cryptoStream.Write(p)
if err != nil {
return n, err
}
for s.cryptoStream.HasData() {
s.framer.QueueControlFrame(s.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
}
return n, nil
}
type cryptoStreamImpl struct {
*stream
queue *frameSorter
msgBuf []byte
highestOffset protocol.ByteCount
finished bool
writeOffset protocol.ByteCount
writeBuf []byte
}
var _ cryptoStream = &cryptoStreamImpl{}
func newCryptoStream(sender streamSender, flowController flowcontrol.StreamFlowController, version protocol.VersionNumber) cryptoStream {
str := newStream(version.CryptoStreamID(), sender, flowController, version)
return &cryptoStreamImpl{str}
func newCryptoStream() cryptoStream {
return &cryptoStreamImpl{queue: newFrameSorter()}
}
// SetReadOffset sets the read offset.
// It is only needed for the crypto stream.
// It must not be called concurrently with any other stream methods, especially Read and Write.
func (s *cryptoStreamImpl) setReadOffset(offset protocol.ByteCount) {
s.receiveStream.readOffset = offset
s.receiveStream.frameQueue.readPos = offset
func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
highestOffset := f.Offset + protocol.ByteCount(len(f.Data))
if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset {
return fmt.Errorf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset)
}
if s.finished {
if highestOffset > s.highestOffset {
// reject crypto data received after this stream was already finished
return errors.New("received crypto data after change of encryption level")
}
// ignore data with a smaller offset than the highest received
// could e.g. be a retransmission
return nil
}
s.highestOffset = utils.MaxByteCount(s.highestOffset, highestOffset)
if err := s.queue.Push(f.Data, f.Offset); err != nil {
return err
}
for {
_, data := s.queue.Pop()
if data == nil {
return nil
}
s.msgBuf = append(s.msgBuf, data...)
}
}
// GetCryptoData retrieves data that was received in CRYPTO frames
func (s *cryptoStreamImpl) GetCryptoData() []byte {
if len(s.msgBuf) < 4 {
return nil
}
msgLen := 4 + int(s.msgBuf[1])<<16 + int(s.msgBuf[2])<<8 + int(s.msgBuf[3])
if len(s.msgBuf) < msgLen {
return nil
}
msg := make([]byte, msgLen)
copy(msg, s.msgBuf[:msgLen])
s.msgBuf = s.msgBuf[msgLen:]
return msg
}
func (s *cryptoStreamImpl) Finish() error {
if s.queue.HasMoreData() {
return errors.New("encryption level changed, but crypto stream has more data to read")
}
s.finished = true
return nil
}
// Writes writes data that should be sent out in CRYPTO frames
func (s *cryptoStreamImpl) Write(p []byte) (int, error) {
s.writeBuf = append(s.writeBuf, p...)
return len(p), nil
}
func (s *cryptoStreamImpl) HasData() bool {
return len(s.writeBuf) > 0
}
func (s *cryptoStreamImpl) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame {
f := &wire.CryptoFrame{Offset: s.writeOffset}
n := utils.MinByteCount(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf)))
f.Data = s.writeBuf[:n]
s.writeBuf = s.writeBuf[n:]
s.writeOffset += n
return f
}

View File

@@ -0,0 +1,60 @@
package quic
import (
"fmt"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
)
type cryptoDataHandler interface {
HandleMessage([]byte, protocol.EncryptionLevel) bool
}
type cryptoStreamManager struct {
cryptoHandler cryptoDataHandler
initialStream cryptoStream
handshakeStream cryptoStream
oneRTTStream cryptoStream
}
func newCryptoStreamManager(
cryptoHandler cryptoDataHandler,
initialStream cryptoStream,
handshakeStream cryptoStream,
oneRTTStream cryptoStream,
) *cryptoStreamManager {
return &cryptoStreamManager{
cryptoHandler: cryptoHandler,
initialStream: initialStream,
handshakeStream: handshakeStream,
oneRTTStream: oneRTTStream,
}
}
func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) (bool /* encryption level changed */, error) {
var str cryptoStream
switch encLevel {
case protocol.EncryptionInitial:
str = m.initialStream
case protocol.EncryptionHandshake:
str = m.handshakeStream
case protocol.Encryption1RTT:
str = m.oneRTTStream
default:
return false, fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel)
}
if err := str.HandleCryptoFrame(frame); err != nil {
return false, err
}
for {
data := str.GetCryptoData()
if data == nil {
return false, nil
}
if encLevelFinished := m.cryptoHandler.HandleMessage(data, encLevel); encLevelFinished {
return true, str.Finish()
}
}
}

View File

@@ -8,36 +8,31 @@ import (
)
type frameSorter struct {
queue map[protocol.ByteCount][]byte
readPos protocol.ByteCount
finalOffset protocol.ByteCount
gaps *utils.ByteIntervalList
queue map[protocol.ByteCount][]byte
readPos protocol.ByteCount
gaps *utils.ByteIntervalList
}
var errDuplicateStreamData = errors.New("Duplicate Stream Data")
func newFrameSorter() *frameSorter {
s := frameSorter{
gaps: utils.NewByteIntervalList(),
queue: make(map[protocol.ByteCount][]byte),
finalOffset: protocol.MaxByteCount,
gaps: utils.NewByteIntervalList(),
queue: make(map[protocol.ByteCount][]byte),
}
s.gaps.PushFront(utils.ByteInterval{Start: 0, End: protocol.MaxByteCount})
return &s
}
func (s *frameSorter) Push(data []byte, offset protocol.ByteCount, fin bool) error {
err := s.push(data, offset, fin)
func (s *frameSorter) Push(data []byte, offset protocol.ByteCount) error {
err := s.push(data, offset)
if err == errDuplicateStreamData {
return nil
}
return err
}
func (s *frameSorter) push(data []byte, offset protocol.ByteCount, fin bool) error {
if fin {
s.finalOffset = offset + protocol.ByteCount(len(data))
}
func (s *frameSorter) push(data []byte, offset protocol.ByteCount) error {
if len(data) == 0 {
return nil
}
@@ -147,12 +142,18 @@ func (s *frameSorter) push(data []byte, offset protocol.ByteCount, fin bool) err
return nil
}
func (s *frameSorter) Pop() ([]byte /* data */, bool /* fin */) {
func (s *frameSorter) Pop() (protocol.ByteCount, []byte) {
data, ok := s.queue[s.readPos]
if !ok {
return nil, s.readPos >= s.finalOffset
return s.readPos, nil
}
delete(s.queue, s.readPos)
offset := s.readPos
s.readPos += protocol.ByteCount(len(data))
return data, s.readPos >= s.finalOffset
return offset, data
}
// HasMoreData says if there is any more data queued at *any* offset.
func (s *frameSorter) HasMoreData() bool {
return len(s.queue) > 0
}

View File

@@ -7,39 +7,47 @@ import (
"github.com/lucas-clemente/quic-go/internal/wire"
)
type framer struct {
type framer interface {
QueueControlFrame(wire.Frame)
AppendControlFrames([]wire.Frame, protocol.ByteCount) ([]wire.Frame, protocol.ByteCount)
AddActiveStream(protocol.StreamID)
AppendStreamFrames([]wire.Frame, protocol.ByteCount) []wire.Frame
}
type framerI struct {
mutex sync.Mutex
streamGetter streamGetter
cryptoStream cryptoStream
version protocol.VersionNumber
streamQueueMutex sync.Mutex
activeStreams map[protocol.StreamID]struct{}
streamQueue []protocol.StreamID
activeStreams map[protocol.StreamID]struct{}
streamQueue []protocol.StreamID
controlFrameMutex sync.Mutex
controlFrames []wire.Frame
}
var _ framer = &framerI{}
func newFramer(
cryptoStream cryptoStream,
streamGetter streamGetter,
v protocol.VersionNumber,
) *framer {
return &framer{
) framer {
return &framerI{
streamGetter: streamGetter,
cryptoStream: cryptoStream,
activeStreams: make(map[protocol.StreamID]struct{}),
version: v,
}
}
func (f *framer) QueueControlFrame(frame wire.Frame) {
func (f *framerI) QueueControlFrame(frame wire.Frame) {
f.controlFrameMutex.Lock()
f.controlFrames = append(f.controlFrames, frame)
f.controlFrameMutex.Unlock()
}
func (f *framer) AppendControlFrames(frames []wire.Frame, maxLen protocol.ByteCount) ([]wire.Frame, protocol.ByteCount) {
func (f *framerI) AppendControlFrames(frames []wire.Frame, maxLen protocol.ByteCount) ([]wire.Frame, protocol.ByteCount) {
var length protocol.ByteCount
f.controlFrameMutex.Lock()
for len(f.controlFrames) > 0 {
@@ -56,20 +64,18 @@ func (f *framer) AppendControlFrames(frames []wire.Frame, maxLen protocol.ByteCo
return frames, length
}
// AddActiveStream adds a stream that has data to write.
// It should not be used for the crypto stream.
func (f *framer) AddActiveStream(id protocol.StreamID) {
f.streamQueueMutex.Lock()
func (f *framerI) AddActiveStream(id protocol.StreamID) {
f.mutex.Lock()
if _, ok := f.activeStreams[id]; !ok {
f.streamQueue = append(f.streamQueue, id)
f.activeStreams[id] = struct{}{}
}
f.streamQueueMutex.Unlock()
f.mutex.Unlock()
}
func (f *framer) AppendStreamFrames(frames []wire.Frame, maxLen protocol.ByteCount) []wire.Frame {
func (f *framerI) AppendStreamFrames(frames []wire.Frame, maxLen protocol.ByteCount) []wire.Frame {
var length protocol.ByteCount
f.streamQueueMutex.Lock()
f.mutex.Lock()
// pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet
numActiveStreams := len(f.streamQueue)
for i := 0; i < numActiveStreams; i++ {
@@ -98,6 +104,6 @@ func (f *framer) AppendStreamFrames(frames []wire.Frame, maxLen protocol.ByteCou
frames = append(frames, frame)
length += frame.Length(f.version)
}
f.streamQueueMutex.Unlock()
f.mutex.Unlock()
return frames
}

View File

@@ -16,8 +16,8 @@ import (
quic "github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
type roundTripperOpts struct {
@@ -52,10 +52,7 @@ type client struct {
var _ http.RoundTripper = &client{}
var defaultQuicConfig = &quic.Config{
RequestConnectionIDOmission: true,
KeepAlive: true,
}
var defaultQuicConfig = &quic.Config{KeepAlive: true}
// newClient creates a new client
func newClient(
@@ -94,7 +91,7 @@ func (c *client) dial() error {
}
// once the version has been negotiated, open the header stream
c.headerStream, err = c.session.OpenStream()
c.headerStream, err = c.session.OpenStreamSync()
if err != nil {
return err
}
@@ -111,10 +108,10 @@ func (c *client) handleHeaderStream() {
for err == nil {
err = c.readResponse(h2framer, decoder)
}
if quicErr, ok := err.(*qerr.QuicError); !ok || quicErr.ErrorCode != qerr.PeerGoingAway {
if quicErr, ok := err.(*qerr.QuicError); !ok || quicErr.ErrorCode != qerr.NoError {
c.logger.Debugf("Error handling header stream: %s", err)
}
c.headerErr = qerr.Error(qerr.InvalidHeadersStreamData, err.Error())
c.headerErr = qerr.Error(qerr.InternalError, err.Error())
// stop all running request
close(c.headerErrored)
}
@@ -244,7 +241,7 @@ func (c *client) RoundTrip(req *http.Request) (*http.Response, error) {
if streamEnded || isHead {
res.Body = noBody
} else {
res.Body = dataStream
res.Body = &responseBody{dataStream}
if requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
res.Header.Del("Content-Encoding")
res.Header.Del("Content-Length")

View File

@@ -0,0 +1,18 @@
package h2quic
import (
"io"
quic "github.com/lucas-clemente/quic-go"
)
type responseBody struct {
quic.Stream
}
var _ io.ReadCloser = &responseBody{}
func (rb *responseBody) Close() error {
rb.Stream.CancelRead(0)
return nil
}

View File

@@ -14,8 +14,8 @@ import (
quic "github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
)
@@ -127,7 +127,7 @@ func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error {
func (s *Server) handleHeaderStream(session streamCreator) {
stream, err := session.AcceptStream()
if err != nil {
session.CloseWithError(quic.ErrorCode(qerr.InvalidHeadersStreamData), err)
session.CloseWithError(quic.ErrorCode(qerr.InternalError), err)
return
}
@@ -154,7 +154,7 @@ func (s *Server) handleHeaderStream(session streamCreator) {
func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream, headerStreamMutex *sync.Mutex, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error {
h2frame, err := h2framer.ReadFrame()
if err != nil {
return qerr.Error(qerr.HeadersStreamDataDecompressFailure, "cannot read frame")
return qerr.Error(qerr.InternalError, "cannot read frame")
}
var h2headersFrame *http2.HeadersFrame
switch f := h2frame.(type) {
@@ -165,7 +165,7 @@ func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream,
case *http2.HeadersFrame:
h2headersFrame = f
default:
return qerr.Error(qerr.InvalidHeadersStreamData, "expected a header frame")
return qerr.Error(qerr.ProtocolViolation, "expected a header frame")
}
if !h2headersFrame.HeadersEnded() {

View File

@@ -2,11 +2,11 @@ package quic
import (
"context"
"crypto/tls"
"io"
"net"
"time"
"github.com/lucas-clemente/quic-go/internal/handshake"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
@@ -16,20 +16,11 @@ type StreamID = protocol.StreamID
// A VersionNumber is a QUIC version number.
type VersionNumber = protocol.VersionNumber
const (
// VersionGQUIC39 is gQUIC version 39.
VersionGQUIC39 = protocol.Version39
// VersionGQUIC43 is gQUIC version 43.
VersionGQUIC43 = protocol.Version43
// VersionGQUIC44 is gQUIC version 44.
VersionGQUIC44 = protocol.Version44
)
// A Cookie can be used to verify the ownership of the client address.
type Cookie = handshake.Cookie
// ConnectionState records basic details about the QUIC connection.
type ConnectionState = handshake.ConnectionState
type Cookie struct {
RemoteAddr string
SentTime time.Time
}
// An ErrorCode is an application-defined error code.
type ErrorCode = protocol.ApplicationErrorCode
@@ -43,12 +34,16 @@ type Stream interface {
// after a fixed time limit; see SetDeadline and SetReadDeadline.
// If the stream was canceled by the peer, the error implements the StreamError
// interface, and Canceled() == true.
// If the session was closed due to a timeout, the error satisfies
// the net.Error interface, and Timeout() will be true.
io.Reader
// Write writes data to the stream.
// Write can be made to time out and return a net.Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
// If the stream was canceled by the peer, the error implements the StreamError
// interface, and Canceled() == true.
// If the session was closed due to a timeout, the error satisfies
// the net.Error interface, and Timeout() will be true.
io.Writer
// Close closes the write-direction of the stream.
// Future calls to Write are not permitted after calling Close.
@@ -56,16 +51,18 @@ type Stream interface {
// It must not be called after calling CancelWrite.
io.Closer
// CancelWrite aborts sending on this stream.
// It must not be called after Close.
// Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably.
// Write will unblock immediately, and future calls to Write will fail.
CancelWrite(ErrorCode) error
// When called multiple times or after closing the stream it is a no-op.
CancelWrite(ErrorCode)
// CancelRead aborts receiving on this stream.
// It will ask the peer to stop transmitting stream data.
// Read will unblock immediately, and future Read calls will fail.
CancelRead(ErrorCode) error
// When called multiple times or after reading the io.EOF it is a no-op.
CancelRead(ErrorCode)
// The context is canceled as soon as the write-side of the stream is closed.
// This happens when Close() is called, or when the stream is reset (either locally or remotely).
// This happens when Close() or CancelWrite() is called, or when the peer
// cancels the read-side of their stream.
// Warning: This API should not be considered stable and might change soon.
Context() context.Context
// SetReadDeadline sets the deadline for future Read calls and
@@ -91,7 +88,7 @@ type ReceiveStream interface {
// see Stream.Read
io.Reader
// see Stream.CancelRead
CancelRead(ErrorCode) error
CancelRead(ErrorCode)
// see Stream.SetReadDealine
SetReadDeadline(t time.Time) error
}
@@ -105,7 +102,7 @@ type SendStream interface {
// see Stream.Close
io.Closer
// see Stream.CancelWrite
CancelWrite(ErrorCode) error
CancelWrite(ErrorCode)
// see Stream.Context
Context() context.Context
// see Stream.SetWriteDeadline
@@ -122,24 +119,34 @@ type StreamError interface {
// A Session is a QUIC connection between two peers.
type Session interface {
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
// If the session was closed due to a timeout, the error satisfies
// the net.Error interface, and Timeout() will be true.
AcceptStream() (Stream, error)
// AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available.
// If the session was closed due to a timeout, the error satisfies
// the net.Error interface, and Timeout() will be true.
AcceptUniStream() (ReceiveStream, error)
// OpenStream opens a new bidirectional QUIC stream.
// It returns a special error when the peer's concurrent stream limit is reached.
// There is no signaling to the peer about new streams:
// The peer can only accept the stream after data has been sent on the stream.
// TODO(#1152): Enable testing for the special error
// If the error is non-nil, it satisfies the net.Error interface.
// When reaching the peer's stream limit, err.Temporary() will be true.
// If the session was closed due to a timeout, Timeout() will be true.
OpenStream() (Stream, error)
// OpenStreamSync opens a new bidirectional QUIC stream.
// It blocks until the peer's concurrent stream limit allows a new stream to be opened.
// It blocks until a new stream can be opened.
// If the error is non-nil, it satisfies the net.Error interface.
// If the session was closed due to a timeout, Timeout() will be true.
OpenStreamSync() (Stream, error)
// OpenUniStream opens a new outgoing unidirectional QUIC stream.
// It returns a special error when the peer's concurrent stream limit is reached.
// TODO(#1152): Enable testing for the special error
// If the error is non-nil, it satisfies the net.Error interface.
// When reaching the peer's stream limit, Temporary() will be true.
// If the session was closed due to a timeout, Timeout() will be true.
OpenUniStream() (SendStream, error)
// OpenUniStreamSync opens a new outgoing unidirectional QUIC stream.
// It blocks until the peer's concurrent stream limit allows a new stream to be opened.
// It blocks until a new stream can be opened.
// If the error is non-nil, it satisfies the net.Error interface.
// If the session was closed due to a timeout, Timeout() will be true.
OpenUniStreamSync() (SendStream, error)
// LocalAddr returns the local address.
LocalAddr() net.Addr
@@ -155,7 +162,7 @@ type Session interface {
Context() context.Context
// ConnectionState returns basic details about the QUIC connection.
// Warning: This API should not be considered stable and might change soon.
ConnectionState() ConnectionState
ConnectionState() tls.ConnectionState
}
// Config contains all configuration data needed for a QUIC server or client.
@@ -164,11 +171,7 @@ type Config struct {
// If not set, it uses all versions available.
// Warning: This API should not be considered stable and will change soon.
Versions []VersionNumber
// Ask the server to omit the connection ID sent in the Public Header.
// This saves 8 bytes in the Public Header in every packet. However, if the IP address of the server changes, the connection cannot be migrated.
// Currently only valid for the client.
RequestConnectionIDOmission bool
// The length of the connection ID in bytes. Only valid for IETF QUIC.
// The length of the connection ID in bytes.
// It can be 0, or any value between 4 and 18.
// If not set, the interpretation depends on where the Config is used:
// If used for dialing an address, a 0 byte connection ID will be used.
@@ -198,21 +201,21 @@ type Config struct {
// MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open.
// If not set, it will default to 100.
// If set to a negative value, it doesn't allow any bidirectional streams.
// Values larger than 65535 (math.MaxUint16) are invalid.
MaxIncomingStreams int
// MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open.
// This value doesn't have any effect in Google QUIC.
// If not set, it will default to 100.
// If set to a negative value, it doesn't allow any unidirectional streams.
// Values larger than 65535 (math.MaxUint16) are invalid.
MaxIncomingUniStreams int
// KeepAlive defines whether this peer will periodically send PING frames to keep the connection alive.
// The StatelessResetKey is used to generate stateless reset tokens.
// If no key is configured, sending of stateless resets is disabled.
StatelessResetKey []byte
// KeepAlive defines whether this peer will periodically send a packet to keep the connection alive.
KeepAlive bool
}
// A Listener for incoming QUIC connections
type Listener interface {
// Close the server, sending CONNECTION_CLOSE frames to each peer.
// Close the server. All active sessions will be closed.
Close() error
// Addr returns the local network addr that the server is listening on.
Addr() net.Addr

View File

@@ -14,6 +14,7 @@ type SentPacketHandler interface {
SentPacketsAsRetransmission(packets []*Packet, retransmissionOf protocol.PacketNumber)
ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, recvTime time.Time) error
SetHandshakeComplete()
ResetForRetry() error
// The SendMode determines if and what kind of packets can be sent.
SendMode() SendMode
@@ -27,11 +28,13 @@ type SentPacketHandler interface {
// Before sending any packet, SendingAllowed() must be called to learn if we can actually send it.
ShouldSendNumPackets() int
GetStopWaitingFrame(force bool) *wire.StopWaitingFrame
// only to be called once the handshake is complete
GetLowestPacketNotConfirmedAcked() protocol.PacketNumber
DequeuePacketForRetransmission() *Packet
DequeueProbePacket() (*Packet, error)
GetPacketNumberLen(protocol.PacketNumber) protocol.PacketNumberLen
PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen)
PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber
GetAlarmTimeout() time.Time
OnAlarm() error
@@ -39,9 +42,9 @@ type SentPacketHandler interface {
// ReceivedPacketHandler handles ACKs needed to send for incoming packets
type ReceivedPacketHandler interface {
ReceivedPacket(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck bool) error
ReceivedPacket(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel, rcvTime time.Time, shouldInstigateAck bool) error
IgnoreBelow(protocol.PacketNumber)
GetAlarmTimeout() time.Time
GetAckFrame() *wire.AckFrame
GetAckFrame(protocol.EncryptionLevel) *wire.AckFrame
}

View File

@@ -1,10 +1,11 @@
package quic
package ackhandler
import (
"crypto/rand"
"math"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
)
// The packetNumberGenerator generates the packet number for the next packet
@@ -15,6 +16,8 @@ type packetNumberGenerator struct {
next protocol.PacketNumber
nextToSkip protocol.PacketNumber
history []protocol.PacketNumber
}
func newPacketNumberGenerator(initial, averagePeriod protocol.PacketNumber) *packetNumberGenerator {
@@ -37,6 +40,10 @@ func (p *packetNumberGenerator) Pop() protocol.PacketNumber {
p.next++
if p.next == p.nextToSkip {
if len(p.history)+1 > protocol.MaxTrackedSkippedPackets {
p.history = p.history[1:]
}
p.history = append(p.history, p.next)
p.next++
p.generateNewSkip()
}
@@ -60,3 +67,12 @@ func (p *packetNumberGenerator) getRandomNumber() uint16 {
num := uint16(b[0])<<8 + uint16(b[1])
return num
}
func (p *packetNumberGenerator) Validate(ack *wire.AckFrame) bool {
for _, pn := range p.history {
if ack.AcksPacket(pn) {
return false
}
}
return true
}

View File

@@ -1,6 +1,7 @@
package ackhandler
import (
"fmt"
"time"
"github.com/lucas-clemente/quic-go/internal/congestion"
@@ -9,27 +10,6 @@ import (
"github.com/lucas-clemente/quic-go/internal/wire"
)
type receivedPacketHandler struct {
largestObserved protocol.PacketNumber
ignoreBelow protocol.PacketNumber
largestObservedReceivedTime time.Time
packetHistory *receivedPacketHistory
ackSendDelay time.Duration
rttStats *congestion.RTTStats
packetsReceivedSinceLastAck int
retransmittablePacketsReceivedSinceLastAck int
ackQueued bool
ackAlarm time.Time
lastAck *wire.AckFrame
logger utils.Logger
version protocol.VersionNumber
}
const (
// maximum delay that can be applied to an ACK for a retransmittable packet
ackSendDelay = 25 * time.Millisecond
@@ -53,6 +33,14 @@ const (
maxPacketsAfterNewMissing = 4
)
type receivedPacketHandler struct {
initialPackets *receivedPacketTracker
handshakePackets *receivedPacketTracker
oneRTTPackets *receivedPacketTracker
}
var _ ReceivedPacketHandler = &receivedPacketHandler{}
// NewReceivedPacketHandler creates a new receivedPacketHandler
func NewReceivedPacketHandler(
rttStats *congestion.RTTStats,
@@ -60,156 +48,51 @@ func NewReceivedPacketHandler(
version protocol.VersionNumber,
) ReceivedPacketHandler {
return &receivedPacketHandler{
packetHistory: newReceivedPacketHistory(),
ackSendDelay: ackSendDelay,
rttStats: rttStats,
logger: logger,
version: version,
initialPackets: newReceivedPacketTracker(rttStats, logger, version),
handshakePackets: newReceivedPacketTracker(rttStats, logger, version),
oneRTTPackets: newReceivedPacketTracker(rttStats, logger, version),
}
}
func (h *receivedPacketHandler) ReceivedPacket(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck bool) error {
if packetNumber < h.ignoreBelow {
func (h *receivedPacketHandler) ReceivedPacket(
pn protocol.PacketNumber,
encLevel protocol.EncryptionLevel,
rcvTime time.Time,
shouldInstigateAck bool,
) error {
switch encLevel {
case protocol.EncryptionInitial:
return h.initialPackets.ReceivedPacket(pn, rcvTime, shouldInstigateAck)
case protocol.EncryptionHandshake:
return h.handshakePackets.ReceivedPacket(pn, rcvTime, shouldInstigateAck)
case protocol.Encryption1RTT:
return h.oneRTTPackets.ReceivedPacket(pn, rcvTime, shouldInstigateAck)
default:
return fmt.Errorf("received packet with unknown encryption level: %s", encLevel)
}
}
// only to be used with 1-RTT packets
func (h *receivedPacketHandler) IgnoreBelow(pn protocol.PacketNumber) {
h.oneRTTPackets.IgnoreBelow(pn)
}
func (h *receivedPacketHandler) GetAlarmTimeout() time.Time {
initialAlarm := h.initialPackets.GetAlarmTimeout()
handshakeAlarm := h.handshakePackets.GetAlarmTimeout()
oneRTTAlarm := h.oneRTTPackets.GetAlarmTimeout()
return utils.MinNonZeroTime(utils.MinNonZeroTime(initialAlarm, handshakeAlarm), oneRTTAlarm)
}
func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel) *wire.AckFrame {
switch encLevel {
case protocol.EncryptionInitial:
return h.initialPackets.GetAckFrame()
case protocol.EncryptionHandshake:
return h.handshakePackets.GetAckFrame()
case protocol.Encryption1RTT:
return h.oneRTTPackets.GetAckFrame()
default:
return nil
}
isMissing := h.isMissing(packetNumber)
if packetNumber > h.largestObserved {
h.largestObserved = packetNumber
h.largestObservedReceivedTime = rcvTime
}
if err := h.packetHistory.ReceivedPacket(packetNumber); err != nil {
return err
}
h.maybeQueueAck(packetNumber, rcvTime, shouldInstigateAck, isMissing)
return nil
}
// IgnoreBelow sets a lower limit for acking packets.
// Packets with packet numbers smaller than p will not be acked.
func (h *receivedPacketHandler) IgnoreBelow(p protocol.PacketNumber) {
if p <= h.ignoreBelow {
return
}
h.ignoreBelow = p
h.packetHistory.DeleteBelow(p)
if h.logger.Debug() {
h.logger.Debugf("\tIgnoring all packets below %#x.", p)
}
}
// isMissing says if a packet was reported missing in the last ACK.
func (h *receivedPacketHandler) isMissing(p protocol.PacketNumber) bool {
if h.lastAck == nil || p < h.ignoreBelow {
return false
}
return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p)
}
func (h *receivedPacketHandler) hasNewMissingPackets() bool {
if h.lastAck == nil {
return false
}
highestRange := h.packetHistory.GetHighestAckRange()
return highestRange.Smallest >= h.lastAck.LargestAcked() && highestRange.Len() <= maxPacketsAfterNewMissing
}
// maybeQueueAck queues an ACK, if necessary.
// It is implemented analogously to Chrome's QuicConnection::MaybeQueueAck()
// in ACK_DECIMATION_WITH_REORDERING mode.
func (h *receivedPacketHandler) maybeQueueAck(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck, wasMissing bool) {
h.packetsReceivedSinceLastAck++
// always ack the first packet
if h.lastAck == nil {
h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.")
h.ackQueued = true
return
}
// Send an ACK if this packet was reported missing in an ACK sent before.
// Ack decimation with reordering relies on the timer to send an ACK, but if
// missing packets we reported in the previous ack, send an ACK immediately.
if wasMissing {
if h.logger.Debug() {
h.logger.Debugf("\tQueueing ACK because packet %#x was missing before.", packetNumber)
}
h.ackQueued = true
}
if !h.ackQueued && shouldInstigateAck {
h.retransmittablePacketsReceivedSinceLastAck++
if packetNumber > minReceivedBeforeAckDecimation {
// ack up to 10 packets at once
if h.retransmittablePacketsReceivedSinceLastAck >= retransmittablePacketsBeforeAck {
h.ackQueued = true
if h.logger.Debug() {
h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using threshold: %d).", h.retransmittablePacketsReceivedSinceLastAck, retransmittablePacketsBeforeAck)
}
} else if h.ackAlarm.IsZero() {
// wait for the minimum of the ack decimation delay or the delayed ack time before sending an ack
ackDelay := utils.MinDuration(ackSendDelay, time.Duration(float64(h.rttStats.MinRTT())*float64(ackDecimationDelay)))
h.ackAlarm = rcvTime.Add(ackDelay)
if h.logger.Debug() {
h.logger.Debugf("\tSetting ACK timer to min(1/4 min-RTT, max ack delay): %s (%s from now)", ackDelay, time.Until(h.ackAlarm))
}
}
} else {
// send an ACK every 2 retransmittable packets
if h.retransmittablePacketsReceivedSinceLastAck >= initialRetransmittablePacketsBeforeAck {
if h.logger.Debug() {
h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.retransmittablePacketsReceivedSinceLastAck, initialRetransmittablePacketsBeforeAck)
}
h.ackQueued = true
} else if h.ackAlarm.IsZero() {
if h.logger.Debug() {
h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", ackSendDelay)
}
h.ackAlarm = rcvTime.Add(ackSendDelay)
}
}
// If there are new missing packets to report, set a short timer to send an ACK.
if h.hasNewMissingPackets() {
// wait the minimum of 1/8 min RTT and the existing ack time
ackDelay := time.Duration(float64(h.rttStats.MinRTT()) * float64(shortAckDecimationDelay))
ackTime := rcvTime.Add(ackDelay)
if h.ackAlarm.IsZero() || h.ackAlarm.After(ackTime) {
h.ackAlarm = ackTime
if h.logger.Debug() {
h.logger.Debugf("\tSetting ACK timer to 1/8 min-RTT: %s (%s from now)", ackDelay, time.Until(h.ackAlarm))
}
}
}
}
if h.ackQueued {
// cancel the ack alarm
h.ackAlarm = time.Time{}
}
}
func (h *receivedPacketHandler) GetAckFrame() *wire.AckFrame {
now := time.Now()
if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(now)) {
return nil
}
if h.logger.Debug() && !h.ackQueued && !h.ackAlarm.IsZero() {
h.logger.Debugf("Sending ACK because the ACK timer expired.")
}
ack := &wire.AckFrame{
AckRanges: h.packetHistory.GetAckRanges(),
DelayTime: now.Sub(h.largestObservedReceivedTime),
}
h.lastAck = ack
h.ackAlarm = time.Time{}
h.ackQueued = false
h.packetsReceivedSinceLastAck = 0
h.retransmittablePacketsReceivedSinceLastAck = 0
return ack
}
func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { return h.ackAlarm }

View File

@@ -2,12 +2,13 @@ package ackhandler
import (
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
"github.com/lucas-clemente/quic-go/qerr"
)
// The receivedPacketHistory stores if a packet number has already been received.
// It generates ACK ranges which can be used to assemble an ACK frame.
// It does not store packet contents.
type receivedPacketHistory struct {
ranges *utils.PacketIntervalList
@@ -15,7 +16,7 @@ type receivedPacketHistory struct {
lowestInReceivedPacketNumbers protocol.PacketNumber
}
var errTooManyOutstandingReceivedAckRanges = qerr.Error(qerr.TooManyOutstandingReceivedPackets, "Too many outstanding received ACK ranges")
var errTooManyOutstandingReceivedAckRanges = qerr.Error(qerr.InternalError, "Too many outstanding received ACK ranges")
// newReceivedPacketHistory creates a new received packet history
func newReceivedPacketHistory() *receivedPacketHistory {

View File

@@ -0,0 +1,191 @@
package ackhandler
import (
"time"
"github.com/lucas-clemente/quic-go/internal/congestion"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
)
type receivedPacketTracker struct {
largestObserved protocol.PacketNumber
ignoreBelow protocol.PacketNumber
largestObservedReceivedTime time.Time
packetHistory *receivedPacketHistory
ackSendDelay time.Duration
rttStats *congestion.RTTStats
packetsReceivedSinceLastAck int
retransmittablePacketsReceivedSinceLastAck int
ackQueued bool
ackAlarm time.Time
lastAck *wire.AckFrame
logger utils.Logger
version protocol.VersionNumber
}
func newReceivedPacketTracker(
rttStats *congestion.RTTStats,
logger utils.Logger,
version protocol.VersionNumber,
) *receivedPacketTracker {
return &receivedPacketTracker{
packetHistory: newReceivedPacketHistory(),
ackSendDelay: ackSendDelay,
rttStats: rttStats,
logger: logger,
version: version,
}
}
func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck bool) error {
if packetNumber < h.ignoreBelow {
return nil
}
isMissing := h.isMissing(packetNumber)
if packetNumber >= h.largestObserved {
h.largestObserved = packetNumber
h.largestObservedReceivedTime = rcvTime
}
if err := h.packetHistory.ReceivedPacket(packetNumber); err != nil {
return err
}
h.maybeQueueAck(packetNumber, rcvTime, shouldInstigateAck, isMissing)
return nil
}
// IgnoreBelow sets a lower limit for acking packets.
// Packets with packet numbers smaller than p will not be acked.
func (h *receivedPacketTracker) IgnoreBelow(p protocol.PacketNumber) {
if p <= h.ignoreBelow {
return
}
h.ignoreBelow = p
h.packetHistory.DeleteBelow(p)
if h.logger.Debug() {
h.logger.Debugf("\tIgnoring all packets below %#x.", p)
}
}
// isMissing says if a packet was reported missing in the last ACK.
func (h *receivedPacketTracker) isMissing(p protocol.PacketNumber) bool {
if h.lastAck == nil || p < h.ignoreBelow {
return false
}
return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p)
}
func (h *receivedPacketTracker) hasNewMissingPackets() bool {
if h.lastAck == nil {
return false
}
highestRange := h.packetHistory.GetHighestAckRange()
return highestRange.Smallest >= h.lastAck.LargestAcked() && highestRange.Len() <= maxPacketsAfterNewMissing
}
// maybeQueueAck queues an ACK, if necessary.
// It is implemented analogously to Chrome's QuicConnection::MaybeQueueAck()
// in ACK_DECIMATION_WITH_REORDERING mode.
func (h *receivedPacketTracker) maybeQueueAck(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck, wasMissing bool) {
h.packetsReceivedSinceLastAck++
// always ack the first packet
if h.lastAck == nil {
h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.")
h.ackQueued = true
return
}
// Send an ACK if this packet was reported missing in an ACK sent before.
// Ack decimation with reordering relies on the timer to send an ACK, but if
// missing packets we reported in the previous ack, send an ACK immediately.
if wasMissing {
if h.logger.Debug() {
h.logger.Debugf("\tQueueing ACK because packet %#x was missing before.", packetNumber)
}
h.ackQueued = true
}
if !h.ackQueued && shouldInstigateAck {
h.retransmittablePacketsReceivedSinceLastAck++
if packetNumber > minReceivedBeforeAckDecimation {
// ack up to 10 packets at once
if h.retransmittablePacketsReceivedSinceLastAck >= retransmittablePacketsBeforeAck {
h.ackQueued = true
if h.logger.Debug() {
h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using threshold: %d).", h.retransmittablePacketsReceivedSinceLastAck, retransmittablePacketsBeforeAck)
}
} else if h.ackAlarm.IsZero() {
// wait for the minimum of the ack decimation delay or the delayed ack time before sending an ack
ackDelay := utils.MinDuration(ackSendDelay, time.Duration(float64(h.rttStats.MinRTT())*float64(ackDecimationDelay)))
h.ackAlarm = rcvTime.Add(ackDelay)
if h.logger.Debug() {
h.logger.Debugf("\tSetting ACK timer to min(1/4 min-RTT, max ack delay): %s (%s from now)", ackDelay, time.Until(h.ackAlarm))
}
}
} else {
// send an ACK every 2 retransmittable packets
if h.retransmittablePacketsReceivedSinceLastAck >= initialRetransmittablePacketsBeforeAck {
if h.logger.Debug() {
h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.retransmittablePacketsReceivedSinceLastAck, initialRetransmittablePacketsBeforeAck)
}
h.ackQueued = true
} else if h.ackAlarm.IsZero() {
if h.logger.Debug() {
h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", ackSendDelay)
}
h.ackAlarm = rcvTime.Add(ackSendDelay)
}
}
// If there are new missing packets to report, set a short timer to send an ACK.
if h.hasNewMissingPackets() {
// wait the minimum of 1/8 min RTT and the existing ack time
ackDelay := time.Duration(float64(h.rttStats.MinRTT()) * float64(shortAckDecimationDelay))
ackTime := rcvTime.Add(ackDelay)
if h.ackAlarm.IsZero() || h.ackAlarm.After(ackTime) {
h.ackAlarm = ackTime
if h.logger.Debug() {
h.logger.Debugf("\tSetting ACK timer to 1/8 min-RTT: %s (%s from now)", ackDelay, time.Until(h.ackAlarm))
}
}
}
}
if h.ackQueued {
// cancel the ack alarm
h.ackAlarm = time.Time{}
}
}
func (h *receivedPacketTracker) GetAckFrame() *wire.AckFrame {
now := time.Now()
if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(now)) {
return nil
}
if h.logger.Debug() && !h.ackQueued && !h.ackAlarm.IsZero() {
h.logger.Debugf("Sending ACK because the ACK timer expired.")
}
ack := &wire.AckFrame{
AckRanges: h.packetHistory.GetAckRanges(),
DelayTime: now.Sub(h.largestObservedReceivedTime),
}
h.lastAck = ack
h.ackAlarm = time.Time{}
h.ackQueued = false
h.packetsReceivedSinceLastAck = 0
h.retransmittablePacketsReceivedSinceLastAck = 0
return ack
}
func (h *receivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm }

View File

@@ -16,8 +16,6 @@ func stripNonRetransmittableFrames(fs []wire.Frame) []wire.Frame {
// IsFrameRetransmittable returns true if the frame should be retransmitted.
func IsFrameRetransmittable(f wire.Frame) bool {
switch f.(type) {
case *wire.StopWaitingFrame:
return false
case *wire.AckFrame:
return false
default:

View File

@@ -12,10 +12,8 @@ const (
SendAck
// SendRetransmission means that retransmissions should be sent
SendRetransmission
// SendRTO means that an RTO probe packet should be sent
SendRTO
// SendTLP means that a TLP probe packet should be sent
SendTLP
// SendPTO means that a probe packet should be sent
SendPTO
// SendAny means that any packet should be sent
SendAny
)
@@ -28,10 +26,8 @@ func (s SendMode) String() string {
return "ack"
case SendRetransmission:
return "retransmission"
case SendRTO:
return "rto"
case SendTLP:
return "tlp"
case SendPTO:
return "pto"
case SendAny:
return "any"
default:

View File

@@ -8,45 +8,49 @@ import (
"github.com/lucas-clemente/quic-go/internal/congestion"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
"github.com/lucas-clemente/quic-go/qerr"
)
const (
// Maximum reordering in time space before time based loss detection considers a packet lost.
// In fraction of an RTT.
timeReorderingFraction = 1.0 / 8
// defaultRTOTimeout is the RTO time on new connections
defaultRTOTimeout = 500 * time.Millisecond
// Minimum time in the future a tail loss probe alarm may be set for.
minTPLTimeout = 10 * time.Millisecond
// Maximum number of tail loss probes before an RTO fires.
maxTLPs = 2
// Minimum time in the future an RTO alarm may be set for.
minRTOTimeout = 200 * time.Millisecond
// maxRTOTimeout is the maximum RTO time
maxRTOTimeout = 60 * time.Second
// Timer granularity. The timer will not be set to a value smaller than granularity.
granularity = time.Millisecond
)
type packetNumberSpace struct {
history *sentPacketHistory
pns *packetNumberGenerator
largestAcked protocol.PacketNumber
largestSent protocol.PacketNumber
}
func newPacketNumberSpace(initialPN protocol.PacketNumber) *packetNumberSpace {
return &packetNumberSpace{
history: newSentPacketHistory(),
pns: newPacketNumberGenerator(initialPN, protocol.SkipPacketAveragePeriodLength),
}
}
type sentPacketHandler struct {
lastSentPacketNumber protocol.PacketNumber
lastSentRetransmittablePacketTime time.Time
lastSentHandshakePacketTime time.Time
lastSentRetransmittablePacketTime time.Time // only applies to the application-data packet number space
lastSentCryptoPacketTime time.Time
nextPacketSendTime time.Time
skippedPackets []protocol.PacketNumber
nextSendTime time.Time
largestAcked protocol.PacketNumber
largestReceivedPacketWithAck protocol.PacketNumber
// lowestPacketNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived
initialPackets *packetNumberSpace
handshakePackets *packetNumberSpace
oneRTTPackets *packetNumberSpace
// lowestNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived
// example: we send an ACK for packets 90-100 with packet number 20
// once we receive an ACK from the peer for packet 20, the lowestPacketNotConfirmedAcked is 101
lowestPacketNotConfirmedAcked protocol.PacketNumber
largestSentBeforeRTO protocol.PacketNumber
packetHistory *sentPacketHistory
stopWaitingManager stopWaitingManager
// once we receive an ACK from the peer for packet 20, the lowestNotConfirmedAcked is 101
// Only applies to the application-data packet number space.
lowestNotConfirmedAcked protocol.PacketNumber
retransmissionQueue []*Packet
@@ -56,17 +60,14 @@ type sentPacketHandler struct {
rttStats *congestion.RTTStats
handshakeComplete bool
// The number of times the handshake packets have been retransmitted without receiving an ack.
handshakeCount uint32
// The number of times a TLP has been sent without receiving an ack.
tlpCount uint32
allowTLP bool
// The number of times an RTO has been sent without receiving an ack.
rtoCount uint32
// The number of RTO probe packets that should be sent.
numRTOs int
// The number of times the crypto packets have been retransmitted without receiving an ack.
cryptoCount uint32
// The number of times a PTO has been sent without receiving an ack.
ptoCount uint32
// The number of PTO probe packets that should be sent.
// Only applies to the application-data packet number space.
numProbesToSend int
// The time at which the next packet will be considered lost based on early transmit or exceeding the reordering window in time.
lossTime time.Time
@@ -75,12 +76,14 @@ type sentPacketHandler struct {
alarm time.Time
logger utils.Logger
version protocol.VersionNumber
}
// NewSentPacketHandler creates a new sentPacketHandler
func NewSentPacketHandler(rttStats *congestion.RTTStats, logger utils.Logger, version protocol.VersionNumber) SentPacketHandler {
func NewSentPacketHandler(
initialPacketNumber protocol.PacketNumber,
rttStats *congestion.RTTStats,
logger utils.Logger,
) SentPacketHandler {
congestion := congestion.NewCubicSender(
congestion.DefaultClock{},
rttStats,
@@ -90,39 +93,32 @@ func NewSentPacketHandler(rttStats *congestion.RTTStats, logger utils.Logger, ve
)
return &sentPacketHandler{
packetHistory: newSentPacketHistory(),
stopWaitingManager: stopWaitingManager{},
rttStats: rttStats,
congestion: congestion,
logger: logger,
version: version,
initialPackets: newPacketNumberSpace(initialPacketNumber),
handshakePackets: newPacketNumberSpace(0),
oneRTTPackets: newPacketNumberSpace(0),
rttStats: rttStats,
congestion: congestion,
logger: logger,
}
}
func (h *sentPacketHandler) lowestUnacked() protocol.PacketNumber {
if p := h.packetHistory.FirstOutstanding(); p != nil {
return p.PacketNumber
}
return h.largestAcked + 1
}
func (h *sentPacketHandler) SetHandshakeComplete() {
h.logger.Debugf("Handshake complete. Discarding all outstanding handshake packets.")
h.logger.Debugf("Handshake complete. Discarding all outstanding crypto packets.")
var queue []*Packet
for _, packet := range h.retransmissionQueue {
if packet.EncryptionLevel == protocol.EncryptionForwardSecure {
if packet.EncryptionLevel == protocol.Encryption1RTT {
queue = append(queue, packet)
}
}
var handshakePackets []*Packet
h.packetHistory.Iterate(func(p *Packet) (bool, error) {
if p.EncryptionLevel != protocol.EncryptionForwardSecure {
handshakePackets = append(handshakePackets, p)
for _, pnSpace := range []*packetNumberSpace{h.initialPackets, h.handshakePackets} {
var cryptoPackets []*Packet
pnSpace.history.Iterate(func(p *Packet) (bool, error) {
cryptoPackets = append(cryptoPackets, p)
return true, nil
})
for _, p := range cryptoPackets {
pnSpace.history.Remove(p.PacketNumber)
}
return true, nil
})
for _, p := range handshakePackets {
h.packetHistory.Remove(p.PacketNumber)
}
h.retransmissionQueue = queue
h.handshakeComplete = true
@@ -130,7 +126,7 @@ func (h *sentPacketHandler) SetHandshakeComplete() {
func (h *sentPacketHandler) SentPacket(packet *Packet) {
if isRetransmittable := h.sentPacketImpl(packet); isRetransmittable {
h.packetHistory.SentPacket(packet)
h.getPacketNumberSpace(packet.EncryptionLevel).history.SentPacket(packet)
h.updateLossDetectionAlarm()
}
}
@@ -142,20 +138,33 @@ func (h *sentPacketHandler) SentPacketsAsRetransmission(packets []*Packet, retra
p = append(p, packet)
}
}
h.packetHistory.SentPacketsAsRetransmission(p, retransmissionOf)
h.getPacketNumberSpace(p[0].EncryptionLevel).history.SentPacketsAsRetransmission(p, retransmissionOf)
h.updateLossDetectionAlarm()
}
func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLevel) *packetNumberSpace {
switch encLevel {
case protocol.EncryptionInitial:
return h.initialPackets
case protocol.EncryptionHandshake:
return h.handshakePackets
case protocol.Encryption1RTT:
return h.oneRTTPackets
default:
panic("invalid packet number space")
}
}
func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* isRetransmittable */ {
for p := h.lastSentPacketNumber + 1; p < packet.PacketNumber; p++ {
h.logger.Debugf("Skipping packet number %#x", p)
h.skippedPackets = append(h.skippedPackets, p)
if len(h.skippedPackets) > protocol.MaxTrackedSkippedPackets {
h.skippedPackets = h.skippedPackets[1:]
pnSpace := h.getPacketNumberSpace(packet.EncryptionLevel)
if h.logger.Debug() && pnSpace.largestSent != 0 {
for p := pnSpace.largestSent + 1; p < packet.PacketNumber; p++ {
h.logger.Debugf("Skipping packet number %#x", p)
}
}
h.lastSentPacketNumber = packet.PacketNumber
pnSpace.largestSent = packet.PacketNumber
if len(packet.Frames) > 0 {
if ackFrame, ok := packet.Frames[0].(*wire.AckFrame); ok {
@@ -167,61 +176,61 @@ func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* isRetransmitt
isRetransmittable := len(packet.Frames) != 0
if isRetransmittable {
if packet.EncryptionLevel < protocol.EncryptionForwardSecure {
h.lastSentHandshakePacketTime = packet.SendTime
if packet.EncryptionLevel != protocol.Encryption1RTT {
h.lastSentCryptoPacketTime = packet.SendTime
}
h.lastSentRetransmittablePacketTime = packet.SendTime
packet.includedInBytesInFlight = true
h.bytesInFlight += packet.Length
packet.canBeRetransmitted = true
if h.numRTOs > 0 {
h.numRTOs--
if h.numProbesToSend > 0 {
h.numProbesToSend--
}
h.allowTLP = false
}
h.congestion.OnPacketSent(packet.SendTime, h.bytesInFlight, packet.PacketNumber, packet.Length, isRetransmittable)
h.nextPacketSendTime = utils.MaxTime(h.nextPacketSendTime, packet.SendTime).Add(h.congestion.TimeUntilSend(h.bytesInFlight))
h.nextSendTime = utils.MaxTime(h.nextSendTime, packet.SendTime).Add(h.congestion.TimeUntilSend(h.bytesInFlight))
return isRetransmittable
}
func (h *sentPacketHandler) ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, rcvTime time.Time) error {
pnSpace := h.getPacketNumberSpace(encLevel)
largestAcked := ackFrame.LargestAcked()
if largestAcked > h.lastSentPacketNumber {
return qerr.Error(qerr.InvalidAckData, "Received ACK for an unsent package")
if largestAcked > pnSpace.largestSent {
return qerr.Error(qerr.ProtocolViolation, "Received ACK for an unsent packet")
}
// duplicate or out of order ACK
if withPacketNumber != 0 && withPacketNumber <= h.largestReceivedPacketWithAck {
h.logger.Debugf("Ignoring ACK frame (duplicate or out of order).")
return nil
}
h.largestReceivedPacketWithAck = withPacketNumber
h.largestAcked = utils.MaxPacketNumber(h.largestAcked, largestAcked)
pnSpace.largestAcked = utils.MaxPacketNumber(pnSpace.largestAcked, largestAcked)
if h.skippedPacketsAcked(ackFrame) {
return qerr.Error(qerr.InvalidAckData, "Received an ACK for a skipped packet number")
if !pnSpace.pns.Validate(ackFrame) {
return qerr.Error(qerr.ProtocolViolation, "Received an ACK for a skipped packet number")
}
if rttUpdated := h.maybeUpdateRTT(largestAcked, ackFrame.DelayTime, rcvTime); rttUpdated {
// maybe update the RTT
if p := pnSpace.history.GetPacket(ackFrame.LargestAcked()); p != nil {
h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackFrame.DelayTime, rcvTime)
if h.logger.Debug() {
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
}
h.congestion.MaybeExitSlowStart()
}
ackedPackets, err := h.determineNewlyAckedPackets(ackFrame)
ackedPackets, err := h.determineNewlyAckedPackets(ackFrame, encLevel)
if err != nil {
return err
}
if len(ackedPackets) == 0 {
return nil
}
priorInFlight := h.bytesInFlight
for _, p := range ackedPackets {
if encLevel < p.EncryptionLevel {
return fmt.Errorf("Received ACK with encryption level %s that acks a packet %d (encryption level %s)", encLevel, p.PacketNumber, p.EncryptionLevel)
}
// largestAcked == 0 either means that the packet didn't contain an ACK, or it just acked packet 0
// It is safe to ignore the corner case of packets that just acked packet 0, because
// the lowestPacketNotConfirmedAcked is only used to limit the number of ACK ranges we will send.
if p.largestAcked != 0 {
h.lowestPacketNotConfirmedAcked = utils.MaxPacketNumber(h.lowestPacketNotConfirmedAcked, p.largestAcked+1)
if p.largestAcked != 0 && encLevel == protocol.Encryption1RTT {
h.lowestNotConfirmedAcked = utils.MaxPacketNumber(h.lowestNotConfirmedAcked, p.largestAcked+1)
}
if err := h.onPacketAcked(p, rcvTime); err != nil {
return err
@@ -231,27 +240,32 @@ func (h *sentPacketHandler) ReceivedAck(ackFrame *wire.AckFrame, withPacketNumbe
}
}
if err := h.detectLostPackets(rcvTime, priorInFlight); err != nil {
if err := h.detectLostPackets(rcvTime, encLevel, priorInFlight); err != nil {
return err
}
h.ptoCount = 0
h.cryptoCount = 0
h.numProbesToSend = 0
h.updateLossDetectionAlarm()
h.garbageCollectSkippedPackets()
h.stopWaitingManager.ReceivedAck(ackFrame)
return nil
}
func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber {
return h.lowestPacketNotConfirmedAcked
return h.lowestNotConfirmedAcked
}
func (h *sentPacketHandler) determineNewlyAckedPackets(ackFrame *wire.AckFrame) ([]*Packet, error) {
func (h *sentPacketHandler) determineNewlyAckedPackets(
ackFrame *wire.AckFrame,
encLevel protocol.EncryptionLevel,
) ([]*Packet, error) {
pnSpace := h.getPacketNumberSpace(encLevel)
var ackedPackets []*Packet
ackRangeIndex := 0
lowestAcked := ackFrame.LowestAcked()
largestAcked := ackFrame.LargestAcked()
err := h.packetHistory.Iterate(func(p *Packet) (bool, error) {
err := pnSpace.history.Iterate(func(p *Packet) (bool, error) {
// Ignore packets below the lowest acked
if p.PacketNumber < lowestAcked {
return true, nil
@@ -290,57 +304,54 @@ func (h *sentPacketHandler) determineNewlyAckedPackets(ackFrame *wire.AckFrame)
return ackedPackets, err
}
func (h *sentPacketHandler) maybeUpdateRTT(largestAcked protocol.PacketNumber, ackDelay time.Duration, rcvTime time.Time) bool {
if p := h.packetHistory.GetPacket(largestAcked); p != nil {
h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackDelay, rcvTime)
if h.logger.Debug() {
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
}
return true
}
return false
func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
return h.initialPackets.history.HasOutstandingPackets() || h.handshakePackets.history.HasOutstandingPackets()
}
func (h *sentPacketHandler) hasOutstandingPackets() bool {
return h.oneRTTPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
}
func (h *sentPacketHandler) updateLossDetectionAlarm() {
// Cancel the alarm if no packets are outstanding
if !h.packetHistory.HasOutstandingPackets() {
if !h.hasOutstandingPackets() {
h.alarm = time.Time{}
return
}
if h.packetHistory.HasOutstandingHandshakePackets() {
h.alarm = h.lastSentHandshakePacketTime.Add(h.computeHandshakeTimeout())
if h.hasOutstandingCryptoPackets() {
h.alarm = h.lastSentCryptoPacketTime.Add(h.computeCryptoTimeout())
} else if !h.lossTime.IsZero() {
// Early retransmit timer or time loss detection.
h.alarm = h.lossTime
} else {
// RTO or TLP alarm
alarmDuration := h.computeRTOTimeout()
if h.tlpCount < maxTLPs {
tlpAlarm := h.computeTLPTimeout()
// if the RTO duration is shorter than the TLP duration, use the RTO duration
alarmDuration = utils.MinDuration(alarmDuration, tlpAlarm)
}
h.alarm = h.lastSentRetransmittablePacketTime.Add(alarmDuration)
} else { // PTO alarm
h.alarm = h.lastSentRetransmittablePacketTime.Add(h.computePTOTimeout())
}
}
func (h *sentPacketHandler) detectLostPackets(now time.Time, priorInFlight protocol.ByteCount) error {
h.lossTime = time.Time{}
func (h *sentPacketHandler) detectLostPackets(
now time.Time,
encLevel protocol.EncryptionLevel,
priorInFlight protocol.ByteCount,
) error {
if encLevel == protocol.Encryption1RTT {
h.lossTime = time.Time{}
}
pnSpace := h.getPacketNumberSpace(encLevel)
maxRTT := float64(utils.MaxDuration(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT()))
delayUntilLost := time.Duration((1.0 + timeReorderingFraction) * maxRTT)
var lostPackets []*Packet
h.packetHistory.Iterate(func(packet *Packet) (bool, error) {
if packet.PacketNumber > h.largestAcked {
pnSpace.history.Iterate(func(packet *Packet) (bool, error) {
if packet.PacketNumber > pnSpace.largestAcked {
return false, nil
}
timeSinceSent := now.Sub(packet.SendTime)
if timeSinceSent > delayUntilLost {
lostPackets = append(lostPackets, packet)
} else if h.lossTime.IsZero() {
} else if h.lossTime.IsZero() && encLevel == protocol.Encryption1RTT {
if h.logger.Debug() {
h.logger.Debugf("\tsetting loss timer for packet %#x to %s (in %s)", packet.PacketNumber, delayUntilLost, delayUntilLost-timeSinceSent)
}
@@ -349,6 +360,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, priorInFlight proto
}
return true, nil
})
if h.logger.Debug() && len(lostPackets) > 0 {
pns := make([]protocol.PacketNumber, len(lostPackets))
for i, p := range lostPackets {
@@ -365,11 +377,11 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, priorInFlight proto
}
if p.canBeRetransmitted {
// queue the packet for retransmission, and report the loss to the congestion controller
if err := h.queuePacketForRetransmission(p); err != nil {
if err := h.queuePacketForRetransmission(p, pnSpace); err != nil {
return err
}
}
h.packetHistory.Remove(p.PacketNumber)
pnSpace.history.Remove(p.PacketNumber)
}
return nil
}
@@ -379,7 +391,7 @@ func (h *sentPacketHandler) OnAlarm() error {
// updateLossDetectionAlarm. This doesn't reset the timer in the session though.
// When OnAlarm is called, we therefore need to make sure that there are
// actually packets outstanding.
if h.packetHistory.HasOutstandingPackets() {
if h.hasOutstandingPackets() {
if err := h.onVerifiedAlarm(); err != nil {
return err
}
@@ -390,33 +402,24 @@ func (h *sentPacketHandler) OnAlarm() error {
func (h *sentPacketHandler) onVerifiedAlarm() error {
var err error
if h.packetHistory.HasOutstandingHandshakePackets() {
if h.hasOutstandingCryptoPackets() {
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm fired in handshake mode. Handshake count: %d", h.handshakeCount)
h.logger.Debugf("Loss detection alarm fired in crypto mode. Crypto count: %d", h.cryptoCount)
}
h.handshakeCount++
err = h.queueHandshakePacketsForRetransmission()
h.cryptoCount++
err = h.queueCryptoPacketsForRetransmission()
} else if !h.lossTime.IsZero() {
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", h.lossTime)
}
// Early retransmit or time loss detection
err = h.detectLostPackets(time.Now(), h.bytesInFlight)
} else if h.tlpCount < maxTLPs { // TLP
err = h.detectLostPackets(time.Now(), protocol.Encryption1RTT, h.bytesInFlight)
} else { // PTO
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm fired in TLP mode. TLP count: %d", h.tlpCount)
h.logger.Debugf("Loss detection alarm fired in PTO mode. PTO count: %d", h.ptoCount)
}
h.allowTLP = true
h.tlpCount++
} else { // RTO
if h.logger.Debug() {
h.logger.Debugf("Loss detection alarm fired in RTO mode. RTO count: %d", h.rtoCount)
}
if h.rtoCount == 0 {
h.largestSentBeforeRTO = h.lastSentPacketNumber
}
h.rtoCount++
h.numRTOs += 2
h.ptoCount++
h.numProbesToSend += 2
}
return err
}
@@ -426,10 +429,11 @@ func (h *sentPacketHandler) GetAlarmTimeout() time.Time {
}
func (h *sentPacketHandler) onPacketAcked(p *Packet, rcvTime time.Time) error {
pnSpace := h.getPacketNumberSpace(p.EncryptionLevel)
// This happens if a packet and its retransmissions is acked in the same ACK.
// As soon as we process the first one, this will remove all the retransmissions,
// so we won't find the retransmitted packet number later.
if packet := h.packetHistory.GetPacket(p.PacketNumber); packet == nil {
if packet := pnSpace.history.GetPacket(p.PacketNumber); packet == nil {
return nil
}
@@ -438,7 +442,7 @@ func (h *sentPacketHandler) onPacketAcked(p *Packet, rcvTime time.Time) error {
// * this packet wasn't retransmitted yet
if p.isRetransmission {
// that the parent doesn't exist is expected to happen every time the original packet was already acked
if parent := h.packetHistory.GetPacket(p.retransmissionOf); parent != nil {
if parent := pnSpace.history.GetPacket(p.retransmissionOf); parent != nil {
if len(parent.retransmittedAs) == 1 {
parent.retransmittedAs = nil
} else {
@@ -457,44 +461,26 @@ func (h *sentPacketHandler) onPacketAcked(p *Packet, rcvTime time.Time) error {
if p.includedInBytesInFlight {
h.bytesInFlight -= p.Length
}
if h.rtoCount > 0 {
h.verifyRTO(p.PacketNumber)
}
if err := h.stopRetransmissionsFor(p); err != nil {
if err := h.stopRetransmissionsFor(p, pnSpace); err != nil {
return err
}
h.rtoCount = 0
h.tlpCount = 0
h.handshakeCount = 0
return h.packetHistory.Remove(p.PacketNumber)
return pnSpace.history.Remove(p.PacketNumber)
}
func (h *sentPacketHandler) stopRetransmissionsFor(p *Packet) error {
if err := h.packetHistory.MarkCannotBeRetransmitted(p.PacketNumber); err != nil {
func (h *sentPacketHandler) stopRetransmissionsFor(p *Packet, pnSpace *packetNumberSpace) error {
if err := pnSpace.history.MarkCannotBeRetransmitted(p.PacketNumber); err != nil {
return err
}
for _, r := range p.retransmittedAs {
packet := h.packetHistory.GetPacket(r)
packet := pnSpace.history.GetPacket(r)
if packet == nil {
return fmt.Errorf("sent packet handler BUG: marking packet as not retransmittable %d (retransmission of %d) not found in history", r, p.PacketNumber)
}
h.stopRetransmissionsFor(packet)
h.stopRetransmissionsFor(packet, pnSpace)
}
return nil
}
func (h *sentPacketHandler) verifyRTO(pn protocol.PacketNumber) {
if pn <= h.largestSentBeforeRTO {
h.logger.Debugf("Spurious RTO detected. Received an ACK for %#x (largest sent before RTO: %#x)", pn, h.largestSentBeforeRTO)
// Replace SRTT with latest_rtt and increase the variance to prevent
// a spurious RTO from happening again.
h.rttStats.ExpireSmoothedMetrics()
return
}
h.logger.Debugf("RTO verified. Received an ACK for %#x (largest sent before RTO: %#x", pn, h.largestSentBeforeRTO)
h.congestion.OnRetransmissionTimeout(true)
}
func (h *sentPacketHandler) DequeuePacketForRetransmission() *Packet {
if len(h.retransmissionQueue) == 0 {
return nil
@@ -508,28 +494,40 @@ func (h *sentPacketHandler) DequeuePacketForRetransmission() *Packet {
}
func (h *sentPacketHandler) DequeueProbePacket() (*Packet, error) {
pnSpace := h.getPacketNumberSpace(protocol.Encryption1RTT)
if len(h.retransmissionQueue) == 0 {
p := h.packetHistory.FirstOutstanding()
p := pnSpace.history.FirstOutstanding()
if p == nil {
return nil, errors.New("cannot dequeue a probe packet. No outstanding packets")
}
if err := h.queuePacketForRetransmission(p); err != nil {
if err := h.queuePacketForRetransmission(p, pnSpace); err != nil {
return nil, err
}
}
return h.DequeuePacketForRetransmission(), nil
}
func (h *sentPacketHandler) GetPacketNumberLen(p protocol.PacketNumber) protocol.PacketNumberLen {
return protocol.GetPacketNumberLengthForHeader(p, h.lowestUnacked(), h.version)
func (h *sentPacketHandler) PeekPacketNumber(encLevel protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) {
pnSpace := h.getPacketNumberSpace(encLevel)
var lowestUnacked protocol.PacketNumber
if p := pnSpace.history.FirstOutstanding(); p != nil {
lowestUnacked = p.PacketNumber
} else {
lowestUnacked = pnSpace.largestAcked + 1
}
pn := pnSpace.pns.Peek()
return pn, protocol.GetPacketNumberLengthForHeader(pn, lowestUnacked)
}
func (h *sentPacketHandler) GetStopWaitingFrame(force bool) *wire.StopWaitingFrame {
return h.stopWaitingManager.GetStopWaitingFrame(force)
func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) protocol.PacketNumber {
return h.getPacketNumberSpace(encLevel).pns.Pop()
}
func (h *sentPacketHandler) SendMode() SendMode {
numTrackedPackets := len(h.retransmissionQueue) + h.packetHistory.Len()
numTrackedPackets := len(h.retransmissionQueue) + h.initialPackets.history.Len() +
h.handshakePackets.history.Len() + h.oneRTTPackets.history.Len()
// Don't send any packets if we're keeping track of the maximum number of packets.
// Note that since MaxOutstandingSentPackets is smaller than MaxTrackedSentPackets,
@@ -541,11 +539,8 @@ func (h *sentPacketHandler) SendMode() SendMode {
}
return SendNone
}
if h.allowTLP {
return SendTLP
}
if h.numRTOs > 0 {
return SendRTO
if h.numProbesToSend > 0 {
return SendPTO
}
// Only send ACKs if we're congestion limited.
if cwnd := h.congestion.GetCongestionWindow(); h.bytesInFlight > cwnd {
@@ -568,13 +563,13 @@ func (h *sentPacketHandler) SendMode() SendMode {
}
func (h *sentPacketHandler) TimeUntilSend() time.Time {
return h.nextPacketSendTime
return h.nextSendTime
}
func (h *sentPacketHandler) ShouldSendNumPackets() int {
if h.numRTOs > 0 {
if h.numProbesToSend > 0 {
// RTO probes should not be paced, but must be sent immediately.
return h.numRTOs
return h.numProbesToSend
}
delay := h.congestion.TimeUntilSend(h.bytesInFlight)
if delay == 0 || delay > protocol.MinPacingDelay {
@@ -583,77 +578,70 @@ func (h *sentPacketHandler) ShouldSendNumPackets() int {
return int(math.Ceil(float64(protocol.MinPacingDelay) / float64(delay)))
}
func (h *sentPacketHandler) queueHandshakePacketsForRetransmission() error {
var handshakePackets []*Packet
h.packetHistory.Iterate(func(p *Packet) (bool, error) {
if p.canBeRetransmitted && p.EncryptionLevel < protocol.EncryptionForwardSecure {
handshakePackets = append(handshakePackets, p)
func (h *sentPacketHandler) queueCryptoPacketsForRetransmission() error {
if err := h.queueAllPacketsForRetransmission(protocol.EncryptionInitial); err != nil {
return err
}
return h.queueAllPacketsForRetransmission(protocol.EncryptionHandshake)
}
func (h *sentPacketHandler) queueAllPacketsForRetransmission(encLevel protocol.EncryptionLevel) error {
var packets []*Packet
pnSpace := h.getPacketNumberSpace(encLevel)
pnSpace.history.Iterate(func(p *Packet) (bool, error) {
if p.canBeRetransmitted {
packets = append(packets, p)
}
return true, nil
})
for _, p := range handshakePackets {
h.logger.Debugf("Queueing packet %#x as a handshake retransmission", p.PacketNumber)
if err := h.queuePacketForRetransmission(p); err != nil {
for _, p := range packets {
h.logger.Debugf("Queueing packet %#x (%s) as a crypto retransmission", p.PacketNumber, encLevel)
if err := h.queuePacketForRetransmission(p, pnSpace); err != nil {
return err
}
}
return nil
}
func (h *sentPacketHandler) queuePacketForRetransmission(p *Packet) error {
func (h *sentPacketHandler) queuePacketForRetransmission(p *Packet, pnSpace *packetNumberSpace) error {
if !p.canBeRetransmitted {
return fmt.Errorf("sent packet handler BUG: packet %d already queued for retransmission", p.PacketNumber)
}
if err := h.packetHistory.MarkCannotBeRetransmitted(p.PacketNumber); err != nil {
if err := pnSpace.history.MarkCannotBeRetransmitted(p.PacketNumber); err != nil {
return err
}
h.retransmissionQueue = append(h.retransmissionQueue, p)
h.stopWaitingManager.QueuedRetransmissionForPacketNumber(p.PacketNumber)
return nil
}
func (h *sentPacketHandler) computeHandshakeTimeout() time.Duration {
duration := utils.MaxDuration(2*h.rttStats.SmoothedOrInitialRTT(), minTPLTimeout)
func (h *sentPacketHandler) computeCryptoTimeout() time.Duration {
duration := utils.MaxDuration(2*h.rttStats.SmoothedOrInitialRTT(), granularity)
// exponential backoff
// There's an implicit limit to this set by the handshake timeout.
return duration << h.handshakeCount
// There's an implicit limit to this set by the crypto timeout.
return duration << h.cryptoCount
}
func (h *sentPacketHandler) computeTLPTimeout() time.Duration {
func (h *sentPacketHandler) computePTOTimeout() time.Duration {
// TODO(#1236): include the max_ack_delay
return utils.MaxDuration(h.rttStats.SmoothedOrInitialRTT()*3/2, minTPLTimeout)
duration := utils.MaxDuration(h.rttStats.SmoothedOrInitialRTT()+4*h.rttStats.MeanDeviation(), granularity)
return duration << h.ptoCount
}
func (h *sentPacketHandler) computeRTOTimeout() time.Duration {
var rto time.Duration
rtt := h.rttStats.SmoothedRTT()
if rtt == 0 {
rto = defaultRTOTimeout
} else {
rto = rtt + 4*h.rttStats.MeanDeviation()
}
rto = utils.MaxDuration(rto, minRTOTimeout)
// Exponential backoff
rto <<= h.rtoCount
return utils.MinDuration(rto, maxRTOTimeout)
}
func (h *sentPacketHandler) skippedPacketsAcked(ackFrame *wire.AckFrame) bool {
for _, p := range h.skippedPackets {
if ackFrame.AcksPacket(p) {
return true
func (h *sentPacketHandler) ResetForRetry() error {
h.cryptoCount = 0
h.bytesInFlight = 0
var packets []*Packet
h.initialPackets.history.Iterate(func(p *Packet) (bool, error) {
if p.canBeRetransmitted {
packets = append(packets, p)
}
return true, nil
})
for _, p := range packets {
h.logger.Debugf("Queueing packet %#x for retransmission.", p.PacketNumber)
h.retransmissionQueue = append(h.retransmissionQueue, p)
}
return false
}
func (h *sentPacketHandler) garbageCollectSkippedPackets() {
lowestUnacked := h.lowestUnacked()
deleteIndex := 0
for i, p := range h.skippedPackets {
if p < lowestUnacked {
deleteIndex = i + 1
}
}
h.skippedPackets = h.skippedPackets[deleteIndex:]
h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Pop())
h.updateLossDetectionAlarm()
return nil
}

View File

@@ -10,8 +10,7 @@ type sentPacketHistory struct {
packetList *PacketList
packetMap map[protocol.PacketNumber]*PacketElement
numOutstandingPackets int
numOutstandingHandshakePackets int
numOutstandingPackets int
firstOutstanding *PacketElement
}
@@ -35,9 +34,6 @@ func (h *sentPacketHistory) sentPacketImpl(p *Packet) *PacketElement {
}
if p.canBeRetransmitted {
h.numOutstandingPackets++
if p.EncryptionLevel < protocol.EncryptionForwardSecure {
h.numOutstandingHandshakePackets++
}
}
return el
}
@@ -106,12 +102,6 @@ func (h *sentPacketHistory) MarkCannotBeRetransmitted(pn protocol.PacketNumber)
if h.numOutstandingPackets < 0 {
panic("numOutstandingHandshakePackets negative")
}
if el.Value.EncryptionLevel < protocol.EncryptionForwardSecure {
h.numOutstandingHandshakePackets--
if h.numOutstandingHandshakePackets < 0 {
panic("numOutstandingHandshakePackets negative")
}
}
}
el.Value.canBeRetransmitted = false
if el == h.firstOutstanding {
@@ -147,12 +137,6 @@ func (h *sentPacketHistory) Remove(p protocol.PacketNumber) error {
if h.numOutstandingPackets < 0 {
panic("numOutstandingHandshakePackets negative")
}
if el.Value.EncryptionLevel < protocol.EncryptionForwardSecure {
h.numOutstandingHandshakePackets--
if h.numOutstandingHandshakePackets < 0 {
panic("numOutstandingHandshakePackets negative")
}
}
}
h.packetList.Remove(el)
delete(h.packetMap, p)
@@ -162,7 +146,3 @@ func (h *sentPacketHistory) Remove(p protocol.PacketNumber) error {
func (h *sentPacketHistory) HasOutstandingPackets() bool {
return h.numOutstandingPackets > 0
}
func (h *sentPacketHistory) HasOutstandingHandshakePackets() bool {
return h.numOutstandingHandshakePackets > 0
}

View File

@@ -1,43 +0,0 @@
package ackhandler
import (
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
)
// This stopWaitingManager is not supposed to satisfy the StopWaitingManager interface, which is a remnant of the legacy AckHandler, and should be remove once we drop support for QUIC 33
type stopWaitingManager struct {
largestLeastUnackedSent protocol.PacketNumber
nextLeastUnacked protocol.PacketNumber
lastStopWaitingFrame *wire.StopWaitingFrame
}
func (s *stopWaitingManager) GetStopWaitingFrame(force bool) *wire.StopWaitingFrame {
if s.nextLeastUnacked <= s.largestLeastUnackedSent {
if force {
return s.lastStopWaitingFrame
}
return nil
}
s.largestLeastUnackedSent = s.nextLeastUnacked
swf := &wire.StopWaitingFrame{
LeastUnacked: s.nextLeastUnacked,
}
s.lastStopWaitingFrame = swf
return swf
}
func (s *stopWaitingManager) ReceivedAck(ack *wire.AckFrame) {
largestAcked := ack.LargestAcked()
if largestAcked >= s.nextLeastUnacked {
s.nextLeastUnacked = largestAcked + 1
}
}
func (s *stopWaitingManager) QueuedRetransmissionForPacketNumber(p protocol.PacketNumber) {
if p >= s.nextLeastUnacked {
s.nextLeastUnacked = p + 1
}
}

View File

@@ -89,11 +89,7 @@ func (c *cubicSender) TimeUntilSend(bytesInFlight protocol.ByteCount) time.Durat
return 0
}
}
delay := c.rttStats.SmoothedRTT() / time.Duration(2*c.GetCongestionWindow())
if !c.InSlowStart() { // adjust delay, such that it's 1.25*cwd/rtt
delay = delay * 8 / 5
}
return delay
return c.rttStats.SmoothedRTT() * time.Duration(protocol.DefaultTCPMSS) / time.Duration(2*c.GetCongestionWindow())
}
func (c *cubicSender) OnPacketSent(

View File

@@ -1,10 +0,0 @@
package crypto
import "github.com/lucas-clemente/quic-go/internal/protocol"
// An AEAD implements QUIC's authenticated encryption and associated data
type AEAD interface {
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte
Overhead() int
}

View File

@@ -1,72 +0,0 @@
package crypto
import (
"crypto/cipher"
"encoding/binary"
"errors"
"github.com/lucas-clemente/aes12"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
type aeadAESGCM12 struct {
otherIV []byte
myIV []byte
encrypter cipher.AEAD
decrypter cipher.AEAD
}
var _ AEAD = &aeadAESGCM12{}
// NewAEADAESGCM12 creates a AEAD using AES-GCM with 12 bytes tag size
//
// AES-GCM support is a bit hacky, since the go stdlib does not support 12 byte
// tag size, and couples the cipher and aes packages closely.
// See https://github.com/lucas-clemente/aes12.
func NewAEADAESGCM12(otherKey []byte, myKey []byte, otherIV []byte, myIV []byte) (AEAD, error) {
if len(myKey) != 16 || len(otherKey) != 16 || len(myIV) != 4 || len(otherIV) != 4 {
return nil, errors.New("AES-GCM: expected 16-byte keys and 4-byte IVs")
}
encrypterCipher, err := aes12.NewCipher(myKey)
if err != nil {
return nil, err
}
encrypter, err := aes12.NewGCM(encrypterCipher)
if err != nil {
return nil, err
}
decrypterCipher, err := aes12.NewCipher(otherKey)
if err != nil {
return nil, err
}
decrypter, err := aes12.NewGCM(decrypterCipher)
if err != nil {
return nil, err
}
return &aeadAESGCM12{
otherIV: otherIV,
myIV: myIV,
encrypter: encrypter,
decrypter: decrypter,
}, nil
}
func (aead *aeadAESGCM12) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
return aead.decrypter.Open(dst, aead.makeNonce(aead.otherIV, packetNumber), src, associatedData)
}
func (aead *aeadAESGCM12) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
return aead.encrypter.Seal(dst, aead.makeNonce(aead.myIV, packetNumber), src, associatedData)
}
func (aead *aeadAESGCM12) makeNonce(iv []byte, packetNumber protocol.PacketNumber) []byte {
res := make([]byte, 12)
copy(res[0:4], iv)
binary.LittleEndian.PutUint64(res[4:12], uint64(packetNumber))
return res
}
func (aead *aeadAESGCM12) Overhead() int {
return aead.encrypter.Overhead()
}

View File

@@ -1,74 +0,0 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"errors"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
type aeadAESGCM struct {
otherIV []byte
myIV []byte
encrypter cipher.AEAD
decrypter cipher.AEAD
}
var _ AEAD = &aeadAESGCM{}
const ivLen = 12
// NewAEADAESGCM creates a AEAD using AES-GCM
func NewAEADAESGCM(otherKey []byte, myKey []byte, otherIV []byte, myIV []byte) (AEAD, error) {
// the IVs need to be at least 8 bytes long, otherwise we can't compute the nonce
if len(otherIV) != ivLen || len(myIV) != ivLen {
return nil, errors.New("AES-GCM: expected 12 byte IVs")
}
encrypterCipher, err := aes.NewCipher(myKey)
if err != nil {
return nil, err
}
encrypter, err := cipher.NewGCM(encrypterCipher)
if err != nil {
return nil, err
}
decrypterCipher, err := aes.NewCipher(otherKey)
if err != nil {
return nil, err
}
decrypter, err := cipher.NewGCM(decrypterCipher)
if err != nil {
return nil, err
}
return &aeadAESGCM{
otherIV: otherIV,
myIV: myIV,
encrypter: encrypter,
decrypter: decrypter,
}, nil
}
func (aead *aeadAESGCM) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
return aead.decrypter.Open(dst, aead.makeNonce(aead.otherIV, packetNumber), src, associatedData)
}
func (aead *aeadAESGCM) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
return aead.encrypter.Seal(dst, aead.makeNonce(aead.myIV, packetNumber), src, associatedData)
}
func (aead *aeadAESGCM) makeNonce(iv []byte, packetNumber protocol.PacketNumber) []byte {
nonce := make([]byte, ivLen)
binary.BigEndian.PutUint64(nonce[ivLen-8:], uint64(packetNumber))
for i := 0; i < ivLen; i++ {
nonce[i] ^= iv[i]
}
return nonce
}
func (aead *aeadAESGCM) Overhead() int {
return aead.encrypter.Overhead()
}

View File

@@ -1,48 +0,0 @@
package crypto
import (
"fmt"
"hash/fnv"
"github.com/hashicorp/golang-lru"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
var (
compressedCertsCache *lru.Cache
)
func getCompressedCert(chain [][]byte, pCommonSetHashes, pCachedHashes []byte) ([]byte, error) {
// Hash all inputs
hasher := fnv.New64a()
for _, v := range chain {
hasher.Write(v)
}
hasher.Write(pCommonSetHashes)
hasher.Write(pCachedHashes)
hash := hasher.Sum64()
var result []byte
resultI, isCached := compressedCertsCache.Get(hash)
if isCached {
result = resultI.([]byte)
} else {
var err error
result, err = compressChain(chain, pCommonSetHashes, pCachedHashes)
if err != nil {
return nil, err
}
compressedCertsCache.Add(hash, result)
}
return result, nil
}
func init() {
var err error
compressedCertsCache, err = lru.New(protocol.NumCachedCertificates)
if err != nil {
panic(fmt.Sprintf("fatal error in quic-go: could not create lru cache: %s", err.Error()))
}
}

View File

@@ -1,118 +0,0 @@
package crypto
import (
"crypto/tls"
"errors"
"strings"
)
// A CertChain holds a certificate and a private key
type CertChain interface {
SignServerProof(sni string, chlo []byte, serverConfigData []byte) ([]byte, error)
GetCertsCompressed(sni string, commonSetHashes, cachedHashes []byte) ([]byte, error)
GetLeafCert(sni string) ([]byte, error)
}
// proofSource stores a key and a certificate for the server proof
type certChain struct {
config *tls.Config
}
var _ CertChain = &certChain{}
var errNoMatchingCertificate = errors.New("no matching certificate found")
// NewCertChain loads the key and cert from files
func NewCertChain(tlsConfig *tls.Config) CertChain {
return &certChain{config: tlsConfig}
}
// SignServerProof signs CHLO and server config for use in the server proof
func (c *certChain) SignServerProof(sni string, chlo []byte, serverConfigData []byte) ([]byte, error) {
cert, err := c.getCertForSNI(sni)
if err != nil {
return nil, err
}
return signServerProof(cert, chlo, serverConfigData)
}
// GetCertsCompressed gets the certificate in the format described by the QUIC crypto doc
func (c *certChain) GetCertsCompressed(sni string, pCommonSetHashes, pCachedHashes []byte) ([]byte, error) {
cert, err := c.getCertForSNI(sni)
if err != nil {
return nil, err
}
return getCompressedCert(cert.Certificate, pCommonSetHashes, pCachedHashes)
}
// GetLeafCert gets the leaf certificate
func (c *certChain) GetLeafCert(sni string) ([]byte, error) {
cert, err := c.getCertForSNI(sni)
if err != nil {
return nil, err
}
return cert.Certificate[0], nil
}
func (c *certChain) getCertForSNI(sni string) (*tls.Certificate, error) {
conf, err := maybeGetConfigForClient(c.config, sni)
if err != nil {
return nil, err
}
// The rest of this function is mostly copied from crypto/tls.getCertificate
if conf.GetCertificate != nil {
cert, err := conf.GetCertificate(&tls.ClientHelloInfo{ServerName: sni})
if cert != nil || err != nil {
return cert, err
}
}
if len(conf.Certificates) == 0 {
return nil, errNoMatchingCertificate
}
if len(conf.Certificates) == 1 || conf.NameToCertificate == nil {
// There's only one choice, so no point doing any work.
return &conf.Certificates[0], nil
}
name := strings.ToLower(sni)
for len(name) > 0 && name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
if cert, ok := conf.NameToCertificate[name]; ok {
return cert, nil
}
// try replacing labels in the name with wildcards until we get a
// match.
labels := strings.Split(name, ".")
for i := range labels {
labels[i] = "*"
candidate := strings.Join(labels, ".")
if cert, ok := conf.NameToCertificate[candidate]; ok {
return cert, nil
}
}
// If nothing matches, return the first certificate.
return &conf.Certificates[0], nil
}
func maybeGetConfigForClient(c *tls.Config, sni string) (*tls.Config, error) {
if c.GetConfigForClient == nil {
return c, nil
}
confForClient, err := c.GetConfigForClient(&tls.ClientHelloInfo{ServerName: sni})
if err != nil {
return nil, err
}
// if GetConfigForClient returns nil, use the original config
if confForClient == nil {
return c, nil
}
return confForClient, nil
}

View File

@@ -1,272 +0,0 @@
package crypto
import (
"bytes"
"compress/flate"
"compress/zlib"
"encoding/binary"
"errors"
"fmt"
"hash/fnv"
"github.com/lucas-clemente/quic-go/internal/utils"
)
type entryType uint8
const (
entryCompressed entryType = 1
entryCached entryType = 2
entryCommon entryType = 3
)
type entry struct {
t entryType
h uint64 // set hash
i uint32 // index
}
func compressChain(chain [][]byte, pCommonSetHashes, pCachedHashes []byte) ([]byte, error) {
res := &bytes.Buffer{}
cachedHashes, err := splitHashes(pCachedHashes)
if err != nil {
return nil, err
}
setHashes, err := splitHashes(pCommonSetHashes)
if err != nil {
return nil, err
}
chainHashes := make([]uint64, len(chain))
for i := range chain {
chainHashes[i] = HashCert(chain[i])
}
entries := buildEntries(chain, chainHashes, cachedHashes, setHashes)
totalUncompressedLen := 0
for i, e := range entries {
res.WriteByte(uint8(e.t))
switch e.t {
case entryCached:
utils.LittleEndian.WriteUint64(res, e.h)
case entryCommon:
utils.LittleEndian.WriteUint64(res, e.h)
utils.LittleEndian.WriteUint32(res, e.i)
case entryCompressed:
totalUncompressedLen += 4 + len(chain[i])
}
}
res.WriteByte(0) // end of list
if totalUncompressedLen > 0 {
gz, err := zlib.NewWriterLevelDict(res, flate.BestCompression, buildZlibDictForEntries(entries, chain))
if err != nil {
return nil, fmt.Errorf("cert compression failed: %s", err.Error())
}
utils.LittleEndian.WriteUint32(res, uint32(totalUncompressedLen))
for i, e := range entries {
if e.t != entryCompressed {
continue
}
lenCert := len(chain[i])
gz.Write([]byte{
byte(lenCert & 0xff),
byte((lenCert >> 8) & 0xff),
byte((lenCert >> 16) & 0xff),
byte((lenCert >> 24) & 0xff),
})
gz.Write(chain[i])
}
gz.Close()
}
return res.Bytes(), nil
}
func decompressChain(data []byte) ([][]byte, error) {
var chain [][]byte
var entries []entry
r := bytes.NewReader(data)
var numCerts int
var hasCompressedCerts bool
for {
entryTypeByte, err := r.ReadByte()
if entryTypeByte == 0 {
break
}
et := entryType(entryTypeByte)
if err != nil {
return nil, err
}
numCerts++
switch et {
case entryCached:
// we're not sending any certificate hashes in the CHLO, so there shouldn't be any cached certificates in the chain
return nil, errors.New("unexpected cached certificate")
case entryCommon:
e := entry{t: entryCommon}
e.h, err = utils.LittleEndian.ReadUint64(r)
if err != nil {
return nil, err
}
e.i, err = utils.LittleEndian.ReadUint32(r)
if err != nil {
return nil, err
}
certSet, ok := certSets[e.h]
if !ok {
return nil, errors.New("unknown certSet")
}
if e.i >= uint32(len(certSet)) {
return nil, errors.New("certificate not found in certSet")
}
entries = append(entries, e)
chain = append(chain, certSet[e.i])
case entryCompressed:
hasCompressedCerts = true
entries = append(entries, entry{t: entryCompressed})
chain = append(chain, nil)
default:
return nil, errors.New("unknown entryType")
}
}
if numCerts == 0 {
return make([][]byte, 0), nil
}
if hasCompressedCerts {
uncompressedLength, err := utils.LittleEndian.ReadUint32(r)
if err != nil {
fmt.Println(4)
return nil, err
}
zlibDict := buildZlibDictForEntries(entries, chain)
gz, err := zlib.NewReaderDict(r, zlibDict)
if err != nil {
return nil, err
}
defer gz.Close()
var totalLength uint32
var certIndex int
for totalLength < uncompressedLength {
lenBytes := make([]byte, 4)
_, err := gz.Read(lenBytes)
if err != nil {
return nil, err
}
certLen := binary.LittleEndian.Uint32(lenBytes)
cert := make([]byte, certLen)
n, err := gz.Read(cert)
if uint32(n) != certLen && err != nil {
return nil, err
}
for {
if certIndex >= len(entries) {
return nil, errors.New("CertCompression BUG: no element to save uncompressed certificate")
}
if entries[certIndex].t == entryCompressed {
chain[certIndex] = cert
certIndex++
break
}
certIndex++
}
totalLength += 4 + certLen
}
}
return chain, nil
}
func buildEntries(chain [][]byte, chainHashes, cachedHashes, setHashes []uint64) []entry {
res := make([]entry, len(chain))
chainLoop:
for i := range chain {
// Check if hash is in cachedHashes
for j := range cachedHashes {
if chainHashes[i] == cachedHashes[j] {
res[i] = entry{t: entryCached, h: chainHashes[i]}
continue chainLoop
}
}
// Go through common sets and check if it's in there
for _, setHash := range setHashes {
set, ok := certSets[setHash]
if !ok {
// We don't have this set
continue
}
// We have this set, check if chain[i] is in the set
pos := set.findCertInSet(chain[i])
if pos >= 0 {
// Found
res[i] = entry{t: entryCommon, h: setHash, i: uint32(pos)}
continue chainLoop
}
}
res[i] = entry{t: entryCompressed}
}
return res
}
func buildZlibDictForEntries(entries []entry, chain [][]byte) []byte {
var dict bytes.Buffer
// First the cached and common in reverse order
for i := len(entries) - 1; i >= 0; i-- {
if entries[i].t == entryCompressed {
continue
}
dict.Write(chain[i])
}
dict.Write(certDictZlib)
return dict.Bytes()
}
func splitHashes(hashes []byte) ([]uint64, error) {
if len(hashes)%8 != 0 {
return nil, errors.New("expected a multiple of 8 bytes for CCS / CCRT hashes")
}
n := len(hashes) / 8
res := make([]uint64, n)
for i := 0; i < n; i++ {
res[i] = binary.LittleEndian.Uint64(hashes[i*8 : (i+1)*8])
}
return res, nil
}
func getCommonCertificateHashes() []byte {
ccs := make([]byte, 8*len(certSets))
i := 0
for certSetHash := range certSets {
binary.LittleEndian.PutUint64(ccs[i*8:(i+1)*8], certSetHash)
i++
}
return ccs
}
// HashCert calculates the FNV1a hash of a certificate
func HashCert(cert []byte) uint64 {
h := fnv.New64a()
h.Write(cert)
return h.Sum64()
}

View File

@@ -1,128 +0,0 @@
package crypto
var certDictZlib = []byte{
0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01,
0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07,
0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34,
0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e,
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65,
0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06,
0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2,
0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e,
0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03,
0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33,
0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37,
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c,
0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08,
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17,
0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72,
0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65,
0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74,
0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68,
0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76,
0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e,
0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11,
0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01,
0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50,
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30,
0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61,
0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56,
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31,
0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65,
0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63,
0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72,
0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a,
0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72,
0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72,
0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e,
0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63,
0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b,
0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06,
0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68,
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72,
0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee,
0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27,
0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
}

View File

@@ -1,135 +0,0 @@
package crypto
import (
"crypto/tls"
"crypto/x509"
"errors"
"hash/fnv"
"time"
"github.com/lucas-clemente/quic-go/qerr"
)
// CertManager manages the certificates sent by the server
type CertManager interface {
SetData([]byte) error
GetCommonCertificateHashes() []byte
GetLeafCert() []byte
GetLeafCertHash() (uint64, error)
VerifyServerProof(proof, chlo, serverConfigData []byte) bool
Verify(hostname string) error
GetChain() []*x509.Certificate
}
type certManager struct {
chain []*x509.Certificate
config *tls.Config
}
var _ CertManager = &certManager{}
var errNoCertificateChain = errors.New("CertManager BUG: No certicifate chain loaded")
// NewCertManager creates a new CertManager
func NewCertManager(tlsConfig *tls.Config) CertManager {
return &certManager{config: tlsConfig}
}
// SetData takes the byte-slice sent in the SHLO and decompresses it into the certificate chain
func (c *certManager) SetData(data []byte) error {
byteChain, err := decompressChain(data)
if err != nil {
return qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")
}
chain := make([]*x509.Certificate, len(byteChain))
for i, data := range byteChain {
cert, err := x509.ParseCertificate(data)
if err != nil {
return err
}
chain[i] = cert
}
c.chain = chain
return nil
}
func (c *certManager) GetChain() []*x509.Certificate {
return c.chain
}
func (c *certManager) GetCommonCertificateHashes() []byte {
return getCommonCertificateHashes()
}
// GetLeafCert returns the leaf certificate of the certificate chain
// it returns nil if the certificate chain has not yet been set
func (c *certManager) GetLeafCert() []byte {
if len(c.chain) == 0 {
return nil
}
return c.chain[0].Raw
}
// GetLeafCertHash calculates the FNV1a_64 hash of the leaf certificate
func (c *certManager) GetLeafCertHash() (uint64, error) {
leafCert := c.GetLeafCert()
if leafCert == nil {
return 0, errNoCertificateChain
}
h := fnv.New64a()
_, err := h.Write(leafCert)
if err != nil {
return 0, err
}
return h.Sum64(), nil
}
// VerifyServerProof verifies the signature of the server config
// it should only be called after the certificate chain has been set, otherwise it returns false
func (c *certManager) VerifyServerProof(proof, chlo, serverConfigData []byte) bool {
if len(c.chain) == 0 {
return false
}
return verifyServerProof(proof, c.chain[0], chlo, serverConfigData)
}
// Verify verifies the certificate chain
func (c *certManager) Verify(hostname string) error {
if len(c.chain) == 0 {
return errNoCertificateChain
}
if c.config != nil && c.config.InsecureSkipVerify {
return nil
}
leafCert := c.chain[0]
var opts x509.VerifyOptions
if c.config != nil {
opts.Roots = c.config.RootCAs
if c.config.Time == nil {
opts.CurrentTime = time.Now()
} else {
opts.CurrentTime = c.config.Time()
}
}
// we don't need to care about the tls.Config.ServerName here, since hostname has already been set to that value in the session setup
opts.DNSName = hostname
// the first certificate is the leaf certificate, all others are intermediates
if len(c.chain) > 1 {
intermediates := x509.NewCertPool()
for i := 1; i < len(c.chain); i++ {
intermediates.AddCert(c.chain[i])
}
opts.Intermediates = intermediates
}
_, err := leafCert.Verify(opts)
return err
}

View File

@@ -1,24 +0,0 @@
package crypto
import (
"bytes"
"github.com/lucas-clemente/quic-go-certificates"
)
type certSet [][]byte
var certSets = map[uint64]certSet{
certsets.CertSet2Hash: certsets.CertSet2,
certsets.CertSet3Hash: certsets.CertSet3,
}
// findCertInSet searches for the cert in the set. Negative return value means not found.
func (s *certSet) findCertInSet(cert []byte) int {
for i, c := range *s {
if bytes.Equal(c, cert) {
return i
}
}
return -1
}

View File

@@ -1,61 +0,0 @@
// +build ignore
package crypto
import (
"crypto/cipher"
"encoding/binary"
"errors"
"github.com/aead/chacha20"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
type aeadChacha20Poly1305 struct {
otherIV []byte
myIV []byte
encrypter cipher.AEAD
decrypter cipher.AEAD
}
// NewAEADChacha20Poly1305 creates a AEAD using chacha20poly1305
func NewAEADChacha20Poly1305(otherKey []byte, myKey []byte, otherIV []byte, myIV []byte) (AEAD, error) {
if len(myKey) != 32 || len(otherKey) != 32 || len(myIV) != 4 || len(otherIV) != 4 {
return nil, errors.New("chacha20poly1305: expected 32-byte keys and 4-byte IVs")
}
// copy because ChaCha20Poly1305 expects array pointers
var MyKey, OtherKey [32]byte
copy(MyKey[:], myKey)
copy(OtherKey[:], otherKey)
encrypter, err := chacha20.NewChaCha20Poly1305WithTagSize(&MyKey, 12)
if err != nil {
return nil, err
}
decrypter, err := chacha20.NewChaCha20Poly1305WithTagSize(&OtherKey, 12)
if err != nil {
return nil, err
}
return &aeadChacha20Poly1305{
otherIV: otherIV,
myIV: myIV,
encrypter: encrypter,
decrypter: decrypter,
}, nil
}
func (aead *aeadChacha20Poly1305) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
return aead.decrypter.Open(dst, aead.makeNonce(aead.otherIV, packetNumber), src, associatedData)
}
func (aead *aeadChacha20Poly1305) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
return aead.encrypter.Seal(dst, aead.makeNonce(aead.myIV, packetNumber), src, associatedData)
}
func (aead *aeadChacha20Poly1305) makeNonce(iv []byte, packetNumber protocol.PacketNumber) []byte {
res := make([]byte, 12)
copy(res[0:4], iv)
binary.LittleEndian.PutUint64(res[4:12], uint64(packetNumber))
return res
}

View File

@@ -1,41 +0,0 @@
package crypto
import (
"crypto/rand"
"errors"
"golang.org/x/crypto/curve25519"
)
// KeyExchange manages the exchange of keys
type curve25519KEX struct {
secret [32]byte
public [32]byte
}
var _ KeyExchange = &curve25519KEX{}
// NewCurve25519KEX creates a new KeyExchange using Curve25519, see https://cr.yp.to/ecdh.html
func NewCurve25519KEX() (KeyExchange, error) {
c := &curve25519KEX{}
if _, err := rand.Read(c.secret[:]); err != nil {
return nil, errors.New("Curve25519: could not create private key")
}
curve25519.ScalarBaseMult(&c.public, &c.secret)
return c, nil
}
func (c *curve25519KEX) PublicKey() []byte {
return c.public[:]
}
func (c *curve25519KEX) CalculateSharedKey(otherPublic []byte) ([]byte, error) {
if len(otherPublic) != 32 {
return nil, errors.New("Curve25519: expected public key of 32 byte")
}
var res [32]byte
var otherPublicArray [32]byte
copy(otherPublicArray[:], otherPublic)
curve25519.ScalarMult(&res, &c.secret, &otherPublicArray)
return res[:], nil
}

View File

@@ -1,56 +0,0 @@
package crypto
import (
"crypto"
"crypto/hmac"
"encoding/binary"
)
// copied from https://github.com/cloudflare/tls-tris/blob/master/hkdf.go
func hkdfExtract(hash crypto.Hash, secret, salt []byte) []byte {
if salt == nil {
salt = make([]byte, hash.Size())
}
if secret == nil {
secret = make([]byte, hash.Size())
}
extractor := hmac.New(hash.New, salt)
extractor.Write(secret)
return extractor.Sum(nil)
}
// copied from https://github.com/cloudflare/tls-tris/blob/master/hkdf.go
func hkdfExpand(hash crypto.Hash, prk, info []byte, l int) []byte {
var (
expander = hmac.New(hash.New, prk)
res = make([]byte, l)
counter = byte(1)
prev []byte
)
if l > 255*expander.Size() {
panic("hkdf: requested too much output")
}
p := res
for len(p) > 0 {
expander.Reset()
expander.Write(prev)
expander.Write(info)
expander.Write([]byte{counter})
prev = expander.Sum(prev[:0])
counter++
n := copy(p, prev)
p = p[n:]
}
return res
}
func qhkdfExpand(secret []byte, label string, length int) []byte {
qlabel := make([]byte, 2+1+5+len(label))
binary.BigEndian.PutUint16(qlabel[0:2], uint16(length))
qlabel[2] = uint8(5 + len(label))
copy(qlabel[3:], []byte("QUIC "+label))
return hkdfExpand(crypto.SHA256, secret, qlabel, length)
}

View File

@@ -1,49 +0,0 @@
package crypto
import (
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
const (
clientExporterLabel = "EXPORTER-QUIC client 1rtt"
serverExporterLabel = "EXPORTER-QUIC server 1rtt"
)
// A TLSExporter gets the negotiated ciphersuite and computes exporter
type TLSExporter interface {
ConnectionState() mint.ConnectionState
ComputeExporter(label string, context []byte, keyLength int) ([]byte, error)
}
// DeriveAESKeys derives the AES keys and creates a matching AES-GCM AEAD instance
func DeriveAESKeys(tls TLSExporter, pers protocol.Perspective) (AEAD, error) {
var myLabel, otherLabel string
if pers == protocol.PerspectiveClient {
myLabel = clientExporterLabel
otherLabel = serverExporterLabel
} else {
myLabel = serverExporterLabel
otherLabel = clientExporterLabel
}
myKey, myIV, err := computeKeyAndIV(tls, myLabel)
if err != nil {
return nil, err
}
otherKey, otherIV, err := computeKeyAndIV(tls, otherLabel)
if err != nil {
return nil, err
}
return NewAEADAESGCM(otherKey, myKey, otherIV, myIV)
}
func computeKeyAndIV(tls TLSExporter, label string) (key, iv []byte, err error) {
cs := tls.ConnectionState().CipherSuite
secret, err := tls.ComputeExporter(label, nil, cs.Hash.Size())
if err != nil {
return nil, nil, err
}
key = qhkdfExpand(secret, "key", cs.KeyLen)
iv = qhkdfExpand(secret, "iv", cs.IvLen)
return key, iv, nil
}

View File

@@ -1,100 +0,0 @@
package crypto
import (
"bytes"
"crypto/sha256"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"golang.org/x/crypto/hkdf"
)
// DeriveKeysChacha20 derives the client and server keys and creates a matching chacha20poly1305 AEAD instance
// func DeriveKeysChacha20(version protocol.VersionNumber, forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte) (AEAD, error) {
// otherKey, myKey, otherIV, myIV, err := deriveKeys(version, forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 32)
// if err != nil {
// return nil, err
// }
// return NewAEADChacha20Poly1305(otherKey, myKey, otherIV, myIV)
// }
// DeriveQuicCryptoAESKeys derives the client and server keys and creates a matching AES-GCM AEAD instance
func DeriveQuicCryptoAESKeys(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (AEAD, error) {
var swap bool
if pers == protocol.PerspectiveClient {
swap = true
}
otherKey, myKey, otherIV, myIV, err := deriveKeys(forwardSecure, sharedSecret, nonces, connID, chlo, scfg, cert, divNonce, 16, swap)
if err != nil {
return nil, err
}
return NewAEADAESGCM12(otherKey, myKey, otherIV, myIV)
}
// deriveKeys derives the keys and the IVs
// swap should be set true if generating the values for the client, and false for the server
func deriveKeys(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo, scfg, cert, divNonce []byte, keyLen int, swap bool) ([]byte, []byte, []byte, []byte, error) {
var info bytes.Buffer
if forwardSecure {
info.Write([]byte("QUIC forward secure key expansion\x00"))
} else {
info.Write([]byte("QUIC key expansion\x00"))
}
info.Write(connID)
info.Write(chlo)
info.Write(scfg)
info.Write(cert)
r := hkdf.New(sha256.New, sharedSecret, nonces, info.Bytes())
s := make([]byte, 2*keyLen+2*4)
if _, err := io.ReadFull(r, s); err != nil {
return nil, nil, nil, nil, err
}
key1 := s[:keyLen]
key2 := s[keyLen : 2*keyLen]
iv1 := s[2*keyLen : 2*keyLen+4]
iv2 := s[2*keyLen+4:]
var otherKey, myKey []byte
var otherIV, myIV []byte
if !forwardSecure {
if err := diversify(key2, iv2, divNonce); err != nil {
return nil, nil, nil, nil, err
}
}
if swap {
otherKey = key2
myKey = key1
otherIV = iv2
myIV = iv1
} else {
otherKey = key1
myKey = key2
otherIV = iv1
myIV = iv2
}
return otherKey, myKey, otherIV, myIV, nil
}
func diversify(key, iv, divNonce []byte) error {
secret := make([]byte, len(key)+len(iv))
copy(secret, key)
copy(secret[len(key):], iv)
r := hkdf.New(sha256.New, secret, divNonce, []byte("QUIC key diversification"))
if _, err := io.ReadFull(r, key); err != nil {
return err
}
if _, err := io.ReadFull(r, iv); err != nil {
return err
}
return nil
}

View File

@@ -1,7 +0,0 @@
package crypto
// KeyExchange manages the exchange of keys
type KeyExchange interface {
PublicKey() []byte
CalculateSharedKey(otherPublic []byte) ([]byte, error)
}

View File

@@ -1,11 +0,0 @@
package crypto
import "github.com/lucas-clemente/quic-go/internal/protocol"
// NewNullAEAD creates a NullAEAD
func NewNullAEAD(p protocol.Perspective, connID protocol.ConnectionID, v protocol.VersionNumber) (AEAD, error) {
if v.UsesTLS() {
return newNullAEADAESGCM(connID, p)
}
return &nullAEADFNV128a{perspective: p}, nil
}

View File

@@ -1,40 +0,0 @@
package crypto
import (
"crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
var quicVersion1Salt = []byte{0x9c, 0x10, 0x8f, 0x98, 0x52, 0x0a, 0x5c, 0x5c, 0x32, 0x96, 0x8e, 0x95, 0x0e, 0x8a, 0x2c, 0x5f, 0xe0, 0x6d, 0x6c, 0x38}
func newNullAEADAESGCM(connectionID protocol.ConnectionID, pers protocol.Perspective) (AEAD, error) {
clientSecret, serverSecret := computeSecrets(connectionID)
var mySecret, otherSecret []byte
if pers == protocol.PerspectiveClient {
mySecret = clientSecret
otherSecret = serverSecret
} else {
mySecret = serverSecret
otherSecret = clientSecret
}
myKey, myIV := computeNullAEADKeyAndIV(mySecret)
otherKey, otherIV := computeNullAEADKeyAndIV(otherSecret)
return NewAEADAESGCM(otherKey, myKey, otherIV, myIV)
}
func computeSecrets(connID protocol.ConnectionID) (clientSecret, serverSecret []byte) {
handshakeSecret := hkdfExtract(crypto.SHA256, connID, quicVersion1Salt)
clientSecret = qhkdfExpand(handshakeSecret, "client hs", crypto.SHA256.Size())
serverSecret = qhkdfExpand(handshakeSecret, "server hs", crypto.SHA256.Size())
return
}
func computeNullAEADKeyAndIV(secret []byte) (key, iv []byte) {
key = qhkdfExpand(secret, "key", 16)
iv = qhkdfExpand(secret, "iv", 12)
return
}

View File

@@ -1,79 +0,0 @@
package crypto
import (
"bytes"
"errors"
"fmt"
"hash/fnv"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
// nullAEAD handles not-yet encrypted packets
type nullAEADFNV128a struct {
perspective protocol.Perspective
}
var _ AEAD = &nullAEADFNV128a{}
// Open and verify the ciphertext
func (n *nullAEADFNV128a) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
if len(src) < 12 {
return nil, errors.New("NullAEAD: ciphertext cannot be less than 12 bytes long")
}
hash := fnv.New128a()
hash.Write(associatedData)
hash.Write(src[12:])
if n.perspective == protocol.PerspectiveServer {
hash.Write([]byte("Client"))
} else {
hash.Write([]byte("Server"))
}
sum := make([]byte, 0, 16)
sum = hash.Sum(sum)
// The tag is written in little endian, so we need to reverse the slice.
reverse(sum)
if !bytes.Equal(sum[:12], src[:12]) {
return nil, fmt.Errorf("NullAEAD: failed to authenticate received data (%#v vs %#v)", sum[:12], src[:12])
}
return src[12:], nil
}
// Seal writes hash and ciphertext to the buffer
func (n *nullAEADFNV128a) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
if cap(dst) < 12+len(src) {
dst = make([]byte, 12+len(src))
} else {
dst = dst[:12+len(src)]
}
hash := fnv.New128a()
hash.Write(associatedData)
hash.Write(src)
if n.perspective == protocol.PerspectiveServer {
hash.Write([]byte("Server"))
} else {
hash.Write([]byte("Client"))
}
sum := make([]byte, 0, 16)
sum = hash.Sum(sum)
// The tag is written in little endian, so we need to reverse the slice.
reverse(sum)
copy(dst[12:], src)
copy(dst, sum[:12])
return dst
}
func (n *nullAEADFNV128a) Overhead() int {
return 12
}
func reverse(a []byte) {
for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
a[left], a[right] = a[right], a[left]
}
}

View File

@@ -1,66 +0,0 @@
package crypto
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"errors"
"math/big"
)
type ecdsaSignature struct {
R, S *big.Int
}
// signServerProof signs CHLO and server config for use in the server proof
func signServerProof(cert *tls.Certificate, chlo []byte, serverConfigData []byte) ([]byte, error) {
hash := sha256.New()
hash.Write([]byte("QUIC CHLO and server config signature\x00"))
chloHash := sha256.Sum256(chlo)
hash.Write([]byte{32, 0, 0, 0})
hash.Write(chloHash[:])
hash.Write(serverConfigData)
key, ok := cert.PrivateKey.(crypto.Signer)
if !ok {
return nil, errors.New("expected PrivateKey to implement crypto.Signer")
}
opts := crypto.SignerOpts(crypto.SHA256)
if _, ok = key.(*rsa.PrivateKey); ok {
opts = &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}
}
return key.Sign(rand.Reader, hash.Sum(nil), opts)
}
// verifyServerProof verifies the server proof signature
func verifyServerProof(proof []byte, cert *x509.Certificate, chlo []byte, serverConfigData []byte) bool {
hash := sha256.New()
hash.Write([]byte("QUIC CHLO and server config signature\x00"))
chloHash := sha256.Sum256(chlo)
hash.Write([]byte{32, 0, 0, 0})
hash.Write(chloHash[:])
hash.Write(serverConfigData)
// RSA
if cert.PublicKeyAlgorithm == x509.RSA {
opts := &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}
err := rsa.VerifyPSS(cert.PublicKey.(*rsa.PublicKey), crypto.SHA256, hash.Sum(nil), proof, opts)
return err == nil
}
// ECDSA
signature := &ecdsaSignature{}
rest, err := asn1.Unmarshal(proof, signature)
if err != nil || len(rest) != 0 {
return false
}
return ecdsa.Verify(cert.PublicKey.(*ecdsa.PublicKey), hash.Sum(nil), signature.R, signature.S)
}

View File

@@ -5,8 +5,8 @@ import (
"github.com/lucas-clemente/quic-go/internal/congestion"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
type connectionFlowController struct {
@@ -49,12 +49,17 @@ func (c *connectionFlowController) IncrementHighestReceived(increment protocol.B
c.highestReceived += increment
if c.checkFlowControlViolation() {
return qerr.Error(qerr.FlowControlReceivedTooMuchData, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow))
return qerr.Error(qerr.FlowControlError, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow))
}
return nil
}
func (c *connectionFlowController) MaybeQueueWindowUpdate() {
func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) {
c.baseFlowController.AddBytesRead(n)
c.maybeQueueWindowUpdate()
}
func (c *connectionFlowController) maybeQueueWindowUpdate() {
c.mutex.Lock()
hasWindowUpdate := c.hasWindowUpdate()
c.mutex.Unlock()

View File

@@ -10,7 +10,6 @@ type flowController interface {
// for receiving
AddBytesRead(protocol.ByteCount)
GetWindowUpdate() protocol.ByteCount // returns 0 if no update is necessary
MaybeQueueWindowUpdate() // queues a window update, if necessary
IsNewlyBlocked() (bool, protocol.ByteCount)
}
@@ -19,8 +18,12 @@ type StreamFlowController interface {
flowController
// for receiving
// UpdateHighestReceived should be called when a new highest offset is received
// final has to be to true if this is the final offset of the stream, as contained in a STREAM frame with FIN bit, and the RST_STREAM frame
// final has to be to true if this is the final offset of the stream,
// as contained in a STREAM frame with FIN bit, and the RESET_STREAM frame
UpdateHighestReceived(offset protocol.ByteCount, final bool) error
// Abandon should be called when reading from the stream is aborted early,
// and there won't be any further calls to AddBytesRead.
Abandon()
}
// The ConnectionFlowController is the flow controller for the connection.

View File

@@ -5,8 +5,8 @@ import (
"github.com/lucas-clemente/quic-go/internal/congestion"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
type streamFlowController struct {
@@ -16,8 +16,7 @@ type streamFlowController struct {
queueWindowUpdate func()
connection connectionFlowControllerI
contributesToConnection bool // does the stream contribute to connection level flow control
connection connectionFlowControllerI
receivedFinalOffset bool
}
@@ -27,7 +26,6 @@ var _ StreamFlowController = &streamFlowController{}
// NewStreamFlowController gets a new flow controller for a stream
func NewStreamFlowController(
streamID protocol.StreamID,
contributesToConnection bool,
cfc ConnectionFlowController,
receiveWindow protocol.ByteCount,
maxReceiveWindow protocol.ByteCount,
@@ -37,10 +35,9 @@ func NewStreamFlowController(
logger utils.Logger,
) StreamFlowController {
return &streamFlowController{
streamID: streamID,
contributesToConnection: contributesToConnection,
connection: cfc.(connectionFlowControllerI),
queueWindowUpdate: func() { queueWindowUpdate(streamID) },
streamID: streamID,
connection: cfc.(connectionFlowControllerI),
queueWindowUpdate: func() { queueWindowUpdate(streamID) },
baseFlowController: baseFlowController{
rttStats: rttStats,
receiveWindow: receiveWindow,
@@ -52,79 +49,74 @@ func NewStreamFlowController(
}
}
// UpdateHighestReceived updates the highestReceived value, if the byteOffset is higher
// it returns an ErrReceivedSmallerByteOffset if the received byteOffset is smaller than any byteOffset received before
func (c *streamFlowController) UpdateHighestReceived(byteOffset protocol.ByteCount, final bool) error {
// UpdateHighestReceived updates the highestReceived value, if the offset is higher.
func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool) error {
c.mutex.Lock()
defer c.mutex.Unlock()
// when receiving a final offset, check that this final offset is consistent with a final offset we might have received earlier
if final && c.receivedFinalOffset && byteOffset != c.highestReceived {
return qerr.Error(qerr.StreamDataAfterTermination, fmt.Sprintf("Received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, byteOffset))
}
// if we already received a final offset, check that the offset in the STREAM frames is below the final offset
if c.receivedFinalOffset && byteOffset > c.highestReceived {
return qerr.StreamDataAfterTermination
// If the final offset for this stream is already known, check for consistency.
if c.receivedFinalOffset {
// If we receive another final offset, check that it's the same.
if final && offset != c.highestReceived {
return qerr.Error(qerr.FinalSizeError, fmt.Sprintf("Received inconsistent final offset for stream %d (old: %#x, new: %#x bytes)", c.streamID, c.highestReceived, offset))
}
// Check that the offset is below the final offset.
if offset > c.highestReceived {
return qerr.Error(qerr.FinalSizeError, fmt.Sprintf("Received offset %#x for stream %d. Final offset was already received at %#x", offset, c.streamID, c.highestReceived))
}
}
if final {
c.receivedFinalOffset = true
}
if byteOffset == c.highestReceived {
if offset == c.highestReceived {
return nil
}
if byteOffset <= c.highestReceived {
// a STREAM_FRAME with a higher offset was received before.
// A higher offset was received before.
// This can happen due to reordering.
if offset <= c.highestReceived {
if final {
// If the current byteOffset is smaller than the offset in that STREAM_FRAME, this STREAM_FRAME contained data after the end of the stream
return qerr.StreamDataAfterTermination
return qerr.Error(qerr.FinalSizeError, fmt.Sprintf("Received final offset %#x for stream %d, but already received offset %#x before", offset, c.streamID, c.highestReceived))
}
// this is a reordered STREAM_FRAME
return nil
}
increment := byteOffset - c.highestReceived
c.highestReceived = byteOffset
increment := offset - c.highestReceived
c.highestReceived = offset
if c.checkFlowControlViolation() {
return qerr.Error(qerr.FlowControlReceivedTooMuchData, fmt.Sprintf("Received %d bytes on stream %d, allowed %d bytes", byteOffset, c.streamID, c.receiveWindow))
return qerr.Error(qerr.FlowControlError, fmt.Sprintf("Received %#x bytes on stream %d, allowed %#x bytes", offset, c.streamID, c.receiveWindow))
}
if c.contributesToConnection {
return c.connection.IncrementHighestReceived(increment)
}
return nil
return c.connection.IncrementHighestReceived(increment)
}
func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) {
c.baseFlowController.AddBytesRead(n)
if c.contributesToConnection {
c.connection.AddBytesRead(n)
c.maybeQueueWindowUpdate()
c.connection.AddBytesRead(n)
}
func (c *streamFlowController) Abandon() {
if unread := c.highestReceived - c.bytesRead; unread > 0 {
c.connection.AddBytesRead(unread)
}
}
func (c *streamFlowController) AddBytesSent(n protocol.ByteCount) {
c.baseFlowController.AddBytesSent(n)
if c.contributesToConnection {
c.connection.AddBytesSent(n)
}
c.connection.AddBytesSent(n)
}
func (c *streamFlowController) SendWindowSize() protocol.ByteCount {
window := c.baseFlowController.sendWindowSize()
if c.contributesToConnection {
window = utils.MinByteCount(window, c.connection.SendWindowSize())
}
return window
return utils.MinByteCount(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize())
}
func (c *streamFlowController) MaybeQueueWindowUpdate() {
func (c *streamFlowController) maybeQueueWindowUpdate() {
c.mutex.Lock()
hasWindowUpdate := !c.receivedFinalOffset && c.hasWindowUpdate()
c.mutex.Unlock()
if hasWindowUpdate {
c.queueWindowUpdate()
}
if c.contributesToConnection {
c.connection.MaybeQueueWindowUpdate()
}
}
func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount {
@@ -140,9 +132,7 @@ func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount {
offset := c.baseFlowController.getWindowUpdate()
if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size
c.logger.Debugf("Increasing receive flow control window for stream %d to %d kB", c.streamID, c.receiveWindowSize/(1<<10))
if c.contributesToConnection {
c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier))
}
c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier))
}
c.mutex.Unlock()
return offset

View File

@@ -0,0 +1,104 @@
package handshake
import (
"crypto/cipher"
"encoding/binary"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
type sealer struct {
aead cipher.AEAD
hpEncrypter cipher.Block
// use a single slice to avoid allocations
nonceBuf []byte
hpMask []byte
// short headers protect 5 bits in the first byte, long headers only 4
is1RTT bool
}
var _ Sealer = &sealer{}
func newSealer(aead cipher.AEAD, hpEncrypter cipher.Block, is1RTT bool) Sealer {
return &sealer{
aead: aead,
nonceBuf: make([]byte, aead.NonceSize()),
is1RTT: is1RTT,
hpEncrypter: hpEncrypter,
hpMask: make([]byte, hpEncrypter.BlockSize()),
}
}
func (s *sealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte {
binary.BigEndian.PutUint64(s.nonceBuf[len(s.nonceBuf)-8:], uint64(pn))
// The AEAD we're using here will be the qtls.aeadAESGCM13.
// It uses the nonce provided here and XOR it with the IV.
return s.aead.Seal(dst, s.nonceBuf, src, ad)
}
func (s *sealer) EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) {
if len(sample) != s.hpEncrypter.BlockSize() {
panic("invalid sample size")
}
s.hpEncrypter.Encrypt(s.hpMask, sample)
if s.is1RTT {
*firstByte ^= s.hpMask[0] & 0x1f
} else {
*firstByte ^= s.hpMask[0] & 0xf
}
for i := range pnBytes {
pnBytes[i] ^= s.hpMask[i+1]
}
}
func (s *sealer) Overhead() int {
return s.aead.Overhead()
}
type opener struct {
aead cipher.AEAD
pnDecrypter cipher.Block
// use a single slice to avoid allocations
nonceBuf []byte
hpMask []byte
// short headers protect 5 bits in the first byte, long headers only 4
is1RTT bool
}
var _ Opener = &opener{}
func newOpener(aead cipher.AEAD, pnDecrypter cipher.Block, is1RTT bool) Opener {
return &opener{
aead: aead,
nonceBuf: make([]byte, aead.NonceSize()),
is1RTT: is1RTT,
pnDecrypter: pnDecrypter,
hpMask: make([]byte, pnDecrypter.BlockSize()),
}
}
func (o *opener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) {
binary.BigEndian.PutUint64(o.nonceBuf[len(o.nonceBuf)-8:], uint64(pn))
// The AEAD we're using here will be the qtls.aeadAESGCM13.
// It uses the nonce provided here and XOR it with the IV.
return o.aead.Open(dst, o.nonceBuf, src, ad)
}
func (o *opener) DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) {
if len(sample) != o.pnDecrypter.BlockSize() {
panic("invalid sample size")
}
o.pnDecrypter.Encrypt(o.hpMask, sample)
if o.is1RTT {
*firstByte ^= o.hpMask[0] & 0x1f
} else {
*firstByte ^= o.hpMask[0] & 0xf
}
for i := range pnBytes {
pnBytes[i] ^= o.hpMask[i+1]
}
}

View File

@@ -5,6 +5,8 @@ import (
"fmt"
"net"
"time"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
const (
@@ -14,14 +16,17 @@ const (
// A Cookie is derived from the client address and can be used to verify the ownership of this address.
type Cookie struct {
RemoteAddr string
// The time that the STK was issued (resolution 1 second)
RemoteAddr string
OriginalDestConnectionID protocol.ConnectionID
// The time that the Cookie was issued (resolution 1 second)
SentTime time.Time
}
// token is the struct that is used for ASN1 serialization and deserialization
type token struct {
Data []byte
RemoteAddr []byte
OriginalDestConnectionID []byte
Timestamp int64
}
@@ -42,10 +47,11 @@ func NewCookieGenerator() (*CookieGenerator, error) {
}
// NewToken generates a new Cookie for a given source address
func (g *CookieGenerator) NewToken(raddr net.Addr) ([]byte, error) {
func (g *CookieGenerator) NewToken(raddr net.Addr, origConnID protocol.ConnectionID) ([]byte, error) {
data, err := asn1.Marshal(token{
Data: encodeRemoteAddr(raddr),
Timestamp: time.Now().Unix(),
RemoteAddr: encodeRemoteAddr(raddr),
OriginalDestConnectionID: origConnID,
Timestamp: time.Now().Unix(),
})
if err != nil {
return nil, err
@@ -72,10 +78,14 @@ func (g *CookieGenerator) DecodeToken(encrypted []byte) (*Cookie, error) {
if len(rest) != 0 {
return nil, fmt.Errorf("rest when unpacking token: %d", len(rest))
}
return &Cookie{
RemoteAddr: decodeRemoteAddr(t.Data),
cookie := &Cookie{
RemoteAddr: decodeRemoteAddr(t.RemoteAddr),
SentTime: time.Unix(t.Timestamp, 0),
}, nil
}
if len(t.OriginalDestConnectionID) > 0 {
cookie.OriginalDestConnectionID = protocol.ConnectionID(t.OriginalDestConnectionID)
}
return cookie, nil
}
// encodeRemoteAddr encodes a remote address such that it can be saved in the Cookie

View File

@@ -0,0 +1,581 @@
package handshake
import (
"crypto/aes"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"sync"
"unsafe"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/marten-seemann/qtls"
)
type messageType uint8
// TLS handshake message types.
const (
typeClientHello messageType = 1
typeServerHello messageType = 2
typeNewSessionTicket messageType = 4
typeEncryptedExtensions messageType = 8
typeCertificate messageType = 11
typeCertificateRequest messageType = 13
typeCertificateVerify messageType = 15
typeFinished messageType = 20
)
func (m messageType) String() string {
switch m {
case typeClientHello:
return "ClientHello"
case typeServerHello:
return "ServerHello"
case typeNewSessionTicket:
return "NewSessionTicket"
case typeEncryptedExtensions:
return "EncryptedExtensions"
case typeCertificate:
return "Certificate"
case typeCertificateRequest:
return "CertificateRequest"
case typeCertificateVerify:
return "CertificateVerify"
case typeFinished:
return "Finished"
default:
return fmt.Sprintf("unknown message type: %d", m)
}
}
// ErrOpenerNotYetAvailable is returned when an opener is requested for an encryption level,
// but the corresponding opener has not yet been initialized
// This can happen when packets arrive out of order.
var ErrOpenerNotYetAvailable = errors.New("CryptoSetup: opener at this encryption level not yet available")
type cryptoSetup struct {
tlsConf *qtls.Config
conn *qtls.Conn
messageChan chan []byte
paramsChan <-chan []byte
handleParamsCallback func([]byte)
alertChan chan uint8
// HandleData() sends errors on the messageErrChan
messageErrChan chan error
// handshakeDone is closed as soon as the go routine running qtls.Handshake() returns
handshakeDone chan struct{}
// is closed when Close() is called
closeChan chan struct{}
clientHelloWritten bool
clientHelloWrittenChan chan struct{}
receivedWriteKey chan struct{}
receivedReadKey chan struct{}
// WriteRecord does a non-blocking send on this channel.
// This way, handleMessage can see if qtls tries to write a message.
// This is necessary:
// for servers: to see if a HelloRetryRequest should be sent in response to a ClientHello
// for clients: to see if a ServerHello is a HelloRetryRequest
writeRecord chan struct{}
logger utils.Logger
perspective protocol.Perspective
mutex sync.Mutex // protects all members below
readEncLevel protocol.EncryptionLevel
writeEncLevel protocol.EncryptionLevel
initialStream io.Writer
initialOpener Opener
initialSealer Sealer
handshakeStream io.Writer
handshakeOpener Opener
handshakeSealer Sealer
oneRTTStream io.Writer
opener Opener
sealer Sealer
}
var _ qtls.RecordLayer = &cryptoSetup{}
var _ CryptoSetup = &cryptoSetup{}
// NewCryptoSetupClient creates a new crypto setup for the client
func NewCryptoSetupClient(
initialStream io.Writer,
handshakeStream io.Writer,
oneRTTStream io.Writer,
connID protocol.ConnectionID,
remoteAddr net.Addr,
tp *TransportParameters,
handleParams func([]byte),
tlsConf *tls.Config,
logger utils.Logger,
) (CryptoSetup, <-chan struct{} /* ClientHello written */, error) {
cs, clientHelloWritten, err := newCryptoSetup(
initialStream,
handshakeStream,
oneRTTStream,
connID,
tp,
handleParams,
tlsConf,
logger,
protocol.PerspectiveClient,
)
if err != nil {
return nil, nil, err
}
cs.conn = qtls.Client(newConn(remoteAddr), cs.tlsConf)
return cs, clientHelloWritten, nil
}
// NewCryptoSetupServer creates a new crypto setup for the server
func NewCryptoSetupServer(
initialStream io.Writer,
handshakeStream io.Writer,
oneRTTStream io.Writer,
connID protocol.ConnectionID,
remoteAddr net.Addr,
tp *TransportParameters,
handleParams func([]byte),
tlsConf *tls.Config,
logger utils.Logger,
) (CryptoSetup, error) {
cs, _, err := newCryptoSetup(
initialStream,
handshakeStream,
oneRTTStream,
connID,
tp,
handleParams,
tlsConf,
logger,
protocol.PerspectiveServer,
)
if err != nil {
return nil, err
}
cs.conn = qtls.Server(newConn(remoteAddr), cs.tlsConf)
return cs, nil
}
func newCryptoSetup(
initialStream io.Writer,
handshakeStream io.Writer,
oneRTTStream io.Writer,
connID protocol.ConnectionID,
tp *TransportParameters,
handleParams func([]byte),
tlsConf *tls.Config,
logger utils.Logger,
perspective protocol.Perspective,
) (*cryptoSetup, <-chan struct{} /* ClientHello written */, error) {
initialSealer, initialOpener, err := NewInitialAEAD(connID, perspective)
if err != nil {
return nil, nil, err
}
extHandler := newExtensionHandler(tp.Marshal(), perspective)
cs := &cryptoSetup{
initialStream: initialStream,
initialSealer: initialSealer,
initialOpener: initialOpener,
handshakeStream: handshakeStream,
oneRTTStream: oneRTTStream,
readEncLevel: protocol.EncryptionInitial,
writeEncLevel: protocol.EncryptionInitial,
handleParamsCallback: handleParams,
paramsChan: extHandler.TransportParameters(),
logger: logger,
perspective: perspective,
handshakeDone: make(chan struct{}),
alertChan: make(chan uint8),
messageErrChan: make(chan error, 1),
clientHelloWrittenChan: make(chan struct{}),
messageChan: make(chan []byte, 100),
receivedReadKey: make(chan struct{}),
receivedWriteKey: make(chan struct{}),
writeRecord: make(chan struct{}),
closeChan: make(chan struct{}),
}
qtlsConf := tlsConfigToQtlsConfig(tlsConf, cs, extHandler)
cs.tlsConf = qtlsConf
return cs, cs.clientHelloWrittenChan, nil
}
func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) error {
initialSealer, initialOpener, err := NewInitialAEAD(id, h.perspective)
if err != nil {
return err
}
h.initialSealer = initialSealer
h.initialOpener = initialOpener
return nil
}
func (h *cryptoSetup) RunHandshake() error {
// Handle errors that might occur when HandleData() is called.
handshakeComplete := make(chan struct{})
handshakeErrChan := make(chan error, 1)
go func() {
defer close(h.handshakeDone)
if err := h.conn.Handshake(); err != nil {
handshakeErrChan <- err
return
}
close(handshakeComplete)
}()
select {
case <-h.closeChan:
close(h.messageChan)
// wait until the Handshake() go routine has returned
return errors.New("Handshake aborted")
case <-handshakeComplete: // return when the handshake is done
return nil
case alert := <-h.alertChan:
err := <-handshakeErrChan
return qerr.CryptoError(alert, err.Error())
case err := <-h.messageErrChan:
// If the handshake errored because of an error that occurred during HandleData(),
// that error message will be more useful than the error message generated by Handshake().
// Close the message chan that qtls is receiving messages from.
// This will make qtls.Handshake() return.
// Thereby the go routine running qtls.Handshake() will return.
close(h.messageChan)
return err
}
}
func (h *cryptoSetup) Close() error {
close(h.closeChan)
// wait until qtls.Handshake() actually returned
<-h.handshakeDone
return nil
}
// handleMessage handles a TLS handshake message.
// It is called by the crypto streams when a new message is available.
// It returns if it is done with messages on the same encryption level.
func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) bool /* stream finished */ {
msgType := messageType(data[0])
h.logger.Debugf("Received %s message (%d bytes, encryption level: %s)", msgType, len(data), encLevel)
if err := h.checkEncryptionLevel(msgType, encLevel); err != nil {
h.messageErrChan <- err
return false
}
h.messageChan <- data
switch h.perspective {
case protocol.PerspectiveClient:
return h.handleMessageForClient(msgType)
case protocol.PerspectiveServer:
return h.handleMessageForServer(msgType)
default:
panic("")
}
}
func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protocol.EncryptionLevel) error {
var expected protocol.EncryptionLevel
switch msgType {
case typeClientHello,
typeServerHello:
expected = protocol.EncryptionInitial
case typeEncryptedExtensions,
typeCertificate,
typeCertificateRequest,
typeCertificateVerify,
typeFinished:
expected = protocol.EncryptionHandshake
case typeNewSessionTicket:
expected = protocol.Encryption1RTT
default:
return fmt.Errorf("unexpected handshake message: %d", msgType)
}
if encLevel != expected {
return fmt.Errorf("expected handshake message %s to have encryption level %s, has %s", msgType, expected, encLevel)
}
return nil
}
func (h *cryptoSetup) handleMessageForServer(msgType messageType) bool {
switch msgType {
case typeClientHello:
select {
case <-h.writeRecord:
// If qtls sends a HelloRetryRequest, it will only write the record.
// If it accepts the ClientHello, it will first read the transport parameters.
h.logger.Debugf("Sending HelloRetryRequest")
return false
case data := <-h.paramsChan:
h.handleParamsCallback(data)
case <-h.handshakeDone:
return false
}
// get the handshake read key
select {
case <-h.receivedReadKey:
case <-h.handshakeDone:
return false
}
// get the handshake write key
select {
case <-h.receivedWriteKey:
case <-h.handshakeDone:
return false
}
// get the 1-RTT write key
select {
case <-h.receivedWriteKey:
case <-h.handshakeDone:
return false
}
return true
case typeCertificate, typeCertificateVerify:
// nothing to do
return false
case typeFinished:
// get the 1-RTT read key
select {
case <-h.receivedReadKey:
case <-h.handshakeDone:
return false
}
return true
default:
panic("unexpected handshake message")
}
}
func (h *cryptoSetup) handleMessageForClient(msgType messageType) bool {
switch msgType {
case typeServerHello:
// get the handshake write key
select {
case <-h.writeRecord:
// If qtls writes in response to a ServerHello, this means that this ServerHello
// is a HelloRetryRequest.
// Otherwise, we'd just wait for the Certificate message.
h.logger.Debugf("ServerHello is a HelloRetryRequest")
return false
case <-h.receivedWriteKey:
case <-h.handshakeDone:
return false
}
// get the handshake read key
select {
case <-h.receivedReadKey:
case <-h.handshakeDone:
return false
}
return true
case typeEncryptedExtensions:
select {
case data := <-h.paramsChan:
h.handleParamsCallback(data)
case <-h.handshakeDone:
return false
}
return false
case typeCertificateRequest, typeCertificate, typeCertificateVerify:
// nothing to do
return false
case typeFinished:
// get the 1-RTT read key
select {
case <-h.receivedReadKey:
case <-h.handshakeDone:
return false
}
// get the handshake write key
select {
case <-h.receivedWriteKey:
case <-h.handshakeDone:
return false
}
return true
case typeNewSessionTicket:
<-h.handshakeDone // don't process session tickets before the handshake has completed
h.conn.HandlePostHandshakeMessage()
return false
default:
panic("unexpected handshake message: ")
}
}
// ReadHandshakeMessage is called by TLS.
// It blocks until a new handshake message is available.
func (h *cryptoSetup) ReadHandshakeMessage() ([]byte, error) {
msg, ok := <-h.messageChan
if !ok {
return nil, errors.New("error while handling the handshake message")
}
return msg, nil
}
func (h *cryptoSetup) SetReadKey(suite *qtls.CipherSuite, trafficSecret []byte) {
key := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic key", suite.KeyLen())
iv := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic iv", suite.IVLen())
hpKey := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic hp", suite.KeyLen())
hpDecrypter, err := aes.NewCipher(hpKey)
if err != nil {
panic(fmt.Sprintf("error creating new AES cipher: %s", err))
}
h.mutex.Lock()
switch h.readEncLevel {
case protocol.EncryptionInitial:
h.readEncLevel = protocol.EncryptionHandshake
h.handshakeOpener = newOpener(suite.AEAD(key, iv), hpDecrypter, false)
h.logger.Debugf("Installed Handshake Read keys")
case protocol.EncryptionHandshake:
h.readEncLevel = protocol.Encryption1RTT
h.opener = newOpener(suite.AEAD(key, iv), hpDecrypter, true)
h.logger.Debugf("Installed 1-RTT Read keys")
default:
panic("unexpected read encryption level")
}
h.mutex.Unlock()
h.receivedReadKey <- struct{}{}
}
func (h *cryptoSetup) SetWriteKey(suite *qtls.CipherSuite, trafficSecret []byte) {
key := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic key", suite.KeyLen())
iv := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic iv", suite.IVLen())
hpKey := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic hp", suite.KeyLen())
hpEncrypter, err := aes.NewCipher(hpKey)
if err != nil {
panic(fmt.Sprintf("error creating new AES cipher: %s", err))
}
h.mutex.Lock()
switch h.writeEncLevel {
case protocol.EncryptionInitial:
h.writeEncLevel = protocol.EncryptionHandshake
h.handshakeSealer = newSealer(suite.AEAD(key, iv), hpEncrypter, false)
h.logger.Debugf("Installed Handshake Write keys")
case protocol.EncryptionHandshake:
h.writeEncLevel = protocol.Encryption1RTT
h.sealer = newSealer(suite.AEAD(key, iv), hpEncrypter, true)
h.logger.Debugf("Installed 1-RTT Write keys")
default:
panic("unexpected write encryption level")
}
h.mutex.Unlock()
h.receivedWriteKey <- struct{}{}
}
// WriteRecord is called when TLS writes data
func (h *cryptoSetup) WriteRecord(p []byte) (int, error) {
defer func() {
select {
case h.writeRecord <- struct{}{}:
default:
}
}()
h.mutex.Lock()
defer h.mutex.Unlock()
switch h.writeEncLevel {
case protocol.EncryptionInitial:
// assume that the first WriteRecord call contains the ClientHello
n, err := h.initialStream.Write(p)
if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient {
h.clientHelloWritten = true
close(h.clientHelloWrittenChan)
}
return n, err
case protocol.EncryptionHandshake:
return h.handshakeStream.Write(p)
case protocol.Encryption1RTT:
return h.oneRTTStream.Write(p)
default:
panic(fmt.Sprintf("unexpected write encryption level: %s", h.writeEncLevel))
}
}
func (h *cryptoSetup) SendAlert(alert uint8) {
h.alertChan <- alert
}
func (h *cryptoSetup) GetSealer() (protocol.EncryptionLevel, Sealer) {
h.mutex.Lock()
defer h.mutex.Unlock()
if h.sealer != nil {
return protocol.Encryption1RTT, h.sealer
}
if h.handshakeSealer != nil {
return protocol.EncryptionHandshake, h.handshakeSealer
}
return protocol.EncryptionInitial, h.initialSealer
}
func (h *cryptoSetup) GetSealerWithEncryptionLevel(level protocol.EncryptionLevel) (Sealer, error) {
errNoSealer := fmt.Errorf("CryptoSetup: no sealer with encryption level %s", level.String())
h.mutex.Lock()
defer h.mutex.Unlock()
switch level {
case protocol.EncryptionInitial:
return h.initialSealer, nil
case protocol.EncryptionHandshake:
if h.handshakeSealer == nil {
return nil, errNoSealer
}
return h.handshakeSealer, nil
case protocol.Encryption1RTT:
if h.sealer == nil {
return nil, errNoSealer
}
return h.sealer, nil
default:
return nil, errNoSealer
}
}
func (h *cryptoSetup) GetOpener(level protocol.EncryptionLevel) (Opener, error) {
h.mutex.Lock()
defer h.mutex.Unlock()
switch level {
case protocol.EncryptionInitial:
return h.initialOpener, nil
case protocol.EncryptionHandshake:
if h.handshakeOpener == nil {
return nil, ErrOpenerNotYetAvailable
}
return h.handshakeOpener, nil
case protocol.Encryption1RTT:
if h.opener == nil {
return nil, ErrOpenerNotYetAvailable
}
return h.opener, nil
default:
return nil, fmt.Errorf("CryptoSetup: no opener with encryption level %s", level)
}
}
func (h *cryptoSetup) ConnectionState() tls.ConnectionState {
cs := h.conn.ConnectionState()
// h.conn is a qtls.Conn, which returns a qtls.ConnectionState.
// qtls.ConnectionState is identical to the tls.ConnectionState.
// It contains an unexported field which is used ExportKeyingMaterial().
// The only way to return a tls.ConnectionState is to use unsafe.
// In unsafe.go we check that the two objects are actually identical.
return *(*tls.ConnectionState)(unsafe.Pointer(&cs))
}

View File

@@ -1,542 +0,0 @@
package handshake
import (
"bytes"
"crypto/rand"
"crypto/tls"
"encoding/binary"
"errors"
"fmt"
"io"
"sync"
"time"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
type cryptoSetupClient struct {
mutex sync.RWMutex
hostname string
connID protocol.ConnectionID
version protocol.VersionNumber
initialVersion protocol.VersionNumber
negotiatedVersions []protocol.VersionNumber
cryptoStream io.ReadWriter
serverConfig *serverConfigClient
stk []byte
sno []byte
nonc []byte
proof []byte
chloForSignature []byte
lastSentCHLO []byte
certManager crypto.CertManager
divNonceChan chan struct{}
diversificationNonce []byte
clientHelloCounter int
serverVerified bool // has the certificate chain and the proof already been verified
keyDerivation QuicCryptoKeyDerivationFunction
receivedSecurePacket bool
nullAEAD crypto.AEAD
secureAEAD crypto.AEAD
forwardSecureAEAD crypto.AEAD
paramsChan chan<- TransportParameters
handshakeEvent chan<- struct{}
params *TransportParameters
logger utils.Logger
}
var _ CryptoSetup = &cryptoSetupClient{}
var (
errNoObitForClientNonce = errors.New("CryptoSetup BUG: No OBIT for client nonce available")
errClientNonceAlreadyExists = errors.New("CryptoSetup BUG: A client nonce was already generated")
errConflictingDiversificationNonces = errors.New("Received two different diversification nonces")
)
// NewCryptoSetupClient creates a new CryptoSetup instance for a client
func NewCryptoSetupClient(
cryptoStream io.ReadWriter,
connID protocol.ConnectionID,
version protocol.VersionNumber,
tlsConf *tls.Config,
params *TransportParameters,
paramsChan chan<- TransportParameters,
handshakeEvent chan<- struct{},
initialVersion protocol.VersionNumber,
negotiatedVersions []protocol.VersionNumber,
logger utils.Logger,
) (CryptoSetup, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, version)
if err != nil {
return nil, err
}
divNonceChan := make(chan struct{})
cs := &cryptoSetupClient{
cryptoStream: cryptoStream,
hostname: tlsConf.ServerName,
connID: connID,
version: version,
certManager: crypto.NewCertManager(tlsConf),
params: params,
keyDerivation: crypto.DeriveQuicCryptoAESKeys,
nullAEAD: nullAEAD,
paramsChan: paramsChan,
handshakeEvent: handshakeEvent,
initialVersion: initialVersion,
// The server might have sent greased versions in the Version Negotiation packet.
// We need strip those from the list, since they won't be included in the handshake tag.
negotiatedVersions: protocol.StripGreasedVersions(negotiatedVersions),
divNonceChan: divNonceChan,
logger: logger,
}
return cs, nil
}
func (h *cryptoSetupClient) HandleCryptoStream() error {
messageChan := make(chan HandshakeMessage)
errorChan := make(chan error, 1)
go func() {
for {
message, err := ParseHandshakeMessage(h.cryptoStream)
if err != nil {
errorChan <- qerr.Error(qerr.HandshakeFailed, err.Error())
return
}
messageChan <- message
}
}()
for {
if err := h.maybeUpgradeCrypto(); err != nil {
return err
}
h.mutex.RLock()
sendCHLO := h.secureAEAD == nil
h.mutex.RUnlock()
if sendCHLO {
if err := h.sendCHLO(); err != nil {
return err
}
}
var message HandshakeMessage
select {
case <-h.divNonceChan:
// there's no message to process, but we should try upgrading the crypto again
continue
case message = <-messageChan:
case err := <-errorChan:
return err
}
h.logger.Debugf("Got %s", message)
switch message.Tag {
case TagREJ:
if err := h.handleREJMessage(message.Data); err != nil {
return err
}
case TagSHLO:
params, err := h.handleSHLOMessage(message.Data)
if err != nil {
return err
}
// blocks until the session has received the parameters
h.paramsChan <- *params
h.handshakeEvent <- struct{}{}
close(h.handshakeEvent)
default:
return qerr.InvalidCryptoMessageType
}
}
}
func (h *cryptoSetupClient) handleREJMessage(cryptoData map[Tag][]byte) error {
var err error
if stk, ok := cryptoData[TagSTK]; ok {
h.stk = stk
}
if sno, ok := cryptoData[TagSNO]; ok {
h.sno = sno
}
// TODO: what happens if the server sends a different server config in two packets?
if scfg, ok := cryptoData[TagSCFG]; ok {
h.serverConfig, err = parseServerConfig(scfg)
if err != nil {
return err
}
if h.serverConfig.IsExpired() {
return qerr.CryptoServerConfigExpired
}
// now that we have a server config, we can use its OBIT value to generate a client nonce
if len(h.nonc) == 0 {
err = h.generateClientNonce()
if err != nil {
return err
}
}
}
if proof, ok := cryptoData[TagPROF]; ok {
h.proof = proof
h.chloForSignature = h.lastSentCHLO
}
if crt, ok := cryptoData[TagCERT]; ok {
err := h.certManager.SetData(crt)
if err != nil {
return qerr.Error(qerr.InvalidCryptoMessageParameter, "Certificate data invalid")
}
err = h.certManager.Verify(h.hostname)
if err != nil {
h.logger.Infof("Certificate validation failed: %s", err.Error())
return qerr.ProofInvalid
}
}
if h.serverConfig != nil && len(h.proof) != 0 && h.certManager.GetLeafCert() != nil {
validProof := h.certManager.VerifyServerProof(h.proof, h.chloForSignature, h.serverConfig.Get())
if !validProof {
h.logger.Infof("Server proof verification failed")
return qerr.ProofInvalid
}
h.serverVerified = true
}
return nil
}
func (h *cryptoSetupClient) handleSHLOMessage(cryptoData map[Tag][]byte) (*TransportParameters, error) {
h.mutex.Lock()
defer h.mutex.Unlock()
if !h.receivedSecurePacket {
return nil, qerr.Error(qerr.CryptoEncryptionLevelIncorrect, "unencrypted SHLO message")
}
if sno, ok := cryptoData[TagSNO]; ok {
h.sno = sno
}
serverPubs, ok := cryptoData[TagPUBS]
if !ok {
return nil, qerr.Error(qerr.CryptoMessageParameterNotFound, "PUBS")
}
verTag, ok := cryptoData[TagVER]
if !ok {
return nil, qerr.Error(qerr.InvalidCryptoMessageParameter, "server hello missing version list")
}
if !h.validateVersionList(verTag) {
return nil, qerr.Error(qerr.VersionNegotiationMismatch, "Downgrade attack detected")
}
nonce := append(h.nonc, h.sno...)
ephermalSharedSecret, err := h.serverConfig.kex.CalculateSharedKey(serverPubs)
if err != nil {
return nil, err
}
leafCert := h.certManager.GetLeafCert()
h.forwardSecureAEAD, err = h.keyDerivation(
true,
ephermalSharedSecret,
nonce,
h.connID,
h.lastSentCHLO,
h.serverConfig.Get(),
leafCert,
nil,
protocol.PerspectiveClient,
)
if err != nil {
return nil, err
}
h.logger.Debugf("Creating AEAD for forward-secure encryption. Stopping to accept all lower encryption levels.")
params, err := readHelloMap(cryptoData)
if err != nil {
return nil, qerr.InvalidCryptoMessageParameter
}
return params, nil
}
func (h *cryptoSetupClient) validateVersionList(verTags []byte) bool {
numNegotiatedVersions := len(h.negotiatedVersions)
if numNegotiatedVersions == 0 {
return true
}
if len(verTags)%4 != 0 || len(verTags)/4 != numNegotiatedVersions {
return false
}
b := bytes.NewReader(verTags)
for i := 0; i < numNegotiatedVersions; i++ {
v, err := utils.BigEndian.ReadUint32(b)
if err != nil { // should never occur, since the length was already checked
return false
}
if protocol.VersionNumber(v) != h.negotiatedVersions[i] {
return false
}
}
return true
}
func (h *cryptoSetupClient) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.forwardSecureAEAD != nil {
data, err := h.forwardSecureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
return data, protocol.EncryptionForwardSecure, nil
}
return nil, protocol.EncryptionUnspecified, err
}
if h.secureAEAD != nil {
data, err := h.secureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
h.logger.Debugf("Received first secure packet. Stopping to accept unencrypted packets.")
h.receivedSecurePacket = true
return data, protocol.EncryptionSecure, nil
}
if h.receivedSecurePacket {
return nil, protocol.EncryptionUnspecified, err
}
}
res, err := h.nullAEAD.Open(dst, src, packetNumber, associatedData)
if err != nil {
return nil, protocol.EncryptionUnspecified, err
}
return res, protocol.EncryptionUnencrypted, nil
}
func (h *cryptoSetupClient) GetSealer() (protocol.EncryptionLevel, Sealer) {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.forwardSecureAEAD != nil {
return protocol.EncryptionForwardSecure, h.forwardSecureAEAD
} else if h.secureAEAD != nil {
return protocol.EncryptionSecure, h.secureAEAD
} else {
return protocol.EncryptionUnencrypted, h.nullAEAD
}
}
func (h *cryptoSetupClient) GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer) {
return protocol.EncryptionUnencrypted, h.nullAEAD
}
func (h *cryptoSetupClient) GetSealerWithEncryptionLevel(encLevel protocol.EncryptionLevel) (Sealer, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
switch encLevel {
case protocol.EncryptionUnencrypted:
return h.nullAEAD, nil
case protocol.EncryptionSecure:
if h.secureAEAD == nil {
return nil, errors.New("CryptoSetupClient: no secureAEAD")
}
return h.secureAEAD, nil
case protocol.EncryptionForwardSecure:
if h.forwardSecureAEAD == nil {
return nil, errors.New("CryptoSetupClient: no forwardSecureAEAD")
}
return h.forwardSecureAEAD, nil
}
return nil, errors.New("CryptoSetupClient: no encryption level specified")
}
func (h *cryptoSetupClient) ConnectionState() ConnectionState {
h.mutex.Lock()
defer h.mutex.Unlock()
return ConnectionState{
HandshakeComplete: h.forwardSecureAEAD != nil,
PeerCertificates: h.certManager.GetChain(),
}
}
func (h *cryptoSetupClient) SetDiversificationNonce(divNonce []byte) error {
h.mutex.Lock()
if len(h.diversificationNonce) > 0 {
defer h.mutex.Unlock()
if !bytes.Equal(h.diversificationNonce, divNonce) {
return errConflictingDiversificationNonces
}
return nil
}
h.diversificationNonce = divNonce
h.mutex.Unlock()
h.divNonceChan <- struct{}{}
return nil
}
func (h *cryptoSetupClient) sendCHLO() error {
h.clientHelloCounter++
if h.clientHelloCounter > protocol.MaxClientHellos {
return qerr.Error(qerr.CryptoTooManyRejects, fmt.Sprintf("More than %d rejects", protocol.MaxClientHellos))
}
b := &bytes.Buffer{}
tags, err := h.getTags()
if err != nil {
return err
}
h.addPadding(tags)
message := HandshakeMessage{
Tag: TagCHLO,
Data: tags,
}
h.logger.Debugf("Sending %s", message)
message.Write(b)
_, err = h.cryptoStream.Write(b.Bytes())
if err != nil {
return err
}
h.lastSentCHLO = b.Bytes()
return nil
}
func (h *cryptoSetupClient) getTags() (map[Tag][]byte, error) {
tags := h.params.getHelloMap()
tags[TagSNI] = []byte(h.hostname)
tags[TagPDMD] = []byte("X509")
ccs := h.certManager.GetCommonCertificateHashes()
if len(ccs) > 0 {
tags[TagCCS] = ccs
}
versionTag := make([]byte, 4)
binary.BigEndian.PutUint32(versionTag, uint32(h.initialVersion))
tags[TagVER] = versionTag
if len(h.stk) > 0 {
tags[TagSTK] = h.stk
}
if len(h.sno) > 0 {
tags[TagSNO] = h.sno
}
if h.serverConfig != nil {
tags[TagSCID] = h.serverConfig.ID
leafCert := h.certManager.GetLeafCert()
if leafCert != nil {
certHash, _ := h.certManager.GetLeafCertHash()
xlct := make([]byte, 8)
binary.LittleEndian.PutUint64(xlct, certHash)
tags[TagNONC] = h.nonc
tags[TagXLCT] = xlct
tags[TagKEXS] = []byte("C255")
tags[TagAEAD] = []byte("AESG")
tags[TagPUBS] = h.serverConfig.kex.PublicKey() // TODO: check if 3 bytes need to be prepended
}
}
return tags, nil
}
// add a TagPAD to a tagMap, such that the total size will be bigger than the ClientHelloMinimumSize
func (h *cryptoSetupClient) addPadding(tags map[Tag][]byte) {
var size int
for _, tag := range tags {
size += 8 + len(tag) // 4 bytes for the tag + 4 bytes for the offset + the length of the data
}
paddingSize := protocol.MinClientHelloSize - size
if paddingSize > 0 {
tags[TagPAD] = bytes.Repeat([]byte{0}, paddingSize)
}
}
func (h *cryptoSetupClient) maybeUpgradeCrypto() error {
if !h.serverVerified {
return nil
}
h.mutex.Lock()
defer h.mutex.Unlock()
leafCert := h.certManager.GetLeafCert()
if h.secureAEAD == nil && (h.serverConfig != nil && len(h.serverConfig.sharedSecret) > 0 && len(h.nonc) > 0 && len(leafCert) > 0 && len(h.diversificationNonce) > 0 && len(h.lastSentCHLO) > 0) {
var err error
var nonce []byte
if h.sno == nil {
nonce = h.nonc
} else {
nonce = append(h.nonc, h.sno...)
}
h.secureAEAD, err = h.keyDerivation(
false,
h.serverConfig.sharedSecret,
nonce,
h.connID,
h.lastSentCHLO,
h.serverConfig.Get(),
leafCert,
h.diversificationNonce,
protocol.PerspectiveClient,
)
if err != nil {
return err
}
h.logger.Debugf("Creating AEAD for secure encryption.")
h.handshakeEvent <- struct{}{}
}
return nil
}
func (h *cryptoSetupClient) generateClientNonce() error {
if len(h.nonc) > 0 {
return errClientNonceAlreadyExists
}
nonc := make([]byte, 32)
binary.BigEndian.PutUint32(nonc, uint32(time.Now().Unix()))
if len(h.serverConfig.obit) != 8 {
return errNoObitForClientNonce
}
copy(nonc[4:12], h.serverConfig.obit)
_, err := rand.Read(nonc[12:])
if err != nil {
return err
}
h.nonc = nonc
return nil
}

View File

@@ -1,467 +0,0 @@
package handshake
import (
"bytes"
"crypto/rand"
"encoding/binary"
"errors"
"io"
"net"
"sync"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
// QuicCryptoKeyDerivationFunction is used for key derivation
type QuicCryptoKeyDerivationFunction func(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (crypto.AEAD, error)
// KeyExchangeFunction is used to make a new KEX
type KeyExchangeFunction func() (crypto.KeyExchange, error)
// The CryptoSetupServer handles all things crypto for the Session
type cryptoSetupServer struct {
mutex sync.RWMutex
connID protocol.ConnectionID
remoteAddr net.Addr
scfg *ServerConfig
diversificationNonce []byte
version protocol.VersionNumber
supportedVersions []protocol.VersionNumber
acceptSTKCallback func(net.Addr, *Cookie) bool
nullAEAD crypto.AEAD
secureAEAD crypto.AEAD
forwardSecureAEAD crypto.AEAD
receivedForwardSecurePacket bool
receivedSecurePacket bool
sentSHLO chan struct{} // this channel is closed as soon as the SHLO has been written
receivedParams bool
paramsChan chan<- TransportParameters
handshakeEvent chan<- struct{}
keyDerivation QuicCryptoKeyDerivationFunction
keyExchange KeyExchangeFunction
cryptoStream io.ReadWriter
params *TransportParameters
sni string // need to fill out the ConnectionState
logger utils.Logger
}
var _ CryptoSetup = &cryptoSetupServer{}
// ErrNSTPExperiment is returned when the client sends the NSTP tag in the CHLO.
// This is an experiment implemented by Chrome in QUIC 38, which we don't support at this point.
var ErrNSTPExperiment = qerr.Error(qerr.InvalidCryptoMessageParameter, "NSTP experiment. Unsupported")
// NewCryptoSetup creates a new CryptoSetup instance for a server
func NewCryptoSetup(
cryptoStream io.ReadWriter,
connID protocol.ConnectionID,
remoteAddr net.Addr,
version protocol.VersionNumber,
divNonce []byte,
scfg *ServerConfig,
params *TransportParameters,
supportedVersions []protocol.VersionNumber,
acceptSTK func(net.Addr, *Cookie) bool,
paramsChan chan<- TransportParameters,
handshakeEvent chan<- struct{},
logger utils.Logger,
) (CryptoSetup, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, connID, version)
if err != nil {
return nil, err
}
return &cryptoSetupServer{
cryptoStream: cryptoStream,
connID: connID,
remoteAddr: remoteAddr,
version: version,
supportedVersions: supportedVersions,
diversificationNonce: divNonce,
scfg: scfg,
keyDerivation: crypto.DeriveQuicCryptoAESKeys,
keyExchange: getEphermalKEX,
nullAEAD: nullAEAD,
params: params,
acceptSTKCallback: acceptSTK,
sentSHLO: make(chan struct{}),
paramsChan: paramsChan,
handshakeEvent: handshakeEvent,
logger: logger,
}, nil
}
// HandleCryptoStream reads and writes messages on the crypto stream
func (h *cryptoSetupServer) HandleCryptoStream() error {
for {
var chloData bytes.Buffer
message, err := ParseHandshakeMessage(io.TeeReader(h.cryptoStream, &chloData))
if err != nil {
return qerr.HandshakeFailed
}
if message.Tag != TagCHLO {
return qerr.InvalidCryptoMessageType
}
h.logger.Debugf("Got %s", message)
done, err := h.handleMessage(chloData.Bytes(), message.Data)
if err != nil {
return err
}
if done {
return nil
}
}
}
func (h *cryptoSetupServer) handleMessage(chloData []byte, cryptoData map[Tag][]byte) (bool, error) {
if _, isNSTPExperiment := cryptoData[TagNSTP]; isNSTPExperiment {
return false, ErrNSTPExperiment
}
sniSlice, ok := cryptoData[TagSNI]
if !ok {
return false, qerr.Error(qerr.CryptoMessageParameterNotFound, "SNI required")
}
sni := string(sniSlice)
if sni == "" {
return false, qerr.Error(qerr.CryptoMessageParameterNotFound, "SNI required")
}
h.sni = sni
// prevent version downgrade attacks
// see https://groups.google.com/a/chromium.org/forum/#!topic/proto-quic/N-de9j63tCk for a discussion and examples
verSlice, ok := cryptoData[TagVER]
if !ok {
return false, qerr.Error(qerr.InvalidCryptoMessageParameter, "client hello missing version tag")
}
if len(verSlice) != 4 {
return false, qerr.Error(qerr.InvalidCryptoMessageParameter, "incorrect version tag")
}
ver := protocol.VersionNumber(binary.BigEndian.Uint32(verSlice))
// If the client's preferred version is not the version we are currently speaking, then the client went through a version negotiation. In this case, we need to make sure that we actually do not support this version and that it wasn't a downgrade attack.
if ver != h.version && protocol.IsSupportedVersion(h.supportedVersions, ver) {
return false, qerr.Error(qerr.VersionNegotiationMismatch, "Downgrade attack detected")
}
var reply []byte
var err error
certUncompressed, err := h.scfg.certChain.GetLeafCert(sni)
if err != nil {
return false, err
}
params, err := readHelloMap(cryptoData)
if err != nil {
return false, err
}
// blocks until the session has received the parameters
if !h.receivedParams {
h.receivedParams = true
h.paramsChan <- *params
}
if !h.isInchoateCHLO(cryptoData, certUncompressed) {
// We have a CHLO with a proper server config ID, do a 0-RTT handshake
reply, err = h.handleCHLO(sni, chloData, cryptoData)
if err != nil {
return false, err
}
if _, err := h.cryptoStream.Write(reply); err != nil {
return false, err
}
h.handshakeEvent <- struct{}{}
close(h.sentSHLO)
return true, nil
}
// We have an inchoate or non-matching CHLO, we now send a rejection
reply, err = h.handleInchoateCHLO(sni, chloData, cryptoData)
if err != nil {
return false, err
}
_, err = h.cryptoStream.Write(reply)
return false, err
}
// Open a message
func (h *cryptoSetupServer) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.forwardSecureAEAD != nil {
res, err := h.forwardSecureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
if !h.receivedForwardSecurePacket { // this is the first forward secure packet we receive from the client
h.logger.Debugf("Received first forward-secure packet. Stopping to accept all lower encryption levels.")
h.receivedForwardSecurePacket = true
// wait for the send on the handshakeEvent chan
<-h.sentSHLO
close(h.handshakeEvent)
}
return res, protocol.EncryptionForwardSecure, nil
}
if h.receivedForwardSecurePacket {
return nil, protocol.EncryptionUnspecified, err
}
}
if h.secureAEAD != nil {
res, err := h.secureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
h.logger.Debugf("Received first secure packet. Stopping to accept unencrypted packets.")
h.receivedSecurePacket = true
return res, protocol.EncryptionSecure, nil
}
if h.receivedSecurePacket {
return nil, protocol.EncryptionUnspecified, err
}
}
res, err := h.nullAEAD.Open(dst, src, packetNumber, associatedData)
if err != nil {
return res, protocol.EncryptionUnspecified, err
}
return res, protocol.EncryptionUnencrypted, err
}
func (h *cryptoSetupServer) GetSealer() (protocol.EncryptionLevel, Sealer) {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.forwardSecureAEAD != nil {
return protocol.EncryptionForwardSecure, h.forwardSecureAEAD
}
return protocol.EncryptionUnencrypted, h.nullAEAD
}
func (h *cryptoSetupServer) GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer) {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.secureAEAD != nil {
return protocol.EncryptionSecure, h.secureAEAD
}
return protocol.EncryptionUnencrypted, h.nullAEAD
}
func (h *cryptoSetupServer) GetSealerWithEncryptionLevel(encLevel protocol.EncryptionLevel) (Sealer, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
switch encLevel {
case protocol.EncryptionUnencrypted:
return h.nullAEAD, nil
case protocol.EncryptionSecure:
if h.secureAEAD == nil {
return nil, errors.New("CryptoSetupServer: no secureAEAD")
}
return h.secureAEAD, nil
case protocol.EncryptionForwardSecure:
if h.forwardSecureAEAD == nil {
return nil, errors.New("CryptoSetupServer: no forwardSecureAEAD")
}
return h.forwardSecureAEAD, nil
}
return nil, errors.New("CryptoSetupServer: no encryption level specified")
}
func (h *cryptoSetupServer) isInchoateCHLO(cryptoData map[Tag][]byte, cert []byte) bool {
if _, ok := cryptoData[TagPUBS]; !ok {
return true
}
scid, ok := cryptoData[TagSCID]
if !ok || !bytes.Equal(h.scfg.ID, scid) {
return true
}
xlctTag, ok := cryptoData[TagXLCT]
if !ok || len(xlctTag) != 8 {
return true
}
xlct := binary.LittleEndian.Uint64(xlctTag)
if crypto.HashCert(cert) != xlct {
return true
}
return !h.acceptSTK(cryptoData[TagSTK])
}
func (h *cryptoSetupServer) acceptSTK(token []byte) bool {
stk, err := h.scfg.cookieGenerator.DecodeToken(token)
if err != nil {
h.logger.Debugf("STK invalid: %s", err.Error())
return false
}
return h.acceptSTKCallback(h.remoteAddr, stk)
}
func (h *cryptoSetupServer) handleInchoateCHLO(sni string, chlo []byte, cryptoData map[Tag][]byte) ([]byte, error) {
token, err := h.scfg.cookieGenerator.NewToken(h.remoteAddr)
if err != nil {
return nil, err
}
replyMap := map[Tag][]byte{
TagSCFG: h.scfg.Get(),
TagSTK: token,
TagSVID: []byte("quic-go"),
}
if h.acceptSTK(cryptoData[TagSTK]) {
proof, err := h.scfg.Sign(sni, chlo)
if err != nil {
return nil, err
}
commonSetHashes := cryptoData[TagCCS]
cachedCertsHashes := cryptoData[TagCCRT]
certCompressed, err := h.scfg.GetCertsCompressed(sni, commonSetHashes, cachedCertsHashes)
if err != nil {
return nil, err
}
// Token was valid, send more details
replyMap[TagPROF] = proof
replyMap[TagCERT] = certCompressed
}
message := HandshakeMessage{
Tag: TagREJ,
Data: replyMap,
}
var serverReply bytes.Buffer
message.Write(&serverReply)
h.logger.Debugf("Sending %s", message)
return serverReply.Bytes(), nil
}
func (h *cryptoSetupServer) handleCHLO(sni string, data []byte, cryptoData map[Tag][]byte) ([]byte, error) {
// We have a CHLO matching our server config, we can continue with the 0-RTT handshake
sharedSecret, err := h.scfg.kex.CalculateSharedKey(cryptoData[TagPUBS])
if err != nil {
return nil, err
}
h.mutex.Lock()
defer h.mutex.Unlock()
certUncompressed, err := h.scfg.certChain.GetLeafCert(sni)
if err != nil {
return nil, err
}
serverNonce := make([]byte, 32)
if _, err = rand.Read(serverNonce); err != nil {
return nil, err
}
clientNonce := cryptoData[TagNONC]
err = h.validateClientNonce(clientNonce)
if err != nil {
return nil, err
}
aead := cryptoData[TagAEAD]
if !bytes.Equal(aead, []byte("AESG")) {
return nil, qerr.Error(qerr.CryptoNoSupport, "Unsupported AEAD or KEXS")
}
kexs := cryptoData[TagKEXS]
if !bytes.Equal(kexs, []byte("C255")) {
return nil, qerr.Error(qerr.CryptoNoSupport, "Unsupported AEAD or KEXS")
}
h.secureAEAD, err = h.keyDerivation(
false,
sharedSecret,
clientNonce,
h.connID,
data,
h.scfg.Get(),
certUncompressed,
h.diversificationNonce,
protocol.PerspectiveServer,
)
if err != nil {
return nil, err
}
h.logger.Debugf("Creating AEAD for secure encryption.")
h.handshakeEvent <- struct{}{}
// Generate a new curve instance to derive the forward secure key
var fsNonce bytes.Buffer
fsNonce.Write(clientNonce)
fsNonce.Write(serverNonce)
ephermalKex, err := h.keyExchange()
if err != nil {
return nil, err
}
ephermalSharedSecret, err := ephermalKex.CalculateSharedKey(cryptoData[TagPUBS])
if err != nil {
return nil, err
}
h.forwardSecureAEAD, err = h.keyDerivation(
true,
ephermalSharedSecret,
fsNonce.Bytes(),
h.connID,
data,
h.scfg.Get(),
certUncompressed,
nil,
protocol.PerspectiveServer,
)
if err != nil {
return nil, err
}
h.logger.Debugf("Creating AEAD for forward-secure encryption.")
replyMap := h.params.getHelloMap()
// add crypto parameters
verTag := &bytes.Buffer{}
for _, v := range h.supportedVersions {
utils.BigEndian.WriteUint32(verTag, uint32(v))
}
replyMap[TagPUBS] = ephermalKex.PublicKey()
replyMap[TagSNO] = serverNonce
replyMap[TagVER] = verTag.Bytes()
// note that the SHLO *has* to fit into one packet
message := HandshakeMessage{
Tag: TagSHLO,
Data: replyMap,
}
var reply bytes.Buffer
message.Write(&reply)
h.logger.Debugf("Sending %s", message)
return reply.Bytes(), nil
}
func (h *cryptoSetupServer) ConnectionState() ConnectionState {
h.mutex.Lock()
defer h.mutex.Unlock()
return ConnectionState{
ServerName: h.sni,
HandshakeComplete: h.receivedForwardSecurePacket,
}
}
func (h *cryptoSetupServer) validateClientNonce(nonce []byte) error {
if len(nonce) != 32 {
return qerr.Error(qerr.InvalidCryptoMessageParameter, "invalid client nonce length")
}
if !bytes.Equal(nonce[4:12], h.scfg.obit) {
return qerr.Error(qerr.InvalidCryptoMessageParameter, "OBIT not matching")
}
return nil
}

View File

@@ -1,163 +0,0 @@
package handshake
import (
"errors"
"fmt"
"io"
"sync"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
// KeyDerivationFunction is used for key derivation
type KeyDerivationFunction func(crypto.TLSExporter, protocol.Perspective) (crypto.AEAD, error)
type cryptoSetupTLS struct {
mutex sync.RWMutex
perspective protocol.Perspective
keyDerivation KeyDerivationFunction
nullAEAD crypto.AEAD
aead crypto.AEAD
tls mintTLS
conn *cryptoStreamConn
handshakeEvent chan<- struct{}
}
var _ CryptoSetupTLS = &cryptoSetupTLS{}
// NewCryptoSetupTLSServer creates a new TLS CryptoSetup instance for a server
func NewCryptoSetupTLSServer(
cryptoStream io.ReadWriter,
connID protocol.ConnectionID,
config *mint.Config,
handshakeEvent chan<- struct{},
version protocol.VersionNumber,
) (CryptoSetupTLS, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, connID, version)
if err != nil {
return nil, err
}
conn := newCryptoStreamConn(cryptoStream)
tls := mint.Server(conn, config)
return &cryptoSetupTLS{
tls: tls,
conn: conn,
nullAEAD: nullAEAD,
perspective: protocol.PerspectiveServer,
keyDerivation: crypto.DeriveAESKeys,
handshakeEvent: handshakeEvent,
}, nil
}
// NewCryptoSetupTLSClient creates a new TLS CryptoSetup instance for a client
func NewCryptoSetupTLSClient(
cryptoStream io.ReadWriter,
connID protocol.ConnectionID,
config *mint.Config,
handshakeEvent chan<- struct{},
version protocol.VersionNumber,
) (CryptoSetupTLS, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, version)
if err != nil {
return nil, err
}
conn := newCryptoStreamConn(cryptoStream)
tls := mint.Client(conn, config)
return &cryptoSetupTLS{
tls: tls,
conn: conn,
perspective: protocol.PerspectiveClient,
nullAEAD: nullAEAD,
keyDerivation: crypto.DeriveAESKeys,
handshakeEvent: handshakeEvent,
}, nil
}
func (h *cryptoSetupTLS) HandleCryptoStream() error {
for {
if alert := h.tls.Handshake(); alert != mint.AlertNoAlert {
return fmt.Errorf("TLS handshake error: %s (Alert %d)", alert.String(), alert)
}
state := h.tls.ConnectionState().HandshakeState
if err := h.conn.Flush(); err != nil {
return err
}
if state == mint.StateClientConnected || state == mint.StateServerConnected {
break
}
}
aead, err := h.keyDerivation(h.tls, h.perspective)
if err != nil {
return err
}
h.mutex.Lock()
h.aead = aead
h.mutex.Unlock()
h.handshakeEvent <- struct{}{}
close(h.handshakeEvent)
return nil
}
func (h *cryptoSetupTLS) OpenHandshake(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
return h.nullAEAD.Open(dst, src, packetNumber, associatedData)
}
func (h *cryptoSetupTLS) Open1RTT(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.aead == nil {
return nil, errors.New("no 1-RTT sealer")
}
return h.aead.Open(dst, src, packetNumber, associatedData)
}
func (h *cryptoSetupTLS) GetSealer() (protocol.EncryptionLevel, Sealer) {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.aead != nil {
return protocol.EncryptionForwardSecure, h.aead
}
return protocol.EncryptionUnencrypted, h.nullAEAD
}
func (h *cryptoSetupTLS) GetSealerWithEncryptionLevel(encLevel protocol.EncryptionLevel) (Sealer, error) {
errNoSealer := fmt.Errorf("CryptoSetup: no sealer with encryption level %s", encLevel.String())
h.mutex.RLock()
defer h.mutex.RUnlock()
switch encLevel {
case protocol.EncryptionUnencrypted:
return h.nullAEAD, nil
case protocol.EncryptionForwardSecure:
if h.aead == nil {
return nil, errNoSealer
}
return h.aead, nil
default:
return nil, errNoSealer
}
}
func (h *cryptoSetupTLS) GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer) {
return protocol.EncryptionUnencrypted, h.nullAEAD
}
func (h *cryptoSetupTLS) ConnectionState() ConnectionState {
h.mutex.Lock()
defer h.mutex.Unlock()
mintConnState := h.tls.ConnectionState()
return ConnectionState{
// TODO: set the ServerName, once mint exports it
HandshakeComplete: h.aead != nil,
PeerCertificates: mintConnState.PeerCertificates,
}
}

View File

@@ -1,69 +0,0 @@
package handshake
import (
"bytes"
"io"
"net"
"time"
)
type cryptoStreamConn struct {
buffer *bytes.Buffer
stream io.ReadWriter
}
var _ net.Conn = &cryptoStreamConn{}
func newCryptoStreamConn(stream io.ReadWriter) *cryptoStreamConn {
return &cryptoStreamConn{
stream: stream,
buffer: &bytes.Buffer{},
}
}
func (c *cryptoStreamConn) Read(b []byte) (int, error) {
return c.stream.Read(b)
}
func (c *cryptoStreamConn) Write(p []byte) (int, error) {
return c.buffer.Write(p)
}
func (c *cryptoStreamConn) Flush() error {
if c.buffer.Len() == 0 {
return nil
}
_, err := c.stream.Write(c.buffer.Bytes())
c.buffer.Reset()
return err
}
// Close is not implemented
func (c *cryptoStreamConn) Close() error {
return nil
}
// LocalAddr is not implemented
func (c *cryptoStreamConn) LocalAddr() net.Addr {
return nil
}
// RemoteAddr is not implemented
func (c *cryptoStreamConn) RemoteAddr() net.Addr {
return nil
}
// SetReadDeadline is not implemented
func (c *cryptoStreamConn) SetReadDeadline(time.Time) error {
return nil
}
// SetWriteDeadline is not implemented
func (c *cryptoStreamConn) SetWriteDeadline(time.Time) error {
return nil
}
// SetDeadline is not implemented
func (c *cryptoStreamConn) SetDeadline(time.Time) error {
return nil
}

View File

@@ -1,48 +0,0 @@
package handshake
import (
"sync"
"time"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
var (
kexLifetime = protocol.EphermalKeyLifetime
kexCurrent crypto.KeyExchange
kexCurrentTime time.Time
kexMutex sync.RWMutex
)
// getEphermalKEX returns the currently active KEX, which changes every protocol.EphermalKeyLifetime
// See the explanation from the QUIC crypto doc:
//
// A single connection is the usual scope for forward security, but the security
// difference between an ephemeral key used for a single connection, and one
// used for all connections for 60 seconds is negligible. Thus we can amortise
// the Diffie-Hellman key generation at the server over all the connections in a
// small time span.
func getEphermalKEX() (crypto.KeyExchange, error) {
kexMutex.RLock()
res := kexCurrent
t := kexCurrentTime
kexMutex.RUnlock()
if res != nil && time.Since(t) < kexLifetime {
return res, nil
}
kexMutex.Lock()
defer kexMutex.Unlock()
// Check if still unfulfilled
if kexCurrent == nil || time.Since(kexCurrentTime) >= kexLifetime {
kex, err := crypto.NewCurve25519KEX()
if err != nil {
return nil, err
}
kexCurrent = kex
kexCurrentTime = time.Now()
return kexCurrent, nil
}
return kexCurrent, nil
}

View File

@@ -1,137 +0,0 @@
package handshake
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"sort"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
// A HandshakeMessage is a handshake message
type HandshakeMessage struct {
Tag Tag
Data map[Tag][]byte
}
var _ fmt.Stringer = &HandshakeMessage{}
// ParseHandshakeMessage reads a crypto message
func ParseHandshakeMessage(r io.Reader) (HandshakeMessage, error) {
slice4 := make([]byte, 4)
if _, err := io.ReadFull(r, slice4); err != nil {
return HandshakeMessage{}, err
}
messageTag := Tag(binary.LittleEndian.Uint32(slice4))
if _, err := io.ReadFull(r, slice4); err != nil {
return HandshakeMessage{}, err
}
nPairs := binary.LittleEndian.Uint32(slice4)
if nPairs > protocol.CryptoMaxParams {
return HandshakeMessage{}, qerr.CryptoTooManyEntries
}
index := make([]byte, nPairs*8)
if _, err := io.ReadFull(r, index); err != nil {
return HandshakeMessage{}, err
}
resultMap := map[Tag][]byte{}
var dataStart uint32
for indexPos := 0; indexPos < int(nPairs)*8; indexPos += 8 {
tag := Tag(binary.LittleEndian.Uint32(index[indexPos : indexPos+4]))
dataEnd := binary.LittleEndian.Uint32(index[indexPos+4 : indexPos+8])
dataLen := dataEnd - dataStart
if dataLen > protocol.CryptoParameterMaxLength {
return HandshakeMessage{}, qerr.Error(qerr.CryptoInvalidValueLength, "value too long")
}
data := make([]byte, dataLen)
if _, err := io.ReadFull(r, data); err != nil {
return HandshakeMessage{}, err
}
resultMap[tag] = data
dataStart = dataEnd
}
return HandshakeMessage{
Tag: messageTag,
Data: resultMap}, nil
}
// Write writes a crypto message
func (h HandshakeMessage) Write(b *bytes.Buffer) {
data := h.Data
utils.LittleEndian.WriteUint32(b, uint32(h.Tag))
utils.LittleEndian.WriteUint16(b, uint16(len(data)))
utils.LittleEndian.WriteUint16(b, 0)
// Save current position in the buffer, so that we can update the index in-place later
indexStart := b.Len()
indexData := make([]byte, 8*len(data))
b.Write(indexData) // Will be updated later
offset := uint32(0)
for i, t := range h.getTagsSorted() {
v := data[t]
b.Write(v)
offset += uint32(len(v))
binary.LittleEndian.PutUint32(indexData[i*8:], uint32(t))
binary.LittleEndian.PutUint32(indexData[i*8+4:], offset)
}
// Now we write the index data for real
copy(b.Bytes()[indexStart:], indexData)
}
func (h *HandshakeMessage) getTagsSorted() []Tag {
tags := make([]Tag, len(h.Data))
i := 0
for t := range h.Data {
tags[i] = t
i++
}
sort.Slice(tags, func(i, j int) bool {
return tags[i] < tags[j]
})
return tags
}
func (h HandshakeMessage) String() string {
var pad string
res := tagToString(h.Tag) + ":\n"
for _, tag := range h.getTagsSorted() {
if tag == TagPAD {
pad = fmt.Sprintf("\t%s: (%d bytes)\n", tagToString(tag), len(h.Data[tag]))
} else {
res += fmt.Sprintf("\t%s: %#v\n", tagToString(tag), string(h.Data[tag]))
}
}
if len(pad) > 0 {
res += pad
}
return res
}
func tagToString(tag Tag) string {
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, uint32(tag))
for i := range b {
if b[i] == 0 {
b[i] = ' '
}
}
return string(b)
}

View File

@@ -0,0 +1,52 @@
package handshake
import (
"crypto"
"crypto/aes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/marten-seemann/qtls"
)
var quicVersion1Salt = []byte{0xef, 0x4f, 0xb0, 0xab, 0xb4, 0x74, 0x70, 0xc4, 0x1b, 0xef, 0xcf, 0x80, 0x31, 0x33, 0x4f, 0xae, 0x48, 0x5e, 0x09, 0xa0}
// NewInitialAEAD creates a new AEAD for Initial encryption / decryption.
func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective) (Sealer, Opener, error) {
clientSecret, serverSecret := computeSecrets(connID)
var mySecret, otherSecret []byte
if pers == protocol.PerspectiveClient {
mySecret = clientSecret
otherSecret = serverSecret
} else {
mySecret = serverSecret
otherSecret = clientSecret
}
myKey, myHPKey, myIV := computeInitialKeyAndIV(mySecret)
otherKey, otherHPKey, otherIV := computeInitialKeyAndIV(otherSecret)
encrypter := qtls.AEADAESGCMTLS13(myKey, myIV)
hpEncrypter, err := aes.NewCipher(myHPKey)
if err != nil {
return nil, nil, err
}
decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV)
hpDecrypter, err := aes.NewCipher(otherHPKey)
if err != nil {
return nil, nil, err
}
return newSealer(encrypter, hpEncrypter, false), newOpener(decrypter, hpDecrypter, false), nil
}
func computeSecrets(connID protocol.ConnectionID) (clientSecret, serverSecret []byte) {
initialSecret := qtls.HkdfExtract(crypto.SHA256, connID, quicVersion1Salt)
clientSecret = qtls.HkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
serverSecret = qtls.HkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size())
return
}
func computeInitialKeyAndIV(secret []byte) (key, hpKey, iv []byte) {
key = qtls.HkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
hpKey = qtls.HkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic hp", 16)
iv = qtls.HkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
return
}

View File

@@ -1,55 +1,46 @@
package handshake
import (
"crypto/tls"
"crypto/x509"
"io"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/marten-seemann/qtls"
)
// Opener opens a packet
type Opener interface {
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte)
}
// Sealer seals a packet
type Sealer interface {
Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte
EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte)
Overhead() int
}
// mintTLS combines some methods needed to interact with mint.
type mintTLS interface {
crypto.TLSExporter
Handshake() mint.Alert
// A tlsExtensionHandler sends and received the QUIC TLS extension.
type tlsExtensionHandler interface {
GetExtensions(msgType uint8) []qtls.Extension
ReceivedExtensions(msgType uint8, exts []qtls.Extension)
TransportParameters() <-chan []byte
}
// A TLSExtensionHandler sends and received the QUIC TLS extension.
// It provides the parameters sent by the peer on a channel.
type TLSExtensionHandler interface {
Send(mint.HandshakeType, *mint.ExtensionList) error
Receive(mint.HandshakeType, *mint.ExtensionList) error
GetPeerParams() <-chan TransportParameters
}
// CryptoSetup handles the handshake and protecting / unprotecting packets
type CryptoSetup interface {
RunHandshake() error
io.Closer
ChangeConnectionID(protocol.ConnectionID) error
type baseCryptoSetup interface {
HandleCryptoStream() error
ConnectionState() ConnectionState
HandleMessage([]byte, protocol.EncryptionLevel) bool
ConnectionState() tls.ConnectionState
GetSealer() (protocol.EncryptionLevel, Sealer)
GetSealerWithEncryptionLevel(protocol.EncryptionLevel) (Sealer, error)
GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer)
}
// CryptoSetup is the crypto setup used by gQUIC
type CryptoSetup interface {
baseCryptoSetup
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error)
}
// CryptoSetupTLS is the crypto setup used by IETF QUIC
type CryptoSetupTLS interface {
baseCryptoSetup
OpenHandshake(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
Open1RTT(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
GetOpener(protocol.EncryptionLevel) (Opener, error)
}
// ConnectionState records basic details about the QUIC connection.

View File

@@ -1,3 +0,0 @@
package handshake
//go:generate sh -c "../mockgen_internal.sh handshake mock_mint_tls_test.go github.com/lucas-clemente/quic-go/internal/handshake mintTLS"

View File

@@ -0,0 +1,132 @@
package handshake
import (
"crypto/tls"
"net"
"time"
"unsafe"
"github.com/marten-seemann/qtls"
)
type conn struct {
remoteAddr net.Addr
}
func newConn(remote net.Addr) net.Conn {
return &conn{remoteAddr: remote}
}
var _ net.Conn = &conn{}
func (c *conn) Read([]byte) (int, error) { return 0, nil }
func (c *conn) Write([]byte) (int, error) { return 0, nil }
func (c *conn) Close() error { return nil }
func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
func (c *conn) LocalAddr() net.Addr { return nil }
func (c *conn) SetReadDeadline(time.Time) error { return nil }
func (c *conn) SetWriteDeadline(time.Time) error { return nil }
func (c *conn) SetDeadline(time.Time) error { return nil }
type clientSessionCache struct {
tls.ClientSessionCache
}
var _ qtls.ClientSessionCache = &clientSessionCache{}
func (c *clientSessionCache) Get(sessionKey string) (*qtls.ClientSessionState, bool) {
sess, ok := c.ClientSessionCache.Get(sessionKey)
if sess == nil {
return nil, ok
}
// qtls.ClientSessionState is identical to the tls.ClientSessionState.
// In order to allow users of quic-go to use a tls.Config,
// we need this workaround to use the ClientSessionCache.
// In unsafe.go we check that the two structs are actually identical.
usess := (*[unsafe.Sizeof(*sess)]byte)(unsafe.Pointer(sess))[:]
var session qtls.ClientSessionState
usession := (*[unsafe.Sizeof(session)]byte)(unsafe.Pointer(&session))[:]
copy(usession, usess)
return &session, ok
}
func (c *clientSessionCache) Put(sessionKey string, cs *qtls.ClientSessionState) {
// qtls.ClientSessionState is identical to the tls.ClientSessionState.
// In order to allow users of quic-go to use a tls.Config,
// we need this workaround to use the ClientSessionCache.
// In unsafe.go we check that the two structs are actually identical.
usess := (*[unsafe.Sizeof(*cs)]byte)(unsafe.Pointer(cs))[:]
var session tls.ClientSessionState
usession := (*[unsafe.Sizeof(session)]byte)(unsafe.Pointer(&session))[:]
copy(usession, usess)
c.ClientSessionCache.Put(sessionKey, &session)
}
func tlsConfigToQtlsConfig(
c *tls.Config,
recordLayer qtls.RecordLayer,
extHandler tlsExtensionHandler,
) *qtls.Config {
if c == nil {
c = &tls.Config{}
}
// Clone the config first. This executes the tls.Config.serverInit().
// This sets the SessionTicketKey, if the user didn't supply one.
c = c.Clone()
// QUIC requires TLS 1.3 or newer
minVersion := c.MinVersion
if minVersion < qtls.VersionTLS13 {
minVersion = qtls.VersionTLS13
}
maxVersion := c.MaxVersion
if maxVersion < qtls.VersionTLS13 {
maxVersion = qtls.VersionTLS13
}
var getConfigForClient func(ch *tls.ClientHelloInfo) (*qtls.Config, error)
if c.GetConfigForClient != nil {
getConfigForClient = func(ch *tls.ClientHelloInfo) (*qtls.Config, error) {
tlsConf, err := c.GetConfigForClient(ch)
if err != nil {
return nil, err
}
if tlsConf == nil {
return nil, nil
}
return tlsConfigToQtlsConfig(tlsConf, recordLayer, extHandler), nil
}
}
var csc qtls.ClientSessionCache
if c.ClientSessionCache != nil {
csc = &clientSessionCache{c.ClientSessionCache}
}
return &qtls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
GetClientCertificate: c.GetClientCertificate,
GetConfigForClient: getConfigForClient,
VerifyPeerCertificate: c.VerifyPeerCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: csc,
MinVersion: minVersion,
MaxVersion: maxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
// no need to copy Renegotiation, it's not supported by TLS 1.3
KeyLogWriter: c.KeyLogWriter,
AlternativeRecordLayer: recordLayer,
GetExtensions: extHandler.GetExtensions,
ReceivedExtensions: extHandler.ReceivedExtensions,
}
}

View File

@@ -1,73 +0,0 @@
package handshake
import (
"bytes"
"crypto/rand"
"github.com/lucas-clemente/quic-go/internal/crypto"
)
// ServerConfig is a server config
type ServerConfig struct {
kex crypto.KeyExchange
certChain crypto.CertChain
ID []byte
obit []byte
cookieGenerator *CookieGenerator
}
// NewServerConfig creates a new server config
func NewServerConfig(kex crypto.KeyExchange, certChain crypto.CertChain) (*ServerConfig, error) {
id := make([]byte, 16)
_, err := rand.Read(id)
if err != nil {
return nil, err
}
obit := make([]byte, 8)
if _, err = rand.Read(obit); err != nil {
return nil, err
}
cookieGenerator, err := NewCookieGenerator()
if err != nil {
return nil, err
}
return &ServerConfig{
kex: kex,
certChain: certChain,
ID: id,
obit: obit,
cookieGenerator: cookieGenerator,
}, nil
}
// Get the server config binary representation
func (s *ServerConfig) Get() []byte {
var serverConfig bytes.Buffer
msg := HandshakeMessage{
Tag: TagSCFG,
Data: map[Tag][]byte{
TagSCID: s.ID,
TagKEXS: []byte("C255"),
TagAEAD: []byte("AESG"),
TagPUBS: append([]byte{0x20, 0x00, 0x00}, s.kex.PublicKey()...),
TagOBIT: s.obit,
TagEXPY: {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
},
}
msg.Write(&serverConfig)
return serverConfig.Bytes()
}
// Sign the server config and CHLO with the server's keyData
func (s *ServerConfig) Sign(sni string, chlo []byte) ([]byte, error) {
return s.certChain.SignServerProof(sni, chlo, s.Get())
}
// GetCertsCompressed returns the certificate data
func (s *ServerConfig) GetCertsCompressed(sni string, commonSetHashes, compressedHashes []byte) ([]byte, error) {
return s.certChain.GetCertsCompressed(sni, commonSetHashes, compressedHashes)
}

View File

@@ -1,184 +0,0 @@
package handshake
import (
"bytes"
"encoding/binary"
"errors"
"math"
"time"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
type serverConfigClient struct {
raw []byte
ID []byte
obit []byte
expiry time.Time
kex crypto.KeyExchange
sharedSecret []byte
}
var (
errMessageNotServerConfig = errors.New("ServerConfig must have TagSCFG")
)
// parseServerConfig parses a server config
func parseServerConfig(data []byte) (*serverConfigClient, error) {
message, err := ParseHandshakeMessage(bytes.NewReader(data))
if err != nil {
return nil, err
}
if message.Tag != TagSCFG {
return nil, errMessageNotServerConfig
}
scfg := &serverConfigClient{raw: data}
err = scfg.parseValues(message.Data)
if err != nil {
return nil, err
}
return scfg, nil
}
func (s *serverConfigClient) parseValues(tagMap map[Tag][]byte) error {
// SCID
scfgID, ok := tagMap[TagSCID]
if !ok {
return qerr.Error(qerr.CryptoMessageParameterNotFound, "SCID")
}
if len(scfgID) != 16 {
return qerr.Error(qerr.CryptoInvalidValueLength, "SCID")
}
s.ID = scfgID
// KEXS
// TODO: setup Key Exchange
kexs, ok := tagMap[TagKEXS]
if !ok {
return qerr.Error(qerr.CryptoMessageParameterNotFound, "KEXS")
}
if len(kexs)%4 != 0 {
return qerr.Error(qerr.CryptoInvalidValueLength, "KEXS")
}
c255Foundat := -1
for i := 0; i < len(kexs)/4; i++ {
if bytes.Equal(kexs[4*i:4*i+4], []byte("C255")) {
c255Foundat = i
break
}
}
if c255Foundat < 0 {
return qerr.Error(qerr.CryptoNoSupport, "KEXS: Could not find C255, other key exchanges are not supported")
}
// AEAD
aead, ok := tagMap[TagAEAD]
if !ok {
return qerr.Error(qerr.CryptoMessageParameterNotFound, "AEAD")
}
if len(aead)%4 != 0 {
return qerr.Error(qerr.CryptoInvalidValueLength, "AEAD")
}
var aesgFound bool
for i := 0; i < len(aead)/4; i++ {
if bytes.Equal(aead[4*i:4*i+4], []byte("AESG")) {
aesgFound = true
break
}
}
if !aesgFound {
return qerr.Error(qerr.CryptoNoSupport, "AEAD")
}
// PUBS
pubs, ok := tagMap[TagPUBS]
if !ok {
return qerr.Error(qerr.CryptoMessageParameterNotFound, "PUBS")
}
var pubsKexs []struct {
Length uint32
Value []byte
}
var lastLen uint32
for i := 0; i < len(pubs)-3; i += int(lastLen) + 3 {
// the PUBS value is always prepended by 3 byte little endian length field
err := binary.Read(bytes.NewReader([]byte{pubs[i], pubs[i+1], pubs[i+2], 0x00}), binary.LittleEndian, &lastLen)
if err != nil {
return qerr.Error(qerr.CryptoInvalidValueLength, "PUBS not decodable")
}
if lastLen == 0 {
return qerr.Error(qerr.CryptoInvalidValueLength, "PUBS")
}
if i+3+int(lastLen) > len(pubs) {
return qerr.Error(qerr.CryptoInvalidValueLength, "PUBS")
}
pubsKexs = append(pubsKexs, struct {
Length uint32
Value []byte
}{lastLen, pubs[i+3 : i+3+int(lastLen)]})
}
if c255Foundat >= len(pubsKexs) {
return qerr.Error(qerr.CryptoMessageParameterNotFound, "KEXS not in PUBS")
}
if pubsKexs[c255Foundat].Length != 32 {
return qerr.Error(qerr.CryptoInvalidValueLength, "PUBS")
}
var err error
s.kex, err = crypto.NewCurve25519KEX()
if err != nil {
return err
}
s.sharedSecret, err = s.kex.CalculateSharedKey(pubsKexs[c255Foundat].Value)
if err != nil {
return err
}
// OBIT
obit, ok := tagMap[TagOBIT]
if !ok {
return qerr.Error(qerr.CryptoMessageParameterNotFound, "OBIT")
}
if len(obit) != 8 {
return qerr.Error(qerr.CryptoInvalidValueLength, "OBIT")
}
s.obit = obit
// EXPY
expy, ok := tagMap[TagEXPY]
if !ok {
return qerr.Error(qerr.CryptoMessageParameterNotFound, "EXPY")
}
if len(expy) != 8 {
return qerr.Error(qerr.CryptoInvalidValueLength, "EXPY")
}
// make sure that the value doesn't overflow an int64
// furthermore, values close to MaxInt64 are not a valid input to time.Unix, thus set MaxInt64/2 as the maximum value here
expyTimestamp := utils.MinUint64(binary.LittleEndian.Uint64(expy), math.MaxInt64/2)
s.expiry = time.Unix(int64(expyTimestamp), 0)
// TODO: implement VER
return nil
}
func (s *serverConfigClient) IsExpired() bool {
return s.expiry.Before(time.Now())
}
func (s *serverConfigClient) Get() []byte {
return s.raw
}

View File

@@ -1,93 +0,0 @@
package handshake
// A Tag in the QUIC crypto
type Tag uint32
const (
// TagCHLO is a client hello
TagCHLO Tag = 'C' + 'H'<<8 + 'L'<<16 + 'O'<<24
// TagREJ is a server hello rejection
TagREJ Tag = 'R' + 'E'<<8 + 'J'<<16
// TagSCFG is a server config
TagSCFG Tag = 'S' + 'C'<<8 + 'F'<<16 + 'G'<<24
// TagPAD is padding
TagPAD Tag = 'P' + 'A'<<8 + 'D'<<16
// TagSNI is the server name indication
TagSNI Tag = 'S' + 'N'<<8 + 'I'<<16
// TagVER is the QUIC version
TagVER Tag = 'V' + 'E'<<8 + 'R'<<16
// TagCCS are the hashes of the common certificate sets
TagCCS Tag = 'C' + 'C'<<8 + 'S'<<16
// TagCCRT are the hashes of the cached certificates
TagCCRT Tag = 'C' + 'C'<<8 + 'R'<<16 + 'T'<<24
// TagMSPC is max streams per connection
TagMSPC Tag = 'M' + 'S'<<8 + 'P'<<16 + 'C'<<24
// TagMIDS is max incoming dyanamic streams
TagMIDS Tag = 'M' + 'I'<<8 + 'D'<<16 + 'S'<<24
// TagUAID is the user agent ID
TagUAID Tag = 'U' + 'A'<<8 + 'I'<<16 + 'D'<<24
// TagSVID is the server ID (unofficial tag by us :)
TagSVID Tag = 'S' + 'V'<<8 + 'I'<<16 + 'D'<<24
// TagTCID is truncation of the connection ID
TagTCID Tag = 'T' + 'C'<<8 + 'I'<<16 + 'D'<<24
// TagPDMD is the proof demand
TagPDMD Tag = 'P' + 'D'<<8 + 'M'<<16 + 'D'<<24
// TagSRBF is the socket receive buffer
TagSRBF Tag = 'S' + 'R'<<8 + 'B'<<16 + 'F'<<24
// TagICSL is the idle connection state lifetime
TagICSL Tag = 'I' + 'C'<<8 + 'S'<<16 + 'L'<<24
// TagNONP is the client proof nonce
TagNONP Tag = 'N' + 'O'<<8 + 'N'<<16 + 'P'<<24
// TagSCLS is the silently close timeout
TagSCLS Tag = 'S' + 'C'<<8 + 'L'<<16 + 'S'<<24
// TagCSCT is the signed cert timestamp (RFC6962) of leaf cert
TagCSCT Tag = 'C' + 'S'<<8 + 'C'<<16 + 'T'<<24
// TagCOPT are the connection options
TagCOPT Tag = 'C' + 'O'<<8 + 'P'<<16 + 'T'<<24
// TagCFCW is the initial session/connection flow control receive window
TagCFCW Tag = 'C' + 'F'<<8 + 'C'<<16 + 'W'<<24
// TagSFCW is the initial stream flow control receive window.
TagSFCW Tag = 'S' + 'F'<<8 + 'C'<<16 + 'W'<<24
// TagNSTP is the no STOP_WAITING experiment
// currently unsupported by quic-go
TagNSTP Tag = 'N' + 'S'<<8 + 'T'<<16 + 'P'<<24
// TagSTK is the source-address token
TagSTK Tag = 'S' + 'T'<<8 + 'K'<<16
// TagSNO is the server nonce
TagSNO Tag = 'S' + 'N'<<8 + 'O'<<16
// TagPROF is the server proof
TagPROF Tag = 'P' + 'R'<<8 + 'O'<<16 + 'F'<<24
// TagNONC is the client nonce
TagNONC Tag = 'N' + 'O'<<8 + 'N'<<16 + 'C'<<24
// TagXLCT is the expected leaf certificate
TagXLCT Tag = 'X' + 'L'<<8 + 'C'<<16 + 'T'<<24
// TagSCID is the server config ID
TagSCID Tag = 'S' + 'C'<<8 + 'I'<<16 + 'D'<<24
// TagKEXS is the list of key exchange algos
TagKEXS Tag = 'K' + 'E'<<8 + 'X'<<16 + 'S'<<24
// TagAEAD is the list of AEAD algos
TagAEAD Tag = 'A' + 'E'<<8 + 'A'<<16 + 'D'<<24
// TagPUBS is the public value for the KEX
TagPUBS Tag = 'P' + 'U'<<8 + 'B'<<16 + 'S'<<24
// TagOBIT is the client orbit
TagOBIT Tag = 'O' + 'B'<<8 + 'I'<<16 + 'T'<<24
// TagEXPY is the server config expiry
TagEXPY Tag = 'E' + 'X'<<8 + 'P'<<16 + 'Y'<<24
// TagCERT is the CERT data
TagCERT Tag = 0xff545243
// TagSHLO is the server hello
TagSHLO Tag = 'S' + 'H'<<8 + 'L'<<16 + 'O'<<24
// TagPRST is the public reset tag
TagPRST Tag = 'P' + 'R'<<8 + 'S'<<16 + 'T'<<24
// TagRSEQ is the public reset rejected packet number
TagRSEQ Tag = 'R' + 'S'<<8 + 'E'<<16 + 'Q'<<24
// TagRNON is the public reset nonce
TagRNON Tag = 'R' + 'N'<<8 + 'O'<<16 + 'N'<<24
)

View File

@@ -1,123 +0,0 @@
package handshake
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
type transportParameterID uint16
const quicTLSExtensionType = 0xff5
const (
initialMaxStreamDataParameterID transportParameterID = 0x0
initialMaxDataParameterID transportParameterID = 0x1
initialMaxBidiStreamsParameterID transportParameterID = 0x2
idleTimeoutParameterID transportParameterID = 0x3
maxPacketSizeParameterID transportParameterID = 0x5
statelessResetTokenParameterID transportParameterID = 0x6
initialMaxUniStreamsParameterID transportParameterID = 0x8
disableMigrationParameterID transportParameterID = 0x9
)
type clientHelloTransportParameters struct {
InitialVersion protocol.VersionNumber
Parameters TransportParameters
}
func (p *clientHelloTransportParameters) Marshal() []byte {
const lenOffset = 4
b := &bytes.Buffer{}
utils.BigEndian.WriteUint32(b, uint32(p.InitialVersion))
b.Write([]byte{0, 0}) // length. Will be replaced later
p.Parameters.marshal(b)
data := b.Bytes()
binary.BigEndian.PutUint16(data[lenOffset:lenOffset+2], uint16(len(data)-lenOffset-2))
return data
}
func (p *clientHelloTransportParameters) Unmarshal(data []byte) error {
if len(data) < 6 {
return errors.New("transport parameter data too short")
}
p.InitialVersion = protocol.VersionNumber(binary.BigEndian.Uint32(data[:4]))
paramsLen := int(binary.BigEndian.Uint16(data[4:6]))
data = data[6:]
if len(data) != paramsLen {
return fmt.Errorf("expected transport parameters to be %d bytes long, have %d", paramsLen, len(data))
}
return p.Parameters.unmarshal(data)
}
type encryptedExtensionsTransportParameters struct {
NegotiatedVersion protocol.VersionNumber
SupportedVersions []protocol.VersionNumber
Parameters TransportParameters
}
func (p *encryptedExtensionsTransportParameters) Marshal() []byte {
b := &bytes.Buffer{}
utils.BigEndian.WriteUint32(b, uint32(p.NegotiatedVersion))
b.WriteByte(uint8(4 * len(p.SupportedVersions)))
for _, v := range p.SupportedVersions {
utils.BigEndian.WriteUint32(b, uint32(v))
}
lenOffset := b.Len()
b.Write([]byte{0, 0}) // length. Will be replaced later
p.Parameters.marshal(b)
data := b.Bytes()
binary.BigEndian.PutUint16(data[lenOffset:lenOffset+2], uint16(len(data)-lenOffset-2))
return data
}
func (p *encryptedExtensionsTransportParameters) Unmarshal(data []byte) error {
if len(data) < 5 {
return errors.New("transport parameter data too short")
}
p.NegotiatedVersion = protocol.VersionNumber(binary.BigEndian.Uint32(data[:4]))
numVersions := int(data[4])
if numVersions%4 != 0 {
return fmt.Errorf("invalid length for version list: %d", numVersions)
}
numVersions /= 4
data = data[5:]
if len(data) < 4*numVersions+2 /*length field for the parameter list */ {
return errors.New("transport parameter data too short")
}
p.SupportedVersions = make([]protocol.VersionNumber, numVersions)
for i := 0; i < numVersions; i++ {
p.SupportedVersions[i] = protocol.VersionNumber(binary.BigEndian.Uint32(data[:4]))
data = data[4:]
}
paramsLen := int(binary.BigEndian.Uint16(data[:2]))
data = data[2:]
if len(data) != paramsLen {
return fmt.Errorf("expected transport parameters to be %d bytes long, have %d", paramsLen, len(data))
}
return p.Parameters.unmarshal(data)
}
type tlsExtensionBody struct {
data []byte
}
var _ mint.ExtensionBody = &tlsExtensionBody{}
func (e *tlsExtensionBody) Type() mint.ExtensionType {
return quicTLSExtensionType
}
func (e *tlsExtensionBody) Marshal() ([]byte, error) {
return e.data, nil
}
func (e *tlsExtensionBody) Unmarshal(data []byte) (int, error) {
e.data = data
return len(data), nil
}

View File

@@ -0,0 +1,58 @@
package handshake
import (
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/marten-seemann/qtls"
)
const quicTLSExtensionType = 0xffa5
type extensionHandler struct {
ourParams []byte
paramsChan chan []byte
perspective protocol.Perspective
}
var _ tlsExtensionHandler = &extensionHandler{}
// newExtensionHandler creates a new extension handler
func newExtensionHandler(params []byte, pers protocol.Perspective) tlsExtensionHandler {
return &extensionHandler{
ourParams: params,
paramsChan: make(chan []byte),
perspective: pers,
}
}
func (h *extensionHandler) GetExtensions(msgType uint8) []qtls.Extension {
if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeClientHello) ||
(h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeEncryptedExtensions) {
return nil
}
return []qtls.Extension{{
Type: quicTLSExtensionType,
Data: h.ourParams,
}}
}
func (h *extensionHandler) ReceivedExtensions(msgType uint8, exts []qtls.Extension) {
if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeEncryptedExtensions) ||
(h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeClientHello) {
return
}
var data []byte
for _, ext := range exts {
if ext.Type == quicTLSExtensionType {
data = ext.Data
break
}
}
h.paramsChan <- data
}
func (h *extensionHandler) TransportParameters() <-chan []byte {
return h.paramsChan
}

View File

@@ -1,112 +0,0 @@
package handshake
import (
"errors"
"fmt"
"github.com/lucas-clemente/quic-go/qerr"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
type extensionHandlerClient struct {
ourParams *TransportParameters
paramsChan chan TransportParameters
initialVersion protocol.VersionNumber
supportedVersions []protocol.VersionNumber
version protocol.VersionNumber
logger utils.Logger
}
var _ mint.AppExtensionHandler = &extensionHandlerClient{}
var _ TLSExtensionHandler = &extensionHandlerClient{}
// NewExtensionHandlerClient creates a new extension handler for the client.
func NewExtensionHandlerClient(
params *TransportParameters,
initialVersion protocol.VersionNumber,
supportedVersions []protocol.VersionNumber,
version protocol.VersionNumber,
logger utils.Logger,
) TLSExtensionHandler {
// The client reads the transport parameters from the Encrypted Extensions message.
// The paramsChan is used in the session's run loop's select statement.
// We have to use an unbuffered channel here to make sure that the session actually processes the transport parameters immediately.
paramsChan := make(chan TransportParameters)
return &extensionHandlerClient{
ourParams: params,
paramsChan: paramsChan,
initialVersion: initialVersion,
supportedVersions: supportedVersions,
version: version,
logger: logger,
}
}
func (h *extensionHandlerClient) Send(hType mint.HandshakeType, el *mint.ExtensionList) error {
if hType != mint.HandshakeTypeClientHello {
return nil
}
h.logger.Debugf("Sending Transport Parameters: %s", h.ourParams)
chtp := &clientHelloTransportParameters{
InitialVersion: h.initialVersion,
Parameters: *h.ourParams,
}
return el.Add(&tlsExtensionBody{data: chtp.Marshal()})
}
func (h *extensionHandlerClient) Receive(hType mint.HandshakeType, el *mint.ExtensionList) error {
ext := &tlsExtensionBody{}
found, err := el.Find(ext)
if err != nil {
return err
}
if hType != mint.HandshakeTypeEncryptedExtensions {
if found {
return fmt.Errorf("Unexpected QUIC extension in handshake message %d", hType)
}
return nil
}
// hType == mint.HandshakeTypeEncryptedExtensions
if !found {
return errors.New("EncryptedExtensions message didn't contain a QUIC extension")
}
eetp := &encryptedExtensionsTransportParameters{}
if err := eetp.Unmarshal(ext.data); err != nil {
return err
}
// check that the negotiated_version is the current version
if eetp.NegotiatedVersion != h.version {
return qerr.Error(qerr.VersionNegotiationMismatch, "current version doesn't match negotiated_version")
}
// check that the current version is included in the supported versions
if !protocol.IsSupportedVersion(eetp.SupportedVersions, h.version) {
return qerr.Error(qerr.VersionNegotiationMismatch, "current version not included in the supported versions")
}
// if version negotiation was performed, check that we would have selected the current version based on the supported versions sent by the server
if h.version != h.initialVersion {
negotiatedVersion, ok := protocol.ChooseSupportedVersion(h.supportedVersions, eetp.SupportedVersions)
if !ok || h.version != negotiatedVersion {
return qerr.Error(qerr.VersionNegotiationMismatch, "would have picked a different version")
}
}
// check that the server sent a stateless reset token
if len(eetp.Parameters.StatelessResetToken) == 0 {
return errors.New("server didn't sent stateless_reset_token")
}
h.logger.Debugf("Received Transport Parameters: %s", &eetp.Parameters)
h.paramsChan <- eetp.Parameters
return nil
}
func (h *extensionHandlerClient) GetPeerParams() <-chan TransportParameters {
return h.paramsChan
}

View File

@@ -1,100 +0,0 @@
package handshake
import (
"errors"
"fmt"
"github.com/lucas-clemente/quic-go/qerr"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
type extensionHandlerServer struct {
ourParams *TransportParameters
paramsChan chan TransportParameters
version protocol.VersionNumber
supportedVersions []protocol.VersionNumber
logger utils.Logger
}
var _ mint.AppExtensionHandler = &extensionHandlerServer{}
var _ TLSExtensionHandler = &extensionHandlerServer{}
// NewExtensionHandlerServer creates a new extension handler for the server
func NewExtensionHandlerServer(
params *TransportParameters,
supportedVersions []protocol.VersionNumber,
version protocol.VersionNumber,
logger utils.Logger,
) TLSExtensionHandler {
// Processing the ClientHello is performed statelessly (and from a single go-routine).
// Therefore, we have to use a buffered chan to pass the transport parameters to that go routine.
paramsChan := make(chan TransportParameters, 1)
return &extensionHandlerServer{
ourParams: params,
paramsChan: paramsChan,
supportedVersions: supportedVersions,
version: version,
logger: logger,
}
}
func (h *extensionHandlerServer) Send(hType mint.HandshakeType, el *mint.ExtensionList) error {
if hType != mint.HandshakeTypeEncryptedExtensions {
return nil
}
h.logger.Debugf("Sending Transport Parameters: %s", h.ourParams)
eetp := &encryptedExtensionsTransportParameters{
NegotiatedVersion: h.version,
SupportedVersions: protocol.GetGreasedVersions(h.supportedVersions),
Parameters: *h.ourParams,
}
return el.Add(&tlsExtensionBody{data: eetp.Marshal()})
}
func (h *extensionHandlerServer) Receive(hType mint.HandshakeType, el *mint.ExtensionList) error {
ext := &tlsExtensionBody{}
found, err := el.Find(ext)
if err != nil {
return err
}
if hType != mint.HandshakeTypeClientHello {
if found {
return fmt.Errorf("Unexpected QUIC extension in handshake message %d", hType)
}
return nil
}
if !found {
return errors.New("ClientHello didn't contain a QUIC extension")
}
chtp := &clientHelloTransportParameters{}
if err := chtp.Unmarshal(ext.data); err != nil {
return err
}
// perform the stateless version negotiation validation:
// make sure that we would have sent a Version Negotiation Packet if the client offered the initial version
// this is the case if and only if the initial version is not contained in the supported versions
if chtp.InitialVersion != h.version && protocol.IsSupportedVersion(h.supportedVersions, chtp.InitialVersion) {
return qerr.Error(qerr.VersionNegotiationMismatch, "Client should have used the initial version")
}
// check that the client didn't send a stateless reset token
if len(chtp.Parameters.StatelessResetToken) != 0 {
// TODO: return the correct error type
return errors.New("client sent a stateless reset token")
}
h.logger.Debugf("Received Transport Parameters: %s", &chtp.Parameters)
h.paramsChan <- chtp.Parameters
return nil
}
func (h *extensionHandlerServer) GetPeerParams() <-chan TransportParameters {
return h.paramsChan
}

View File

@@ -3,158 +3,123 @@ package handshake
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"sort"
"time"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
// errMalformedTag is returned when the tag value cannot be read
var errMalformedTag = qerr.Error(qerr.InvalidCryptoMessageParameter, "malformed Tag value")
type transportParameterID uint16
const (
originalConnectionIDParameterID transportParameterID = 0x0
idleTimeoutParameterID transportParameterID = 0x1
statelessResetTokenParameterID transportParameterID = 0x2
maxPacketSizeParameterID transportParameterID = 0x3
initialMaxDataParameterID transportParameterID = 0x4
initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5
initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6
initialMaxStreamDataUniParameterID transportParameterID = 0x7
initialMaxStreamsBidiParameterID transportParameterID = 0x8
initialMaxStreamsUniParameterID transportParameterID = 0x9
ackDelayExponentParameterID transportParameterID = 0xa
disableMigrationParameterID transportParameterID = 0xc
)
// TransportParameters are parameters sent to the peer during the handshake
type TransportParameters struct {
StreamFlowControlWindow protocol.ByteCount
ConnectionFlowControlWindow protocol.ByteCount
InitialMaxStreamDataBidiLocal protocol.ByteCount
InitialMaxStreamDataBidiRemote protocol.ByteCount
InitialMaxStreamDataUni protocol.ByteCount
InitialMaxData protocol.ByteCount
AckDelayExponent uint8
MaxPacketSize protocol.ByteCount
MaxUniStreams uint16 // only used for IETF QUIC
MaxBidiStreams uint16 // only used for IETF QUIC
MaxStreams uint32 // only used for gQUIC
MaxUniStreams uint64
MaxBidiStreams uint64
OmitConnectionID bool // only used for gQUIC
IdleTimeout time.Duration
DisableMigration bool // only used for IETF QUIC
StatelessResetToken []byte // only used for IETF QUIC
IdleTimeout time.Duration
DisableMigration bool
StatelessResetToken *[16]byte
OriginalConnectionID protocol.ConnectionID
}
// readHelloMap reads the transport parameters from the tags sent in a gQUIC handshake message
func readHelloMap(tags map[Tag][]byte) (*TransportParameters, error) {
params := &TransportParameters{}
if value, ok := tags[TagTCID]; ok {
v, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value))
if err != nil {
return nil, errMalformedTag
}
params.OmitConnectionID = (v == 0)
// Unmarshal the transport parameters
func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error {
if len(data) < 2 {
return errors.New("transport parameter data too short")
}
if value, ok := tags[TagMIDS]; ok {
v, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value))
if err != nil {
return nil, errMalformedTag
}
params.MaxStreams = v
length := binary.BigEndian.Uint16(data[:2])
if len(data)-2 < int(length) {
return fmt.Errorf("expected transport parameters to be %d bytes long, have %d", length, len(data)-2)
}
if value, ok := tags[TagICSL]; ok {
v, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value))
if err != nil {
return nil, errMalformedTag
}
params.IdleTimeout = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(v)*time.Second)
}
if value, ok := tags[TagSFCW]; ok {
v, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value))
if err != nil {
return nil, errMalformedTag
}
params.StreamFlowControlWindow = protocol.ByteCount(v)
}
if value, ok := tags[TagCFCW]; ok {
v, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value))
if err != nil {
return nil, errMalformedTag
}
params.ConnectionFlowControlWindow = protocol.ByteCount(v)
}
return params, nil
}
// GetHelloMap gets all parameters needed for the Hello message in the gQUIC handshake.
func (p *TransportParameters) getHelloMap() map[Tag][]byte {
sfcw := bytes.NewBuffer([]byte{})
utils.LittleEndian.WriteUint32(sfcw, uint32(p.StreamFlowControlWindow))
cfcw := bytes.NewBuffer([]byte{})
utils.LittleEndian.WriteUint32(cfcw, uint32(p.ConnectionFlowControlWindow))
mids := bytes.NewBuffer([]byte{})
utils.LittleEndian.WriteUint32(mids, p.MaxStreams)
icsl := bytes.NewBuffer([]byte{})
utils.LittleEndian.WriteUint32(icsl, uint32(p.IdleTimeout/time.Second))
tags := map[Tag][]byte{
TagICSL: icsl.Bytes(),
TagMIDS: mids.Bytes(),
TagCFCW: cfcw.Bytes(),
TagSFCW: sfcw.Bytes(),
}
if p.OmitConnectionID {
tags[TagTCID] = []byte{0, 0, 0, 0}
}
return tags
}
func (p *TransportParameters) unmarshal(data []byte) error {
// needed to check that every parameter is only sent at most once
var parameterIDs []transportParameterID
for len(data) >= 4 {
paramID := transportParameterID(binary.BigEndian.Uint16(data[:2]))
paramLen := int(binary.BigEndian.Uint16(data[2:4]))
data = data[4:]
if len(data) < paramLen {
return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", len(data), paramLen)
}
var readAckDelayExponent bool
r := bytes.NewReader(data[2:])
for r.Len() >= 4 {
paramIDInt, _ := utils.BigEndian.ReadUint16(r)
paramID := transportParameterID(paramIDInt)
paramLen, _ := utils.BigEndian.ReadUint16(r)
parameterIDs = append(parameterIDs, paramID)
switch paramID {
case initialMaxStreamDataParameterID:
if paramLen != 4 {
return fmt.Errorf("wrong length for initial_max_stream_data: %d (expected 4)", paramLen)
case ackDelayExponentParameterID:
readAckDelayExponent = true
fallthrough
case initialMaxStreamDataBidiLocalParameterID,
initialMaxStreamDataBidiRemoteParameterID,
initialMaxStreamDataUniParameterID,
initialMaxDataParameterID,
initialMaxStreamsBidiParameterID,
initialMaxStreamsUniParameterID,
idleTimeoutParameterID,
maxPacketSizeParameterID:
if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil {
return err
}
p.StreamFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(data[:4]))
case initialMaxDataParameterID:
if paramLen != 4 {
return fmt.Errorf("wrong length for initial_max_data: %d (expected 4)", paramLen)
default:
if r.Len() < int(paramLen) {
return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", r.Len(), paramLen)
}
p.ConnectionFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(data[:4]))
case initialMaxBidiStreamsParameterID:
if paramLen != 2 {
return fmt.Errorf("wrong length for initial_max_stream_id_bidi: %d (expected 2)", paramLen)
switch paramID {
case disableMigrationParameterID:
if paramLen != 0 {
return fmt.Errorf("wrong length for disable_migration: %d (expected empty)", paramLen)
}
p.DisableMigration = true
case statelessResetTokenParameterID:
if sentBy == protocol.PerspectiveClient {
return errors.New("client sent a stateless_reset_token")
}
if paramLen != 16 {
return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen)
}
var token [16]byte
r.Read(token[:])
p.StatelessResetToken = &token
case originalConnectionIDParameterID:
if sentBy == protocol.PerspectiveClient {
return errors.New("client sent an original_connection_id")
}
p.OriginalConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen))
default:
r.Seek(int64(paramLen), io.SeekCurrent)
}
p.MaxBidiStreams = binary.BigEndian.Uint16(data[:2])
case initialMaxUniStreamsParameterID:
if paramLen != 2 {
return fmt.Errorf("wrong length for initial_max_stream_id_uni: %d (expected 2)", paramLen)
}
p.MaxUniStreams = binary.BigEndian.Uint16(data[:2])
case idleTimeoutParameterID:
if paramLen != 2 {
return fmt.Errorf("wrong length for idle_timeout: %d (expected 2)", paramLen)
}
p.IdleTimeout = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(binary.BigEndian.Uint16(data[:2]))*time.Second)
case maxPacketSizeParameterID:
if paramLen != 2 {
return fmt.Errorf("wrong length for max_packet_size: %d (expected 2)", paramLen)
}
maxPacketSize := protocol.ByteCount(binary.BigEndian.Uint16(data[:2]))
if maxPacketSize < 1200 {
return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", maxPacketSize)
}
p.MaxPacketSize = maxPacketSize
case disableMigrationParameterID:
if paramLen != 0 {
return fmt.Errorf("wrong length for disable_migration: %d (expected empty)", paramLen)
}
p.DisableMigration = true
case statelessResetTokenParameterID:
if paramLen != 16 {
return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen)
}
p.StatelessResetToken = data[:16]
}
data = data[paramLen:]
}
if !readAckDelayExponent {
p.AckDelayExponent = protocol.DefaultAckDelayExponent
}
// check that every transport parameter was sent at most once
@@ -165,51 +130,131 @@ func (p *TransportParameters) unmarshal(data []byte) error {
}
}
if len(data) != 0 {
return fmt.Errorf("should have read all data. Still have %d bytes", len(data))
if r.Len() != 0 {
return fmt.Errorf("should have read all data. Still have %d bytes", r.Len())
}
return nil
}
func (p *TransportParameters) marshal(b *bytes.Buffer) {
// initial_max_stream_data
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataParameterID))
utils.BigEndian.WriteUint16(b, 4)
utils.BigEndian.WriteUint32(b, uint32(p.StreamFlowControlWindow))
func (p *TransportParameters) readNumericTransportParameter(
r *bytes.Reader,
paramID transportParameterID,
expectedLen int,
) error {
remainingLen := r.Len()
val, err := utils.ReadVarInt(r)
if err != nil {
return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err)
}
if remainingLen-r.Len() != expectedLen {
return fmt.Errorf("inconsistent transport parameter length for %d", paramID)
}
switch paramID {
case initialMaxStreamDataBidiLocalParameterID:
p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val)
case initialMaxStreamDataBidiRemoteParameterID:
p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val)
case initialMaxStreamDataUniParameterID:
p.InitialMaxStreamDataUni = protocol.ByteCount(val)
case initialMaxDataParameterID:
p.InitialMaxData = protocol.ByteCount(val)
case initialMaxStreamsBidiParameterID:
p.MaxBidiStreams = val
case initialMaxStreamsUniParameterID:
p.MaxUniStreams = val
case idleTimeoutParameterID:
p.IdleTimeout = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond)
case maxPacketSizeParameterID:
if val < 1200 {
return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", val)
}
p.MaxPacketSize = protocol.ByteCount(val)
case ackDelayExponentParameterID:
if val > protocol.MaxAckDelayExponent {
return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent)
}
p.AckDelayExponent = uint8(val)
default:
return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID)
}
return nil
}
// Marshal the transport parameters
func (p *TransportParameters) Marshal() []byte {
b := &bytes.Buffer{}
b.Write([]byte{0, 0}) // length. Will be replaced later
// initial_max_stream_data_bidi_local
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiLocalParameterID))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiLocal))))
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiLocal))
// initial_max_stream_data_bidi_remote
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiRemoteParameterID))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiRemote))))
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiRemote))
// initial_max_stream_data_uni
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataUniParameterID))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataUni))))
utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataUni))
// initial_max_data
utils.BigEndian.WriteUint16(b, uint16(initialMaxDataParameterID))
utils.BigEndian.WriteUint16(b, 4)
utils.BigEndian.WriteUint32(b, uint32(p.ConnectionFlowControlWindow))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxData))))
utils.WriteVarInt(b, uint64(p.InitialMaxData))
// initial_max_bidi_streams
utils.BigEndian.WriteUint16(b, uint16(initialMaxBidiStreamsParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, p.MaxBidiStreams)
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsBidiParameterID))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxBidiStreams)))
utils.WriteVarInt(b, p.MaxBidiStreams)
// initial_max_uni_streams
utils.BigEndian.WriteUint16(b, uint16(initialMaxUniStreamsParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, p.MaxUniStreams)
utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsUniParameterID))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxUniStreams)))
utils.WriteVarInt(b, p.MaxUniStreams)
// idle_timeout
idleTimeout := uint64(p.IdleTimeout / time.Millisecond)
utils.BigEndian.WriteUint16(b, uint16(idleTimeoutParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, uint16(p.IdleTimeout/time.Second))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(idleTimeout)))
utils.WriteVarInt(b, idleTimeout)
// max_packet_size
utils.BigEndian.WriteUint16(b, uint16(maxPacketSizeParameterID))
utils.BigEndian.WriteUint16(b, 2)
utils.BigEndian.WriteUint16(b, uint16(protocol.MaxReceivePacketSize))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(protocol.MaxReceivePacketSize))))
utils.WriteVarInt(b, uint64(protocol.MaxReceivePacketSize))
// ack_delay_exponent
// Only send it if is different from the default value.
if p.AckDelayExponent != protocol.DefaultAckDelayExponent {
utils.BigEndian.WriteUint16(b, uint16(ackDelayExponentParameterID))
utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.AckDelayExponent))))
utils.WriteVarInt(b, uint64(p.AckDelayExponent))
}
// disable_migration
if p.DisableMigration {
utils.BigEndian.WriteUint16(b, uint16(disableMigrationParameterID))
utils.BigEndian.WriteUint16(b, 0)
}
if len(p.StatelessResetToken) > 0 {
if p.StatelessResetToken != nil {
utils.BigEndian.WriteUint16(b, uint16(statelessResetTokenParameterID))
utils.BigEndian.WriteUint16(b, uint16(len(p.StatelessResetToken))) // should always be 16 bytes
b.Write(p.StatelessResetToken)
utils.BigEndian.WriteUint16(b, 16)
b.Write(p.StatelessResetToken[:])
}
// original_connection_id
if p.OriginalConnectionID.Len() > 0 {
utils.BigEndian.WriteUint16(b, uint16(originalConnectionIDParameterID))
utils.BigEndian.WriteUint16(b, uint16(p.OriginalConnectionID.Len()))
b.Write(p.OriginalConnectionID.Bytes())
}
data := b.Bytes()
binary.BigEndian.PutUint16(data[:2], uint16(b.Len()-2))
return data
}
// String returns a string representation, intended for logging.
// It should only used for IETF QUIC.
func (p *TransportParameters) String() string {
return fmt.Sprintf("&handshake.TransportParameters{StreamFlowControlWindow: %#x, ConnectionFlowControlWindow: %#x, MaxBidiStreams: %d, MaxUniStreams: %d, IdleTimeout: %s}", p.StreamFlowControlWindow, p.ConnectionFlowControlWindow, p.MaxBidiStreams, p.MaxUniStreams, p.IdleTimeout)
logString := "&handshake.TransportParameters{OriginalConnectionID: %s, InitialMaxStreamDataBidiLocal: %#x, InitialMaxStreamDataBidiRemote: %#x, InitialMaxStreamDataUni: %#x, InitialMaxData: %#x, MaxBidiStreams: %d, MaxUniStreams: %d, IdleTimeout: %s, AckDelayExponent: %d"
logParams := []interface{}{p.OriginalConnectionID, p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreams, p.MaxUniStreams, p.IdleTimeout, p.AckDelayExponent}
if p.StatelessResetToken != nil { // the client never sends a stateless reset token
logString += ", StatelessResetToken: %#x"
logParams = append(logParams, *p.StatelessResetToken)
}
logString += "}"
return fmt.Sprintf(logString, logParams...)
}

View File

@@ -0,0 +1,38 @@
package handshake
// This package uses unsafe to convert between:
// * qtls.ConnectionState and tls.ConnectionState
// * qtls.ClientSessionState and tls.ClientSessionState
// We check in init() that this conversion actually is safe.
import (
"crypto/tls"
"reflect"
"github.com/marten-seemann/qtls"
)
func init() {
if !structsEqual(&tls.ConnectionState{}, &qtls.ConnectionState{}) {
panic("qtls.ConnectionState not compatible with tls.ConnectionState")
}
if !structsEqual(&tls.ClientSessionState{}, &qtls.ClientSessionState{}) {
panic("qtls.ClientSessionState not compatible with tls.ClientSessionState")
}
}
func structsEqual(a, b interface{}) bool {
sa := reflect.ValueOf(a).Elem()
sb := reflect.ValueOf(b).Elem()
if sa.NumField() != sb.NumField() {
return false
}
for i := 0; i < sa.NumField(); i++ {
fa := sa.Type().Field(i)
fb := sb.Type().Field(i)
if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
return false
}
}
return true
}

View File

@@ -7,22 +7,22 @@ type EncryptionLevel int
const (
// EncryptionUnspecified is a not specified encryption level
EncryptionUnspecified EncryptionLevel = iota
// EncryptionUnencrypted is not encrypted
EncryptionUnencrypted
// EncryptionSecure is encrypted, but not forward secure
EncryptionSecure
// EncryptionForwardSecure is forward secure
EncryptionForwardSecure
// EncryptionInitial is the Initial encryption level
EncryptionInitial
// EncryptionHandshake is the Handshake encryption level
EncryptionHandshake
// Encryption1RTT is the 1-RTT encryption level
Encryption1RTT
)
func (e EncryptionLevel) String() string {
switch e {
case EncryptionUnencrypted:
return "unencrypted"
case EncryptionSecure:
return "encrypted (not forward-secure)"
case EncryptionForwardSecure:
return "forward-secure"
case EncryptionInitial:
return "Initial"
case EncryptionHandshake:
return "Handshake"
case Encryption1RTT:
return "1-RTT"
}
return "unknown"
}

View File

@@ -1,24 +1,37 @@
package protocol
// InferPacketNumber calculates the packet number based on the received packet number, its length and the last seen packet number
func InferPacketNumber(
// PacketNumberLen is the length of the packet number in bytes
type PacketNumberLen uint8
const (
// PacketNumberLenInvalid is the default value and not a valid length for a packet number
PacketNumberLenInvalid PacketNumberLen = 0
// PacketNumberLen1 is a packet number length of 1 byte
PacketNumberLen1 PacketNumberLen = 1
// PacketNumberLen2 is a packet number length of 2 bytes
PacketNumberLen2 PacketNumberLen = 2
// PacketNumberLen3 is a packet number length of 3 bytes
PacketNumberLen3 PacketNumberLen = 3
// PacketNumberLen4 is a packet number length of 4 bytes
PacketNumberLen4 PacketNumberLen = 4
)
// DecodePacketNumber calculates the packet number based on the received packet number, its length and the last seen packet number
func DecodePacketNumber(
packetNumberLength PacketNumberLen,
lastPacketNumber PacketNumber,
wirePacketNumber PacketNumber,
version VersionNumber,
) PacketNumber {
var epochDelta PacketNumber
if version.UsesVarintPacketNumbers() {
switch packetNumberLength {
case PacketNumberLen1:
epochDelta = PacketNumber(1) << 7
case PacketNumberLen2:
epochDelta = PacketNumber(1) << 14
case PacketNumberLen4:
epochDelta = PacketNumber(1) << 30
}
} else {
epochDelta = PacketNumber(1) << (uint8(packetNumberLength) * 8)
switch packetNumberLength {
case PacketNumberLen1:
epochDelta = PacketNumber(1) << 8
case PacketNumberLen2:
epochDelta = PacketNumber(1) << 16
case PacketNumberLen3:
epochDelta = PacketNumber(1) << 24
case PacketNumberLen4:
epochDelta = PacketNumber(1) << 32
}
epoch := lastPacketNumber & ^(epochDelta - 1)
prevEpochBegin := epoch - epochDelta
@@ -46,12 +59,14 @@ func delta(a, b PacketNumber) PacketNumber {
// GetPacketNumberLengthForHeader gets the length of the packet number for the public header
// it never chooses a PacketNumberLen of 1 byte, since this is too short under certain circumstances
func GetPacketNumberLengthForHeader(packetNumber, leastUnacked PacketNumber, version VersionNumber) PacketNumberLen {
func GetPacketNumberLengthForHeader(packetNumber, leastUnacked PacketNumber) PacketNumberLen {
diff := uint64(packetNumber - leastUnacked)
if version.UsesVarintPacketNumbers() && diff < (1<<(14-1)) ||
!version.UsesVarintPacketNumbers() && diff < (1<<(16-1)) {
if diff < (1 << (16 - 1)) {
return PacketNumberLen2
}
if diff < (1 << (24 - 1)) {
return PacketNumberLen3
}
return PacketNumberLen4
}
@@ -63,8 +78,8 @@ func GetPacketNumberLength(packetNumber PacketNumber) PacketNumberLen {
if packetNumber < (1 << (uint8(PacketNumberLen2) * 8)) {
return PacketNumberLen2
}
if packetNumber < (1 << (uint8(PacketNumberLen4) * 8)) {
return PacketNumberLen4
if packetNumber < (1 << (uint8(PacketNumberLen3) * 8)) {
return PacketNumberLen3
}
return PacketNumberLen6
return PacketNumberLen4
}

View File

@@ -8,10 +8,6 @@ const MaxPacketSizeIPv4 = 1252
// MaxPacketSizeIPv6 is the maximum packet size that we use for sending IPv6 packets.
const MaxPacketSizeIPv6 = 1232
// NonForwardSecurePacketSizeReduction is the number of bytes a non forward-secure packet has to be smaller than a forward-secure packet
// This makes sure that those packets can always be retransmitted without splitting the contained StreamFrames
const NonForwardSecurePacketSizeReduction = 50
const defaultMaxCongestionWindowPackets = 1000
// DefaultMaxCongestionWindow is the default for the max congestion window
@@ -20,42 +16,25 @@ const DefaultMaxCongestionWindow ByteCount = defaultMaxCongestionWindowPackets *
// InitialCongestionWindow is the initial congestion window in QUIC packets
const InitialCongestionWindow ByteCount = 32 * DefaultTCPMSS
// MaxUndecryptablePackets limits the number of undecryptable packets that a
// session queues for later until it sends a public reset.
// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the session.
const MaxUndecryptablePackets = 10
// PublicResetTimeout is the time to wait before sending a Public Reset when receiving too many undecryptable packets during the handshake
// This timeout allows the Go scheduler to switch to the Go rountine that reads the crypto stream and to escalate the crypto
const PublicResetTimeout = 500 * time.Millisecond
// ReceiveStreamFlowControlWindow is the stream-level flow control window for receiving data
// This is the value that Google servers are using
const ReceiveStreamFlowControlWindow = (1 << 10) * 32 // 32 kB
// ReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data
// This is the value that Google servers are using
const ReceiveConnectionFlowControlWindow = (1 << 10) * 48 // 48 kB
// DefaultMaxReceiveStreamFlowControlWindowServer is the default maximum stream-level flow control window for receiving data, for the server
// This is the value that Google servers are using
const DefaultMaxReceiveStreamFlowControlWindowServer = 1 * (1 << 20) // 1 MB
// DefaultMaxReceiveConnectionFlowControlWindowServer is the default connection-level flow control window for receiving data, for the server
// This is the value that Google servers are using
const DefaultMaxReceiveConnectionFlowControlWindowServer = 1.5 * (1 << 20) // 1.5 MB
// DefaultMaxReceiveStreamFlowControlWindowClient is the default maximum stream-level flow control window for receiving data, for the client
// This is the value that Chromium is using
const DefaultMaxReceiveStreamFlowControlWindowClient = 6 * (1 << 20) // 6 MB
// DefaultMaxReceiveConnectionFlowControlWindowClient is the default connection-level flow control window for receiving data, for the client
// This is the value that Google servers are using
const DefaultMaxReceiveConnectionFlowControlWindowClient = 15 * (1 << 20) // 15 MB
// ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window
// This is the value that Chromium is using
const ConnectionFlowControlMultiplier = 1.5
// InitialMaxStreamData is the stream-level flow control window for receiving data
const InitialMaxStreamData = (1 << 10) * 512 // 512 kb
// InitialMaxData is the connection-level flow control window for receiving data
const InitialMaxData = ConnectionFlowControlMultiplier * InitialMaxStreamData
// DefaultMaxReceiveStreamFlowControlWindow is the default maximum stream-level flow control window for receiving data, for the server
const DefaultMaxReceiveStreamFlowControlWindow = 6 * (1 << 20) // 6 MB
// DefaultMaxReceiveConnectionFlowControlWindow is the default connection-level flow control window for receiving data, for the server
const DefaultMaxReceiveConnectionFlowControlWindow = 15 * (1 << 20) // 12 MB
// WindowUpdateThreshold is the fraction of the receive window that has to be consumed before an higher offset is advertised to the client
const WindowUpdateThreshold = 0.25
@@ -65,12 +44,6 @@ const DefaultMaxIncomingStreams = 100
// DefaultMaxIncomingUniStreams is the maximum number of unidirectional streams that a peer may open
const DefaultMaxIncomingUniStreams = 100
// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this procentual increase and the absolute increment specified by MaxStreamsMinimumIncrement is used.
const MaxStreamsMultiplier = 1.1
// MaxStreamsMinimumIncrement is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this absolute increment and the procentual increase specified by MaxStreamsMultiplier is used.
const MaxStreamsMinimumIncrement = 10
// MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed.
const MaxSessionUnprocessedPackets = defaultMaxCongestionWindowPackets
@@ -80,6 +53,10 @@ const SkipPacketAveragePeriodLength PacketNumber = 500
// MaxTrackedSkippedPackets is the maximum number of skipped packet numbers the SentPacketHandler keep track of for Optimistic ACK attack mitigation
const MaxTrackedSkippedPackets = 10
// MaxAcceptQueueSize is the maximum number of sessions that the server queues for accepting.
// If the queue is full, new connection attempts will be rejected.
const MaxAcceptQueueSize = 32
// CookieExpiryTime is the valid time of a cookie
const CookieExpiryTime = 24 * time.Hour
@@ -103,15 +80,9 @@ const MaxNonRetransmittableAcks = 19
// prevents DoS attacks against the streamFrameSorter
const MaxStreamFrameSorterGaps = 1000
// CryptoMaxParams is the upper limit for the number of parameters in a crypto message.
// Value taken from Chrome.
const CryptoMaxParams = 128
// CryptoParameterMaxLength is the upper limit for the length of a parameter in a crypto message.
const CryptoParameterMaxLength = 4000
// EphermalKeyLifetime is the lifetime of the ephermal key during the handshake, see handshake.getEphermalKEX.
const EphermalKeyLifetime = time.Minute
// MaxCryptoStreamOffset is the maximum offset allowed on any of the crypto streams.
// This limits the size of the ClientHello and Certificates that can be received.
const MaxCryptoStreamOffset = 16 * (1 << 10)
// MinRemoteIdleTimeout is the minimum value that we accept for the remote idle timeout
const MinRemoteIdleTimeout = 5 * time.Second
@@ -122,12 +93,9 @@ const DefaultIdleTimeout = 30 * time.Second
// DefaultHandshakeTimeout is the default timeout for a connection until the crypto handshake succeeds.
const DefaultHandshakeTimeout = 10 * time.Second
// ClosedSessionDeleteTimeout the server ignores packets arriving on a connection that is already closed
// RetiredConnectionIDDeleteTimeout is the time we keep closed sessions around in order to retransmit the CONNECTION_CLOSE.
// after this time all information about the old connection will be deleted
const ClosedSessionDeleteTimeout = time.Minute
// NumCachedCertificates is the number of cached compressed certificate chains, each taking ~1K space
const NumCachedCertificates = 128
const RetiredConnectionIDDeleteTimeout = 5 * time.Second
// MinStreamFrameSize is the minimum size that has to be left in a packet, so that we add another STREAM frame.
// This avoids splitting up STREAM frames into small pieces, which has 2 advantages:
@@ -135,7 +103,11 @@ const NumCachedCertificates = 128
// 2. it reduces the head-of-line blocking, when a packet is lost
const MinStreamFrameSize ByteCount = 128
// MaxAckFrameSize is the maximum size for an (IETF QUIC) ACK frame that we write
// MaxPostHandshakeCryptoFrameSize is the maximum size of CRYPTO frames
// we send after the handshake completes.
const MaxPostHandshakeCryptoFrameSize ByteCount = 1000
// MaxAckFrameSize is the maximum size for an ACK frame that we write
// Due to the varint encoding, ACK frames can grow (almost) indefinitely large.
// The MaxAckFrameSize should be large enough to encode many ACK range,
// but must ensure that a maximum size ACK frame fits into one packet.
@@ -149,3 +121,6 @@ const MinPacingDelay time.Duration = 100 * time.Microsecond
// DefaultConnectionIDLength is the connection ID length that is used for multiplexed connections
// if no other value is configured.
const DefaultConnectionIDLength = 4
// AckDelayExponent is the ack delay exponent used when sending ACKs.
const AckDelayExponent = 3

View File

@@ -7,34 +7,18 @@ import (
// A PacketNumber in QUIC
type PacketNumber uint64
// PacketNumberLen is the length of the packet number in bytes
type PacketNumberLen uint8
const (
// PacketNumberLenInvalid is the default value and not a valid length for a packet number
PacketNumberLenInvalid PacketNumberLen = 0
// PacketNumberLen1 is a packet number length of 1 byte
PacketNumberLen1 PacketNumberLen = 1
// PacketNumberLen2 is a packet number length of 2 bytes
PacketNumberLen2 PacketNumberLen = 2
// PacketNumberLen4 is a packet number length of 4 bytes
PacketNumberLen4 PacketNumberLen = 4
// PacketNumberLen6 is a packet number length of 6 bytes
PacketNumberLen6 PacketNumberLen = 6
)
// The PacketType is the Long Header Type (only used for the IETF draft header format)
// The PacketType is the Long Header Type
type PacketType uint8
const (
// PacketTypeInitial is the packet type of an Initial packet
PacketTypeInitial PacketType = 0x7f
PacketTypeInitial PacketType = 1 + iota
// PacketTypeRetry is the packet type of a Retry packet
PacketTypeRetry PacketType = 0x7e
PacketTypeRetry
// PacketTypeHandshake is the packet type of a Handshake packet
PacketTypeHandshake PacketType = 0x7d
PacketTypeHandshake
// PacketType0RTT is the packet type of a 0-RTT packet
PacketType0RTT PacketType = 0x7c
PacketType0RTT
)
func (t PacketType) String() string {
@@ -71,20 +55,21 @@ const MaxReceivePacketSize ByteCount = 1452
// Used in QUIC for congestion window computations in bytes.
const DefaultTCPMSS ByteCount = 1460
// MinClientHelloSize is the minimum size the server expects an inchoate CHLO to have (in gQUIC)
const MinClientHelloSize = 1024
// MinInitialPacketSize is the minimum size an Initial packet (in IETF QUIC) is required to have.
// MinInitialPacketSize is the minimum size an Initial packet is required to have.
const MinInitialPacketSize = 1200
// MaxClientHellos is the maximum number of times we'll send a client hello
// The value 3 accounts for:
// * one failure due to an incorrect or missing source-address token
// * one failure due the server's certificate chain being unavailable and the server being unwilling to send it without a valid source-address token
const MaxClientHellos = 3
// ConnectionIDLenGQUIC is the length of the source Connection ID used on gQUIC QUIC packets.
const ConnectionIDLenGQUIC = 8
// MinStatelessResetSize is the minimum size of a stateless reset packet
const MinStatelessResetSize = 1 /* first byte */ + 22 /* random bytes */ + 16 /* token */
// MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet.
const MinConnectionIDLenInitial = 8
// MaxStreamCount is the maximum stream count value that can be sent in MAX_STREAMS frames
// and as the stream count in the transport parameters
const MaxStreamCount = 1 << 60
// DefaultAckDelayExponent is the default ack delay exponent
const DefaultAckDelayExponent = 3
// MaxAckDelayExponent is the maximum ack delay exponent
const MaxAckDelayExponent = 20

View File

@@ -3,34 +3,65 @@ package protocol
// A StreamID in QUIC
type StreamID uint64
// MaxBidiStreamID is the highest stream ID that the peer is allowed to open,
// when it is allowed to open numStreams bidirectional streams.
// It is only valid for IETF QUIC.
func MaxBidiStreamID(numStreams int, pers Perspective) StreamID {
// StreamType encodes if this is a unidirectional or bidirectional stream
type StreamType uint8
const (
// StreamTypeUni is a unidirectional stream
StreamTypeUni StreamType = iota
// StreamTypeBidi is a bidirectional stream
StreamTypeBidi
)
// InitiatedBy says if the stream was initiated by the client or by the server
func (s StreamID) InitiatedBy() Perspective {
if s%2 == 0 {
return PerspectiveClient
}
return PerspectiveServer
}
//Type says if this is a unidirectional or bidirectional stream
func (s StreamID) Type() StreamType {
if s%4 >= 2 {
return StreamTypeUni
}
return StreamTypeBidi
}
// StreamNum returns how many streams in total are below this
// Example: for stream 9 it returns 3 (i.e. streams 1, 5 and 9)
func (s StreamID) StreamNum() uint64 {
return uint64(s/4) + 1
}
// MaxStreamID is the highest stream ID that a peer is allowed to open,
// when it is allowed to open numStreams.
func MaxStreamID(stype StreamType, numStreams uint64, pers Perspective) StreamID {
if numStreams == 0 {
return 0
}
var first StreamID
if pers == PerspectiveClient {
first = 1
} else {
first = 4
switch stype {
case StreamTypeBidi:
switch pers {
case PerspectiveClient:
first = 0
case PerspectiveServer:
first = 1
}
case StreamTypeUni:
switch pers {
case PerspectiveClient:
first = 2
case PerspectiveServer:
first = 3
}
}
return first + 4*StreamID(numStreams-1)
}
// MaxUniStreamID is the highest stream ID that the peer is allowed to open,
// when it is allowed to open numStreams unidirectional streams.
// It is only valid for IETF QUIC.
func MaxUniStreamID(numStreams int, pers Perspective) StreamID {
if numStreams == 0 {
return 0
}
var first StreamID
if pers == PerspectiveClient {
first = 3
} else {
first = 2
}
return first + 4*StreamID(numStreams-1)
// FirstStream returns the first valid stream ID
func FirstStream(stype StreamType, pers Perspective) StreamID {
return MaxStreamID(stype, 1, pers)
}

View File

@@ -18,40 +18,30 @@ const (
// The version numbers, making grepping easier
const (
Version39 VersionNumber = gquicVersion0 + 3*0x100 + 0x9
Version43 VersionNumber = gquicVersion0 + 4*0x100 + 0x3
Version44 VersionNumber = gquicVersion0 + 4*0x100 + 0x4
VersionTLS VersionNumber = 101
VersionWhatever VersionNumber = 0 // for when the version doesn't matter
VersionTLS VersionNumber = VersionMilestone0_11_1
VersionWhatever VersionNumber = 1 // for when the version doesn't matter
VersionUnknown VersionNumber = math.MaxUint32
VersionMilestone0_11_1 VersionNumber = 0xff000013 // QUIC WG draft-19
)
// SupportedVersions lists the versions that the server supports
// must be in sorted descending order
var SupportedVersions = []VersionNumber{
Version44,
Version43,
Version39,
}
var SupportedVersions = []VersionNumber{VersionMilestone0_11_1}
// IsValidVersion says if the version is known to quic-go
func IsValidVersion(v VersionNumber) bool {
return v == VersionTLS || IsSupportedVersion(SupportedVersions, v)
}
// UsesTLS says if this QUIC version uses TLS 1.3 for the handshake
func (vn VersionNumber) UsesTLS() bool {
return !vn.isGQUIC()
}
func (vn VersionNumber) String() string {
switch vn {
case VersionWhatever:
return "whatever"
case VersionUnknown:
return "unknown"
case VersionTLS:
return "TLS dev version (WIP)"
case VersionMilestone0_11_1:
return "QUIC WG draft-19"
default:
if vn.isGQUIC() {
return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())
@@ -62,61 +52,9 @@ func (vn VersionNumber) String() string {
// ToAltSvc returns the representation of the version for the H2 Alt-Svc parameters
func (vn VersionNumber) ToAltSvc() string {
if vn.isGQUIC() {
return fmt.Sprintf("%d", vn.toGQUICVersion())
}
return fmt.Sprintf("%d", vn)
}
// CryptoStreamID gets the Stream ID of the crypto stream
func (vn VersionNumber) CryptoStreamID() StreamID {
if vn.isGQUIC() {
return 1
}
return 0
}
// UsesIETFFrameFormat tells if this version uses the IETF frame format
func (vn VersionNumber) UsesIETFFrameFormat() bool {
return !vn.isGQUIC()
}
// UsesIETFHeaderFormat tells if this version uses the IETF header format
func (vn VersionNumber) UsesIETFHeaderFormat() bool {
return !vn.isGQUIC() || vn >= Version44
}
// UsesLengthInHeader tells if this version uses the Length field in the IETF header
func (vn VersionNumber) UsesLengthInHeader() bool {
return !vn.isGQUIC()
}
// UsesTokenInHeader tells if this version uses the Token field in the IETF header
func (vn VersionNumber) UsesTokenInHeader() bool {
return !vn.isGQUIC()
}
// UsesStopWaitingFrames tells if this version uses STOP_WAITING frames
func (vn VersionNumber) UsesStopWaitingFrames() bool {
return vn.isGQUIC() && vn <= Version43
}
// UsesVarintPacketNumbers tells if this version uses 7/14/30 bit packet numbers
func (vn VersionNumber) UsesVarintPacketNumbers() bool {
return !vn.isGQUIC()
}
// StreamContributesToConnectionFlowControl says if a stream contributes to connection-level flow control
func (vn VersionNumber) StreamContributesToConnectionFlowControl(id StreamID) bool {
if id == vn.CryptoStreamID() {
return false
}
if vn.isGQUIC() && id == 3 {
return false
}
return true
}
func (vn VersionNumber) isGQUIC() bool {
return vn > gquicVersion0 && vn <= maxGquicVersion
}

View File

@@ -0,0 +1,71 @@
package qerr
import (
"fmt"
"github.com/marten-seemann/qtls"
)
// ErrorCode can be used as a normal error without reason.
type ErrorCode uint16
// The error codes defined by QUIC
const (
NoError ErrorCode = 0x0
InternalError ErrorCode = 0x1
ServerBusy ErrorCode = 0x2
FlowControlError ErrorCode = 0x3
StreamLimitError ErrorCode = 0x4
StreamStateError ErrorCode = 0x5
FinalSizeError ErrorCode = 0x6
FrameEncodingError ErrorCode = 0x7
TransportParameterError ErrorCode = 0x8
VersionNegotiationError ErrorCode = 0x9
ProtocolViolation ErrorCode = 0xa
InvalidMigration ErrorCode = 0xc
)
func (e ErrorCode) isCryptoError() bool {
return e >= 0x100 && e < 0x200
}
func (e ErrorCode) Error() string {
if e.isCryptoError() {
return fmt.Sprintf("%s: %s", e.String(), qtls.Alert(e-0x100).Error())
}
return e.String()
}
func (e ErrorCode) String() string {
switch e {
case NoError:
return "NO_ERROR"
case InternalError:
return "INTERNAL_ERROR"
case ServerBusy:
return "SERVER_BUSY"
case FlowControlError:
return "FLOW_CONTROL_ERROR"
case StreamLimitError:
return "STREAM_LIMIT_ERROR"
case StreamStateError:
return "STREAM_STATE_ERROR"
case FinalSizeError:
return "FINAL_SIZE_ERROR"
case FrameEncodingError:
return "FRAME_ENCODING_ERROR"
case TransportParameterError:
return "TRANSPORT_PARAMETER_ERROR"
case VersionNegotiationError:
return "VERSION_NEGOTIATION_ERROR"
case ProtocolViolation:
return "PROTOCOL_VIOLATION"
case InvalidMigration:
return "INVALID_MIGRATION"
default:
if e.isCryptoError() {
return "CRYPTO_ERROR"
}
return fmt.Sprintf("unknown error code: %#x", uint16(e))
}
}

View File

@@ -2,21 +2,18 @@ package qerr
import (
"fmt"
"net"
)
// ErrorCode can be used as a normal error without reason.
type ErrorCode uint32
func (e ErrorCode) Error() string {
return e.String()
}
// A QuicError consists of an error code plus a error reason
type QuicError struct {
ErrorCode ErrorCode
ErrorMessage string
isTimeout bool
}
var _ net.Error = &QuicError{}
// Error creates a new QuicError instance
func Error(errorCode ErrorCode, errorMessage string) *QuicError {
return &QuicError{
@@ -25,19 +22,42 @@ func Error(errorCode ErrorCode, errorMessage string) *QuicError {
}
}
// TimeoutError creates a new QuicError instance for a timeout error
func TimeoutError(errorMessage string) *QuicError {
return &QuicError{
ErrorMessage: errorMessage,
isTimeout: true,
}
}
// CryptoError create a new QuicError instance for a crypto error
func CryptoError(tlsAlert uint8, errorMessage string) *QuicError {
return &QuicError{
ErrorCode: 0x100 + ErrorCode(tlsAlert),
ErrorMessage: errorMessage,
}
}
func (e *QuicError) Error() string {
if len(e.ErrorMessage) == 0 {
return e.ErrorCode.Error()
}
return fmt.Sprintf("%s: %s", e.ErrorCode.String(), e.ErrorMessage)
}
// IsCryptoError says if this error is a crypto error
func (e *QuicError) IsCryptoError() bool {
return e.ErrorCode.isCryptoError()
}
// Temporary says if the error is temporary.
func (e *QuicError) Temporary() bool {
return false
}
// Timeout says if this error is a timeout.
func (e *QuicError) Timeout() bool {
switch e.ErrorCode {
case NetworkIdleTimeout,
HandshakeTimeout,
TimeoutsWithOpenStreams:
return true
}
return false
return e.isTimeout
}
// ToQuicError converts an arbitrary error to a QuicError. It leaves QuicErrors

View File

@@ -8,18 +8,10 @@ import (
// A ByteOrder specifies how to convert byte sequences into 16-, 32-, or 64-bit unsigned integers.
type ByteOrder interface {
ReadUintN(b io.ByteReader, length uint8) (uint64, error)
ReadUint64(io.ByteReader) (uint64, error)
ReadUint32(io.ByteReader) (uint32, error)
ReadUint16(io.ByteReader) (uint16, error)
WriteUint64(*bytes.Buffer, uint64)
WriteUint56(*bytes.Buffer, uint64)
WriteUint48(*bytes.Buffer, uint64)
WriteUint40(*bytes.Buffer, uint64)
WriteUintN(b *bytes.Buffer, length uint8, value uint64)
WriteUint32(*bytes.Buffer, uint32)
WriteUint24(*bytes.Buffer, uint32)
WriteUint16(*bytes.Buffer, uint16)
ReadUfloat16(io.ByteReader) (uint64, error)
WriteUfloat16(*bytes.Buffer, uint64)
}

View File

@@ -2,7 +2,6 @@ package utils
import (
"bytes"
"fmt"
"io"
)
@@ -26,37 +25,6 @@ func (bigEndian) ReadUintN(b io.ByteReader, length uint8) (uint64, error) {
return res, nil
}
// ReadUint64 reads a uint64
func (bigEndian) ReadUint64(b io.ByteReader) (uint64, error) {
var b1, b2, b3, b4, b5, b6, b7, b8 uint8
var err error
if b8, err = b.ReadByte(); err != nil {
return 0, err
}
if b7, err = b.ReadByte(); err != nil {
return 0, err
}
if b6, err = b.ReadByte(); err != nil {
return 0, err
}
if b5, err = b.ReadByte(); err != nil {
return 0, err
}
if b4, err = b.ReadByte(); err != nil {
return 0, err
}
if b3, err = b.ReadByte(); err != nil {
return 0, err
}
if b2, err = b.ReadByte(); err != nil {
return 0, err
}
if b1, err = b.ReadByte(); err != nil {
return 0, err
}
return uint64(b1) + uint64(b2)<<8 + uint64(b3)<<16 + uint64(b4)<<24 + uint64(b5)<<32 + uint64(b6)<<40 + uint64(b7)<<48 + uint64(b8)<<56, nil
}
// ReadUint32 reads a uint32
func (bigEndian) ReadUint32(b io.ByteReader) (uint32, error) {
var b1, b2, b3, b4 uint8
@@ -89,45 +57,10 @@ func (bigEndian) ReadUint16(b io.ByteReader) (uint16, error) {
return uint16(b1) + uint16(b2)<<8, nil
}
// WriteUint64 writes a uint64
func (bigEndian) WriteUint64(b *bytes.Buffer, i uint64) {
b.Write([]byte{
uint8(i >> 56), uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
})
}
// WriteUint56 writes 56 bit of a uint64
func (bigEndian) WriteUint56(b *bytes.Buffer, i uint64) {
if i >= (1 << 56) {
panic(fmt.Sprintf("%#x doesn't fit into 56 bits", i))
func (bigEndian) WriteUintN(b *bytes.Buffer, length uint8, i uint64) {
for j := length; j > 0; j-- {
b.WriteByte(uint8(i >> (8 * (j - 1))))
}
b.Write([]byte{
uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
})
}
// WriteUint48 writes 48 bit of a uint64
func (bigEndian) WriteUint48(b *bytes.Buffer, i uint64) {
if i >= (1 << 48) {
panic(fmt.Sprintf("%#x doesn't fit into 48 bits", i))
}
b.Write([]byte{
uint8(i >> 40), uint8(i >> 32),
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
})
}
// WriteUint40 writes 40 bit of a uint64
func (bigEndian) WriteUint40(b *bytes.Buffer, i uint64) {
if i >= (1 << 40) {
panic(fmt.Sprintf("%#x doesn't fit into 40 bits", i))
}
b.Write([]byte{
uint8(i >> 32),
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
})
}
// WriteUint32 writes a uint32
@@ -135,23 +68,7 @@ func (bigEndian) WriteUint32(b *bytes.Buffer, i uint32) {
b.Write([]byte{uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i)})
}
// WriteUint24 writes 24 bit of a uint32
func (bigEndian) WriteUint24(b *bytes.Buffer, i uint32) {
if i >= (1 << 24) {
panic(fmt.Sprintf("%#x doesn't fit into 24 bits", i))
}
b.Write([]byte{uint8(i >> 16), uint8(i >> 8), uint8(i)})
}
// WriteUint16 writes a uint16
func (bigEndian) WriteUint16(b *bytes.Buffer, i uint16) {
b.Write([]byte{uint8(i >> 8), uint8(i)})
}
func (l bigEndian) ReadUfloat16(b io.ByteReader) (uint64, error) {
return readUfloat16(b, l)
}
func (l bigEndian) WriteUfloat16(b *bytes.Buffer, val uint64) {
writeUfloat16(b, l, val)
}

View File

@@ -1,157 +0,0 @@
package utils
import (
"bytes"
"fmt"
"io"
)
// LittleEndian is the little-endian implementation of ByteOrder.
var LittleEndian ByteOrder = littleEndian{}
type littleEndian struct{}
var _ ByteOrder = &littleEndian{}
// ReadUintN reads N bytes
func (littleEndian) ReadUintN(b io.ByteReader, length uint8) (uint64, error) {
var res uint64
for i := uint8(0); i < length; i++ {
bt, err := b.ReadByte()
if err != nil {
return 0, err
}
res ^= uint64(bt) << (i * 8)
}
return res, nil
}
// ReadUint64 reads a uint64
func (littleEndian) ReadUint64(b io.ByteReader) (uint64, error) {
var b1, b2, b3, b4, b5, b6, b7, b8 uint8
var err error
if b1, err = b.ReadByte(); err != nil {
return 0, err
}
if b2, err = b.ReadByte(); err != nil {
return 0, err
}
if b3, err = b.ReadByte(); err != nil {
return 0, err
}
if b4, err = b.ReadByte(); err != nil {
return 0, err
}
if b5, err = b.ReadByte(); err != nil {
return 0, err
}
if b6, err = b.ReadByte(); err != nil {
return 0, err
}
if b7, err = b.ReadByte(); err != nil {
return 0, err
}
if b8, err = b.ReadByte(); err != nil {
return 0, err
}
return uint64(b1) + uint64(b2)<<8 + uint64(b3)<<16 + uint64(b4)<<24 + uint64(b5)<<32 + uint64(b6)<<40 + uint64(b7)<<48 + uint64(b8)<<56, nil
}
// ReadUint32 reads a uint32
func (littleEndian) ReadUint32(b io.ByteReader) (uint32, error) {
var b1, b2, b3, b4 uint8
var err error
if b1, err = b.ReadByte(); err != nil {
return 0, err
}
if b2, err = b.ReadByte(); err != nil {
return 0, err
}
if b3, err = b.ReadByte(); err != nil {
return 0, err
}
if b4, err = b.ReadByte(); err != nil {
return 0, err
}
return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16 + uint32(b4)<<24, nil
}
// ReadUint16 reads a uint16
func (littleEndian) ReadUint16(b io.ByteReader) (uint16, error) {
var b1, b2 uint8
var err error
if b1, err = b.ReadByte(); err != nil {
return 0, err
}
if b2, err = b.ReadByte(); err != nil {
return 0, err
}
return uint16(b1) + uint16(b2)<<8, nil
}
// WriteUint64 writes a uint64
func (littleEndian) WriteUint64(b *bytes.Buffer, i uint64) {
b.Write([]byte{
uint8(i), uint8(i >> 8), uint8(i >> 16), uint8(i >> 24),
uint8(i >> 32), uint8(i >> 40), uint8(i >> 48), uint8(i >> 56),
})
}
// WriteUint56 writes 56 bit of a uint64
func (littleEndian) WriteUint56(b *bytes.Buffer, i uint64) {
if i >= (1 << 56) {
panic(fmt.Sprintf("%#x doesn't fit into 56 bits", i))
}
b.Write([]byte{
uint8(i), uint8(i >> 8), uint8(i >> 16), uint8(i >> 24),
uint8(i >> 32), uint8(i >> 40), uint8(i >> 48),
})
}
// WriteUint48 writes 48 bit of a uint64
func (littleEndian) WriteUint48(b *bytes.Buffer, i uint64) {
if i >= (1 << 48) {
panic(fmt.Sprintf("%#x doesn't fit into 48 bits", i))
}
b.Write([]byte{
uint8(i), uint8(i >> 8), uint8(i >> 16), uint8(i >> 24),
uint8(i >> 32), uint8(i >> 40),
})
}
// WriteUint40 writes 40 bit of a uint64
func (littleEndian) WriteUint40(b *bytes.Buffer, i uint64) {
if i >= (1 << 40) {
panic(fmt.Sprintf("%#x doesn't fit into 40 bits", i))
}
b.Write([]byte{
uint8(i), uint8(i >> 8), uint8(i >> 16),
uint8(i >> 24), uint8(i >> 32),
})
}
// WriteUint32 writes a uint32
func (littleEndian) WriteUint32(b *bytes.Buffer, i uint32) {
b.Write([]byte{uint8(i), uint8(i >> 8), uint8(i >> 16), uint8(i >> 24)})
}
// WriteUint24 writes 24 bit of a uint32
func (littleEndian) WriteUint24(b *bytes.Buffer, i uint32) {
if i >= (1 << 24) {
panic(fmt.Sprintf("%#x doesn't fit into 24 bits", i))
}
b.Write([]byte{uint8(i), uint8(i >> 8), uint8(i >> 16)})
}
// WriteUint16 writes a uint16
func (littleEndian) WriteUint16(b *bytes.Buffer, i uint16) {
b.Write([]byte{uint8(i), uint8(i >> 8)})
}
func (l littleEndian) ReadUfloat16(b io.ByteReader) (uint64, error) {
return readUfloat16(b, l)
}
func (l littleEndian) WriteUfloat16(b *bytes.Buffer, val uint64) {
writeUfloat16(b, l, val)
}

View File

@@ -1,86 +0,0 @@
package utils
import (
"bytes"
"io"
"math"
)
// We define an unsigned 16-bit floating point value, inspired by IEEE floats
// (http://en.wikipedia.org/wiki/Half_precision_floating-point_format),
// with 5-bit exponent (bias 1), 11-bit mantissa (effective 12 with hidden
// bit) and denormals, but without signs, transfinites or fractions. Wire format
// 16 bits (little-endian byte order) are split into exponent (high 5) and
// mantissa (low 11) and decoded as:
// uint64_t value;
// if (exponent == 0) value = mantissa;
// else value = (mantissa | 1 << 11) << (exponent - 1)
const uFloat16ExponentBits = 5
const uFloat16MaxExponent = (1 << uFloat16ExponentBits) - 2 // 30
const uFloat16MantissaBits = 16 - uFloat16ExponentBits // 11
const uFloat16MantissaEffectiveBits = uFloat16MantissaBits + 1 // 12
const uFloat16MaxValue = ((uint64(1) << uFloat16MantissaEffectiveBits) - 1) << uFloat16MaxExponent // 0x3FFC0000000
// readUfloat16 reads a float in the QUIC-float16 format and returns its uint64 representation
func readUfloat16(b io.ByteReader, byteOrder ByteOrder) (uint64, error) {
val, err := byteOrder.ReadUint16(b)
if err != nil {
return 0, err
}
res := uint64(val)
if res < (1 << uFloat16MantissaEffectiveBits) {
// Fast path: either the value is denormalized (no hidden bit), or
// normalized (hidden bit set, exponent offset by one) with exponent zero.
// Zero exponent offset by one sets the bit exactly where the hidden bit is.
// So in both cases the value encodes itself.
return res, nil
}
exponent := val >> uFloat16MantissaBits // No sign extend on uint!
// After the fast pass, the exponent is at least one (offset by one).
// Un-offset the exponent.
exponent--
// Here we need to clear the exponent and set the hidden bit. We have already
// decremented the exponent, so when we subtract it, it leaves behind the
// hidden bit.
res -= uint64(exponent) << uFloat16MantissaBits
res <<= exponent
return res, nil
}
// writeUfloat16 writes a float in the QUIC-float16 format from its uint64 representation
func writeUfloat16(b *bytes.Buffer, byteOrder ByteOrder, value uint64) {
var result uint16
if value < (uint64(1) << uFloat16MantissaEffectiveBits) {
// Fast path: either the value is denormalized, or has exponent zero.
// Both cases are represented by the value itself.
result = uint16(value)
} else if value >= uFloat16MaxValue {
// Value is out of range; clamp it to the maximum representable.
result = math.MaxUint16
} else {
// The highest bit is between position 13 and 42 (zero-based), which
// corresponds to exponent 1-30. In the output, mantissa is from 0 to 10,
// hidden bit is 11 and exponent is 11 to 15. Shift the highest bit to 11
// and count the shifts.
exponent := uint16(0)
for offset := uint16(16); offset > 0; offset /= 2 {
// Right-shift the value until the highest bit is in position 11.
// For offset of 16, 8, 4, 2 and 1 (binary search over 1-30),
// shift if the bit is at or above 11 + offset.
if value >= (uint64(1) << (uFloat16MantissaBits + offset)) {
exponent += offset
value >>= offset
}
}
// Hidden bit (position 11) is set. We should remove it and increment the
// exponent. Equivalently, we just add it to the exponent.
// This hides the bit.
result = (uint16(value) + (exponent << uFloat16MantissaBits))
}
byteOrder.WriteUint16(b, result)
}

View File

@@ -122,6 +122,18 @@ func MinTime(a, b time.Time) time.Time {
return a
}
// MinNonZeroTime returns the earlist time that is not time.Time{}
// If both a and b are time.Time{}, it returns time.Time{}
func MinNonZeroTime(a, b time.Time) time.Time {
if a.IsZero() {
return b
}
if b.IsZero() {
return a
}
return MinTime(a, b)
}
// MaxTime returns the later time
func MaxTime(a, b time.Time) time.Time {
if a.After(b) {

View File

@@ -1,50 +0,0 @@
package utils
import (
"bytes"
"fmt"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
// ReadVarIntPacketNumber reads a number in the QUIC varint packet number format
func ReadVarIntPacketNumber(b *bytes.Reader) (protocol.PacketNumber, protocol.PacketNumberLen, error) {
b1, err := b.ReadByte()
if err != nil {
return 0, 0, err
}
if b1&0x80 == 0 {
return protocol.PacketNumber(b1), protocol.PacketNumberLen1, nil
}
b2, err := b.ReadByte()
if err != nil {
return 0, 0, err
}
if b1&0x40 == 0 {
return protocol.PacketNumber(uint64(b1&0x3f)<<8 + uint64(b2)), protocol.PacketNumberLen2, nil
}
b3, err := b.ReadByte()
if err != nil {
return 0, 0, err
}
b4, err := b.ReadByte()
if err != nil {
return 0, 0, err
}
return protocol.PacketNumber(uint64(b1&0x3f)<<24 + uint64(b2)<<16 + uint64(b3)<<8 + uint64(b4)), protocol.PacketNumberLen4, nil
}
// WriteVarIntPacketNumber writes a packet number in the QUIC varint packet number format
func WriteVarIntPacketNumber(b *bytes.Buffer, i protocol.PacketNumber, len protocol.PacketNumberLen) error {
switch len {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(i & 0x7f))
case protocol.PacketNumberLen2:
b.Write([]byte{(uint8(i>>8) & 0x3f) | 0x80, uint8(i)})
case protocol.PacketNumberLen4:
b.Write([]byte{(uint8(i>>24) & 0x3f) | 0xc0, uint8(i >> 16), uint8(i >> 8), uint8(i)})
default:
return fmt.Errorf("invalid packet number length: %d", len)
}
return nil
}

View File

@@ -10,8 +10,7 @@ import (
"github.com/lucas-clemente/quic-go/internal/utils"
)
// TODO: use the value sent in the transport parameters
const ackDelayExponent = 3
var errInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges")
// An AckFrame is an ACK frame
type AckFrame struct {
@@ -20,11 +19,7 @@ type AckFrame struct {
}
// parseAckFrame reads an ACK frame
func parseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame, error) {
if !version.UsesIETFFrameFormat() {
return parseAckFrameLegacy(r, version)
}
func parseAckFrame(r *bytes.Reader, ackDelayExponent uint8, version protocol.VersionNumber) (*AckFrame, error) {
typeByte, err := r.ReadByte()
if err != nil {
return nil, err
@@ -104,11 +99,7 @@ func parseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame,
// Write writes an ACK frame.
func (f *AckFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
if !version.UsesIETFFrameFormat() {
return f.writeLegacy(b, version)
}
b.WriteByte(0x1a)
b.WriteByte(0x2)
utils.WriteVarInt(b, uint64(f.LargestAcked()))
utils.WriteVarInt(b, encodeAckDelay(f.DelayTime))
@@ -130,10 +121,6 @@ func (f *AckFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error
// Length of a written frame
func (f *AckFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
if !version.UsesIETFFrameFormat() {
return f.lengthLegacy(version)
}
largestAcked := f.AckRanges[0].Largest
numRanges := f.numEncodableAckRanges()
@@ -235,5 +222,5 @@ func (f *AckFrame) AcksPacket(p protocol.PacketNumber) bool {
}
func encodeAckDelay(delay time.Duration) uint64 {
return uint64(delay.Nanoseconds() / (1000 * (1 << ackDelayExponent)))
return uint64(delay.Nanoseconds() / (1000 * (1 << protocol.AckDelayExponent)))
}

View File

@@ -1,364 +0,0 @@
package wire
import (
"bytes"
"errors"
"time"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
var errInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges")
func parseAckFrameLegacy(r *bytes.Reader, _ protocol.VersionNumber) (*AckFrame, error) {
frame := &AckFrame{}
typeByte, err := r.ReadByte()
if err != nil {
return nil, err
}
hasMissingRanges := typeByte&0x20 == 0x20
largestAckedLen := 2 * ((typeByte & 0x0C) >> 2)
if largestAckedLen == 0 {
largestAckedLen = 1
}
missingSequenceNumberDeltaLen := 2 * (typeByte & 0x03)
if missingSequenceNumberDeltaLen == 0 {
missingSequenceNumberDeltaLen = 1
}
la, err := utils.BigEndian.ReadUintN(r, largestAckedLen)
if err != nil {
return nil, err
}
largestAcked := protocol.PacketNumber(la)
delay, err := utils.BigEndian.ReadUfloat16(r)
if err != nil {
return nil, err
}
frame.DelayTime = time.Duration(delay) * time.Microsecond
var numAckBlocks uint8
if hasMissingRanges {
numAckBlocks, err = r.ReadByte()
if err != nil {
return nil, err
}
}
if hasMissingRanges && numAckBlocks == 0 {
return nil, errInvalidAckRanges
}
abl, err := utils.BigEndian.ReadUintN(r, missingSequenceNumberDeltaLen)
if err != nil {
return nil, err
}
ackBlockLength := protocol.PacketNumber(abl)
if largestAcked > 0 && ackBlockLength < 1 {
return nil, errors.New("invalid first ACK range")
}
if ackBlockLength > largestAcked+1 {
return nil, errInvalidAckRanges
}
if hasMissingRanges {
ackRange := AckRange{
Smallest: largestAcked - ackBlockLength + 1,
Largest: largestAcked,
}
frame.AckRanges = append(frame.AckRanges, ackRange)
var inLongBlock bool
var lastRangeComplete bool
for i := uint8(0); i < numAckBlocks; i++ {
var gap uint8
gap, err = r.ReadByte()
if err != nil {
return nil, err
}
abl, err := utils.BigEndian.ReadUintN(r, missingSequenceNumberDeltaLen)
if err != nil {
return nil, err
}
ackBlockLength := protocol.PacketNumber(abl)
if inLongBlock {
frame.AckRanges[len(frame.AckRanges)-1].Smallest -= protocol.PacketNumber(gap) + ackBlockLength
frame.AckRanges[len(frame.AckRanges)-1].Largest -= protocol.PacketNumber(gap)
} else {
lastRangeComplete = false
ackRange := AckRange{
Largest: frame.AckRanges[len(frame.AckRanges)-1].Smallest - protocol.PacketNumber(gap) - 1,
}
ackRange.Smallest = ackRange.Largest - ackBlockLength + 1
frame.AckRanges = append(frame.AckRanges, ackRange)
}
if ackBlockLength > 0 {
lastRangeComplete = true
}
inLongBlock = (ackBlockLength == 0)
}
// if the last range was not complete, First and Last make no sense
// remove the range from frame.AckRanges
if !lastRangeComplete {
frame.AckRanges = frame.AckRanges[:len(frame.AckRanges)-1]
}
} else {
frame.AckRanges = make([]AckRange, 1)
if largestAcked != 0 {
frame.AckRanges[0].Largest = largestAcked
frame.AckRanges[0].Smallest = largestAcked + 1 - ackBlockLength
}
}
if !frame.validateAckRanges() {
return nil, errInvalidAckRanges
}
var numTimestamp byte
numTimestamp, err = r.ReadByte()
if err != nil {
return nil, err
}
if numTimestamp > 0 {
// Delta Largest acked
_, err = r.ReadByte()
if err != nil {
return nil, err
}
// First Timestamp
_, err = utils.BigEndian.ReadUint32(r)
if err != nil {
return nil, err
}
for i := 0; i < int(numTimestamp)-1; i++ {
// Delta Largest acked
_, err = r.ReadByte()
if err != nil {
return nil, err
}
// Time Since Previous Timestamp
_, err = utils.BigEndian.ReadUint16(r)
if err != nil {
return nil, err
}
}
}
return frame, nil
}
func (f *AckFrame) writeLegacy(b *bytes.Buffer, _ protocol.VersionNumber) error {
largestAcked := f.LargestAcked()
largestAckedLen := protocol.GetPacketNumberLength(largestAcked)
typeByte := uint8(0x40)
if largestAckedLen != protocol.PacketNumberLen1 {
typeByte ^= (uint8(largestAckedLen / 2)) << 2
}
missingSequenceNumberDeltaLen := f.getMissingSequenceNumberDeltaLen()
if missingSequenceNumberDeltaLen != protocol.PacketNumberLen1 {
typeByte ^= (uint8(missingSequenceNumberDeltaLen / 2))
}
if f.HasMissingRanges() {
typeByte |= 0x20
}
b.WriteByte(typeByte)
switch largestAckedLen {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(largestAcked))
case protocol.PacketNumberLen2:
utils.BigEndian.WriteUint16(b, uint16(largestAcked))
case protocol.PacketNumberLen4:
utils.BigEndian.WriteUint32(b, uint32(largestAcked))
case protocol.PacketNumberLen6:
utils.BigEndian.WriteUint48(b, uint64(largestAcked)&(1<<48-1))
}
utils.BigEndian.WriteUfloat16(b, uint64(f.DelayTime/time.Microsecond))
var numRanges uint64
var numRangesWritten uint64
if f.HasMissingRanges() {
numRanges = f.numWritableNackRanges()
if numRanges > 0xFF {
panic("AckFrame: Too many ACK ranges")
}
b.WriteByte(uint8(numRanges - 1))
}
var firstAckBlockLength protocol.PacketNumber
if !f.HasMissingRanges() {
firstAckBlockLength = largestAcked - f.LowestAcked() + 1
} else {
firstAckBlockLength = largestAcked - f.AckRanges[0].Smallest + 1
numRangesWritten++
}
switch missingSequenceNumberDeltaLen {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(firstAckBlockLength))
case protocol.PacketNumberLen2:
utils.BigEndian.WriteUint16(b, uint16(firstAckBlockLength))
case protocol.PacketNumberLen4:
utils.BigEndian.WriteUint32(b, uint32(firstAckBlockLength))
case protocol.PacketNumberLen6:
utils.BigEndian.WriteUint48(b, uint64(firstAckBlockLength)&(1<<48-1))
}
for i, ackRange := range f.AckRanges {
if i == 0 {
continue
}
length := ackRange.Largest - ackRange.Smallest + 1
gap := f.AckRanges[i-1].Smallest - ackRange.Largest - 1
num := gap/0xFF + 1
if gap%0xFF == 0 {
num--
}
if num == 1 {
b.WriteByte(uint8(gap))
switch missingSequenceNumberDeltaLen {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(length))
case protocol.PacketNumberLen2:
utils.BigEndian.WriteUint16(b, uint16(length))
case protocol.PacketNumberLen4:
utils.BigEndian.WriteUint32(b, uint32(length))
case protocol.PacketNumberLen6:
utils.BigEndian.WriteUint48(b, uint64(length)&(1<<48-1))
}
numRangesWritten++
} else {
for i := 0; i < int(num); i++ {
var lengthWritten uint64
var gapWritten uint8
if i == int(num)-1 { // last block
lengthWritten = uint64(length)
gapWritten = uint8(1 + ((gap - 1) % 255))
} else {
lengthWritten = 0
gapWritten = 0xFF
}
b.WriteByte(gapWritten)
switch missingSequenceNumberDeltaLen {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(lengthWritten))
case protocol.PacketNumberLen2:
utils.BigEndian.WriteUint16(b, uint16(lengthWritten))
case protocol.PacketNumberLen4:
utils.BigEndian.WriteUint32(b, uint32(lengthWritten))
case protocol.PacketNumberLen6:
utils.BigEndian.WriteUint48(b, lengthWritten&(1<<48-1))
}
numRangesWritten++
}
}
// this is needed if not all AckRanges can be written to the ACK frame (if there are more than 0xFF)
if numRangesWritten >= numRanges {
break
}
}
if numRanges != numRangesWritten {
return errors.New("BUG: Inconsistent number of ACK ranges written")
}
b.WriteByte(0) // no timestamps
return nil
}
func (f *AckFrame) lengthLegacy(_ protocol.VersionNumber) protocol.ByteCount {
length := protocol.ByteCount(1 + 2 + 1) // 1 TypeByte, 2 ACK delay time, 1 Num Timestamp
length += protocol.ByteCount(protocol.GetPacketNumberLength(f.LargestAcked()))
missingSequenceNumberDeltaLen := protocol.ByteCount(f.getMissingSequenceNumberDeltaLen())
if f.HasMissingRanges() {
length += (1 + missingSequenceNumberDeltaLen) * protocol.ByteCount(f.numWritableNackRanges())
} else {
length += missingSequenceNumberDeltaLen
}
// we don't write
return length
}
// numWritableNackRanges calculates the number of ACK blocks that are about to be written
// this number is different from len(f.AckRanges) for the case of long gaps (> 255 packets)
func (f *AckFrame) numWritableNackRanges() uint64 {
if len(f.AckRanges) == 0 {
return 0
}
var numRanges uint64
for i, ackRange := range f.AckRanges {
if i == 0 {
continue
}
lastAckRange := f.AckRanges[i-1]
gap := lastAckRange.Smallest - ackRange.Largest - 1
rangeLength := 1 + uint64(gap)/0xFF
if uint64(gap)%0xFF == 0 {
rangeLength--
}
if numRanges+rangeLength < 0xFF {
numRanges += rangeLength
} else {
break
}
}
return numRanges + 1
}
func (f *AckFrame) getMissingSequenceNumberDeltaLen() protocol.PacketNumberLen {
var maxRangeLength protocol.PacketNumber
if f.HasMissingRanges() {
for _, ackRange := range f.AckRanges {
rangeLength := ackRange.Largest - ackRange.Smallest + 1
if rangeLength > maxRangeLength {
maxRangeLength = rangeLength
}
}
} else {
maxRangeLength = f.LargestAcked() - f.LowestAcked() + 1
}
if maxRangeLength <= 0xFF {
return protocol.PacketNumberLen1
}
if maxRangeLength <= 0xFFFF {
return protocol.PacketNumberLen2
}
if maxRangeLength <= 0xFFFFFFFF {
return protocol.PacketNumberLen4
}
return protocol.PacketNumberLen6
}

View File

@@ -1,45 +0,0 @@
package wire
import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// A BlockedFrame is a BLOCKED frame
type BlockedFrame struct {
Offset protocol.ByteCount
}
// parseBlockedFrame parses a BLOCKED frame
func parseBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*BlockedFrame, error) {
if _, err := r.ReadByte(); err != nil {
return nil, err
}
offset, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
return &BlockedFrame{
Offset: protocol.ByteCount(offset),
}, nil
}
func (f *BlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
if !version.UsesIETFFrameFormat() {
return (&blockedFrameLegacy{}).Write(b, version)
}
typeByte := uint8(0x08)
b.WriteByte(typeByte)
utils.WriteVarInt(b, uint64(f.Offset))
return nil
}
// Length of a written frame
func (f *BlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
if !version.UsesIETFFrameFormat() {
return 1 + 4
}
return 1 + utils.VarIntLen(uint64(f.Offset))
}

View File

@@ -1,37 +0,0 @@
package wire
import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
type blockedFrameLegacy struct {
StreamID protocol.StreamID
}
// parseBlockedFrameLegacy parses a BLOCKED frame (in gQUIC format)
// The frame returned is
// * a STREAM_BLOCKED frame, if the BLOCKED applies to a stream
// * a BLOCKED frame, if the BLOCKED applies to the connection
func parseBlockedFrameLegacy(r *bytes.Reader, _ protocol.VersionNumber) (Frame, error) {
if _, err := r.ReadByte(); err != nil { // read the TypeByte
return nil, err
}
streamID, err := utils.BigEndian.ReadUint32(r)
if err != nil {
return nil, err
}
if streamID == 0 {
return &BlockedFrame{}, nil
}
return &StreamBlockedFrame{StreamID: protocol.StreamID(streamID)}, nil
}
//Write writes a BLOCKED frame
func (f *blockedFrameLegacy) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
b.WriteByte(0x05)
utils.BigEndian.WriteUint32(b, uint32(f.StreamID))
return nil
}

View File

@@ -2,52 +2,43 @@ package wire
import (
"bytes"
"errors"
"io"
"math"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/qerr"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
// A ConnectionCloseFrame in QUIC
// A ConnectionCloseFrame is a CONNECTION_CLOSE frame
type ConnectionCloseFrame struct {
ErrorCode qerr.ErrorCode
ReasonPhrase string
IsApplicationError bool
ErrorCode qerr.ErrorCode
ReasonPhrase string
}
// parseConnectionCloseFrame reads a CONNECTION_CLOSE frame
func parseConnectionCloseFrame(r *bytes.Reader, version protocol.VersionNumber) (*ConnectionCloseFrame, error) {
if _, err := r.ReadByte(); err != nil { // read the TypeByte
typeByte, err := r.ReadByte()
if err != nil {
return nil, err
}
var errorCode qerr.ErrorCode
var reasonPhraseLen uint64
if version.UsesIETFFrameFormat() {
ec, err := utils.BigEndian.ReadUint16(r)
if err != nil {
return nil, err
}
errorCode = qerr.ErrorCode(ec)
reasonPhraseLen, err = utils.ReadVarInt(r)
if err != nil {
return nil, err
}
} else {
ec, err := utils.BigEndian.ReadUint32(r)
if err != nil {
return nil, err
}
errorCode = qerr.ErrorCode(ec)
length, err := utils.BigEndian.ReadUint16(r)
if err != nil {
return nil, err
}
reasonPhraseLen = uint64(length)
f := &ConnectionCloseFrame{IsApplicationError: typeByte == 0x1d}
ec, err := utils.BigEndian.ReadUint16(r)
if err != nil {
return nil, err
}
f.ErrorCode = qerr.ErrorCode(ec)
// read the Frame Type, if this is not an application error
if !f.IsApplicationError {
if _, err := utils.ReadVarInt(r); err != nil {
return nil, err
}
}
var reasonPhraseLen uint64
reasonPhraseLen, err = utils.ReadVarInt(r)
if err != nil {
return nil, err
}
// shortcut to prevent the unnecessary allocation of dataLen bytes
// if the dataLen is larger than the remaining length of the packet
// reading the whole reason phrase would result in EOF when attempting to READ
@@ -60,37 +51,31 @@ func parseConnectionCloseFrame(r *bytes.Reader, version protocol.VersionNumber)
// this should never happen, since we already checked the reasonPhraseLen earlier
return nil, err
}
return &ConnectionCloseFrame{
ErrorCode: errorCode,
ReasonPhrase: string(reasonPhrase),
}, nil
f.ReasonPhrase = string(reasonPhrase)
return f, nil
}
// Length of a written frame
func (f *ConnectionCloseFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
if version.UsesIETFFrameFormat() {
return 1 + 2 + utils.VarIntLen(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
length := 1 + 2 + utils.VarIntLen(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
if !f.IsApplicationError {
length++ // for the frame type
}
return 1 + 4 + 2 + protocol.ByteCount(len(f.ReasonPhrase))
return length
}
// Write writes an CONNECTION_CLOSE frame.
func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
b.WriteByte(0x02)
if len(f.ReasonPhrase) > math.MaxUint16 {
return errors.New("ConnectionFrame: ReasonPhrase too long")
}
if version.UsesIETFFrameFormat() {
utils.BigEndian.WriteUint16(b, uint16(f.ErrorCode))
utils.WriteVarInt(b, uint64(len(f.ReasonPhrase)))
if f.IsApplicationError {
b.WriteByte(0x1d)
} else {
utils.BigEndian.WriteUint32(b, uint32(f.ErrorCode))
utils.BigEndian.WriteUint16(b, uint16(len(f.ReasonPhrase)))
b.WriteByte(0x1c)
}
b.WriteString(f.ReasonPhrase)
utils.BigEndian.WriteUint16(b, uint16(f.ErrorCode))
if !f.IsApplicationError {
utils.WriteVarInt(b, 0)
}
utils.WriteVarInt(b, uint64(len(f.ReasonPhrase)))
b.WriteString(f.ReasonPhrase)
return nil
}

View File

@@ -0,0 +1,71 @@
package wire
import (
"bytes"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// A CryptoFrame is a CRYPTO frame
type CryptoFrame struct {
Offset protocol.ByteCount
Data []byte
}
func parseCryptoFrame(r *bytes.Reader, _ protocol.VersionNumber) (*CryptoFrame, error) {
if _, err := r.ReadByte(); err != nil {
return nil, err
}
frame := &CryptoFrame{}
offset, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
frame.Offset = protocol.ByteCount(offset)
dataLen, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
if dataLen > uint64(r.Len()) {
return nil, io.EOF
}
if dataLen != 0 {
frame.Data = make([]byte, dataLen)
if _, err := io.ReadFull(r, frame.Data); err != nil {
// this should never happen, since we already checked the dataLen earlier
return nil, err
}
}
return frame, nil
}
func (f *CryptoFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
b.WriteByte(0x6)
utils.WriteVarInt(b, uint64(f.Offset))
utils.WriteVarInt(b, uint64(len(f.Data)))
b.Write(f.Data)
return nil
}
// Length of a written frame
func (f *CryptoFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
return 1 + utils.VarIntLen(uint64(f.Offset)) + utils.VarIntLen(uint64(len(f.Data))) + protocol.ByteCount(len(f.Data))
}
// MaxDataLen returns the maximum data length
func (f *CryptoFrame) MaxDataLen(maxSize protocol.ByteCount) protocol.ByteCount {
// pretend that the data size will be 1 bytes
// if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards
headerLen := 1 + utils.VarIntLen(uint64(f.Offset)) + 1
if headerLen > maxSize {
return 0
}
maxDataLen := maxSize - headerLen
if utils.VarIntLen(uint64(maxDataLen)) != 1 {
maxDataLen--
}
return maxDataLen
}

View File

@@ -0,0 +1,38 @@
package wire
import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// A DataBlockedFrame is a DATA_BLOCKED frame
type DataBlockedFrame struct {
DataLimit protocol.ByteCount
}
func parseDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DataBlockedFrame, error) {
if _, err := r.ReadByte(); err != nil {
return nil, err
}
offset, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
return &DataBlockedFrame{
DataLimit: protocol.ByteCount(offset),
}, nil
}
func (f *DataBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
typeByte := uint8(0x14)
b.WriteByte(typeByte)
utils.WriteVarInt(b, uint64(f.DataLimit))
return nil
}
// Length of a written frame
func (f *DataBlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
return 1 + utils.VarIntLen(uint64(f.DataLimit))
}

View File

@@ -0,0 +1,204 @@
package wire
import (
"bytes"
"errors"
"fmt"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// ExtendedHeader is the header of a QUIC packet.
type ExtendedHeader struct {
Header
typeByte byte
PacketNumberLen protocol.PacketNumberLen
PacketNumber protocol.PacketNumber
KeyPhase int
}
func (h *ExtendedHeader) parse(b *bytes.Reader, v protocol.VersionNumber) (*ExtendedHeader, error) {
// read the (now unencrypted) first byte
var err error
h.typeByte, err = b.ReadByte()
if err != nil {
return nil, err
}
if _, err := b.Seek(int64(h.ParsedLen())-1, io.SeekCurrent); err != nil {
return nil, err
}
if h.IsLongHeader {
return h.parseLongHeader(b, v)
}
return h.parseShortHeader(b, v)
}
func (h *ExtendedHeader) parseLongHeader(b *bytes.Reader, v protocol.VersionNumber) (*ExtendedHeader, error) {
if h.typeByte&0xc != 0 {
return nil, errors.New("5th and 6th bit must be 0")
}
if err := h.readPacketNumber(b); err != nil {
return nil, err
}
return h, nil
}
func (h *ExtendedHeader) parseShortHeader(b *bytes.Reader, v protocol.VersionNumber) (*ExtendedHeader, error) {
if h.typeByte&0x18 != 0 {
return nil, errors.New("4th and 5th bit must be 0")
}
h.KeyPhase = int(h.typeByte&0x4) >> 2
if err := h.readPacketNumber(b); err != nil {
return nil, err
}
return h, nil
}
func (h *ExtendedHeader) readPacketNumber(b *bytes.Reader) error {
h.PacketNumberLen = protocol.PacketNumberLen(h.typeByte&0x3) + 1
pn, err := utils.BigEndian.ReadUintN(b, uint8(h.PacketNumberLen))
if err != nil {
return err
}
h.PacketNumber = protocol.PacketNumber(pn)
return nil
}
// Write writes the Header.
func (h *ExtendedHeader) Write(b *bytes.Buffer, ver protocol.VersionNumber) error {
if h.IsLongHeader {
return h.writeLongHeader(b, ver)
}
return h.writeShortHeader(b, ver)
}
func (h *ExtendedHeader) writeLongHeader(b *bytes.Buffer, v protocol.VersionNumber) error {
var packetType uint8
switch h.Type {
case protocol.PacketTypeInitial:
packetType = 0x0
case protocol.PacketType0RTT:
packetType = 0x1
case protocol.PacketTypeHandshake:
packetType = 0x2
case protocol.PacketTypeRetry:
packetType = 0x3
}
firstByte := 0xc0 | packetType<<4
if h.Type == protocol.PacketTypeRetry {
odcil, err := encodeSingleConnIDLen(h.OrigDestConnectionID)
if err != nil {
return err
}
firstByte |= odcil
} else { // Retry packets don't have a packet number
firstByte |= uint8(h.PacketNumberLen - 1)
}
b.WriteByte(firstByte)
utils.BigEndian.WriteUint32(b, uint32(h.Version))
connIDLen, err := encodeConnIDLen(h.DestConnectionID, h.SrcConnectionID)
if err != nil {
return err
}
b.WriteByte(connIDLen)
b.Write(h.DestConnectionID.Bytes())
b.Write(h.SrcConnectionID.Bytes())
switch h.Type {
case protocol.PacketTypeRetry:
b.Write(h.OrigDestConnectionID.Bytes())
b.Write(h.Token)
return nil
case protocol.PacketTypeInitial:
utils.WriteVarInt(b, uint64(len(h.Token)))
b.Write(h.Token)
}
utils.WriteVarInt(b, uint64(h.Length))
return h.writePacketNumber(b)
}
// TODO: add support for the key phase
func (h *ExtendedHeader) writeShortHeader(b *bytes.Buffer, v protocol.VersionNumber) error {
typeByte := 0x40 | uint8(h.PacketNumberLen-1)
typeByte |= byte(h.KeyPhase << 2)
b.WriteByte(typeByte)
b.Write(h.DestConnectionID.Bytes())
return h.writePacketNumber(b)
}
func (h *ExtendedHeader) writePacketNumber(b *bytes.Buffer) error {
if h.PacketNumberLen == protocol.PacketNumberLenInvalid || h.PacketNumberLen > protocol.PacketNumberLen4 {
return fmt.Errorf("invalid packet number length: %d", h.PacketNumberLen)
}
utils.BigEndian.WriteUintN(b, uint8(h.PacketNumberLen), uint64(h.PacketNumber))
return nil
}
// GetLength determines the length of the Header.
func (h *ExtendedHeader) GetLength(v protocol.VersionNumber) protocol.ByteCount {
if h.IsLongHeader {
length := 1 /* type byte */ + 4 /* version */ + 1 /* conn id len byte */ + protocol.ByteCount(h.DestConnectionID.Len()+h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen) + utils.VarIntLen(uint64(h.Length))
if h.Type == protocol.PacketTypeInitial {
length += utils.VarIntLen(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token))
}
return length
}
length := protocol.ByteCount(1 /* type byte */ + h.DestConnectionID.Len())
length += protocol.ByteCount(h.PacketNumberLen)
return length
}
// Log logs the Header
func (h *ExtendedHeader) Log(logger utils.Logger) {
if h.IsLongHeader {
var token string
if h.Type == protocol.PacketTypeInitial || h.Type == protocol.PacketTypeRetry {
if len(h.Token) == 0 {
token = "Token: (empty), "
} else {
token = fmt.Sprintf("Token: %#x, ", h.Token)
}
if h.Type == protocol.PacketTypeRetry {
logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sOrigDestConnectionID: %s, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.OrigDestConnectionID, h.Version)
return
}
}
logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %#x, PacketNumberLen: %d, Length: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.Length, h.Version)
} else {
logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, KeyPhase: %d}", h.DestConnectionID, h.PacketNumber, h.PacketNumberLen, h.KeyPhase)
}
}
func encodeConnIDLen(dest, src protocol.ConnectionID) (byte, error) {
dcil, err := encodeSingleConnIDLen(dest)
if err != nil {
return 0, err
}
scil, err := encodeSingleConnIDLen(src)
if err != nil {
return 0, err
}
return scil | dcil<<4, nil
}
func encodeSingleConnIDLen(id protocol.ConnectionID) (byte, error) {
len := id.Len()
if len == 0 {
return 0, nil
}
if len < 4 || len > 18 {
return 0, fmt.Errorf("invalid connection ID length: %d bytes", len)
}
return byte(len - 3), nil
}

View File

@@ -2,16 +2,26 @@ package wire
import (
"bytes"
"errors"
"fmt"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/qerr"
"github.com/lucas-clemente/quic-go/internal/qerr"
)
type frameParser struct {
ackDelayExponent uint8
version protocol.VersionNumber
}
// NewFrameParser creates a new frame parser.
func NewFrameParser(v protocol.VersionNumber) FrameParser {
return &frameParser{version: v}
}
// ParseNextFrame parses the next frame
// It skips PADDING frames.
func ParseNextFrame(r *bytes.Reader, hdr *Header, v protocol.VersionNumber) (Frame, error) {
func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) {
for r.Len() != 0 {
typeByte, _ := r.ReadByte()
if typeByte == 0x0 { // PADDING frame
@@ -19,149 +29,69 @@ func ParseNextFrame(r *bytes.Reader, hdr *Header, v protocol.VersionNumber) (Fra
}
r.UnreadByte()
if !v.UsesIETFFrameFormat() {
return parseGQUICFrame(r, typeByte, hdr, v)
}
return parseIETFFrame(r, typeByte, v)
return p.parseFrame(r, typeByte, encLevel)
}
return nil, nil
}
func parseIETFFrame(r *bytes.Reader, typeByte byte, v protocol.VersionNumber) (Frame, error) {
func (p *frameParser) parseFrame(r *bytes.Reader, typeByte byte, encLevel protocol.EncryptionLevel) (Frame, error) {
var frame Frame
var err error
if typeByte&0xf8 == 0x10 {
frame, err = parseStreamFrame(r, v)
if typeByte&0xf8 == 0x8 {
frame, err = parseStreamFrame(r, p.version)
if err != nil {
err = qerr.Error(qerr.InvalidStreamData, err.Error())
return nil, qerr.Error(qerr.FrameEncodingError, err.Error())
}
return frame, err
return frame, nil
}
// TODO: implement all IETF QUIC frame types
switch typeByte {
case 0x1:
frame, err = parseRstStreamFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidRstStreamData, err.Error())
}
case 0x2:
frame, err = parseConnectionCloseFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error())
frame, err = parsePingFrame(r, p.version)
case 0x2, 0x3:
ackDelayExponent := p.ackDelayExponent
if encLevel != protocol.Encryption1RTT {
ackDelayExponent = protocol.DefaultAckDelayExponent
}
frame, err = parseAckFrame(r, ackDelayExponent, p.version)
case 0x4:
frame, err = parseMaxDataFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error())
}
frame, err = parseResetStreamFrame(r, p.version)
case 0x5:
frame, err = parseMaxStreamDataFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error())
}
frame, err = parseStopSendingFrame(r, p.version)
case 0x6:
frame, err = parseMaxStreamIDFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidFrameData, err.Error())
}
frame, err = parseCryptoFrame(r, p.version)
case 0x7:
frame, err = parsePingFrame(r, v)
case 0x8:
frame, err = parseBlockedFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidBlockedData, err.Error())
}
case 0x9:
frame, err = parseStreamBlockedFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidBlockedData, err.Error())
}
case 0xa:
frame, err = parseStreamIDBlockedFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidFrameData, err.Error())
}
case 0xc:
frame, err = parseStopSendingFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidFrameData, err.Error())
}
case 0xe:
frame, err = parsePathChallengeFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidFrameData, err.Error())
}
case 0xf:
frame, err = parsePathResponseFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidFrameData, err.Error())
}
case 0x1a, 0x1b:
frame, err = parseAckFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidAckData, err.Error())
}
frame, err = parseNewTokenFrame(r, p.version)
case 0x10:
frame, err = parseMaxDataFrame(r, p.version)
case 0x11:
frame, err = parseMaxStreamDataFrame(r, p.version)
case 0x12, 0x13:
frame, err = parseMaxStreamsFrame(r, p.version)
case 0x14:
frame, err = parseDataBlockedFrame(r, p.version)
case 0x15:
frame, err = parseStreamDataBlockedFrame(r, p.version)
case 0x16, 0x17:
frame, err = parseStreamsBlockedFrame(r, p.version)
case 0x18:
frame, err = parseNewConnectionIDFrame(r, p.version)
case 0x19:
frame, err = parseRetireConnectionIDFrame(r, p.version)
case 0x1a:
frame, err = parsePathChallengeFrame(r, p.version)
case 0x1b:
frame, err = parsePathResponseFrame(r, p.version)
case 0x1c, 0x1d:
frame, err = parseConnectionCloseFrame(r, p.version)
default:
err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte))
err = fmt.Errorf("unknown type byte 0x%x", typeByte)
}
return frame, err
if err != nil {
return nil, qerr.Error(qerr.FrameEncodingError, err.Error())
}
return frame, nil
}
func parseGQUICFrame(r *bytes.Reader, typeByte byte, hdr *Header, v protocol.VersionNumber) (Frame, error) {
var frame Frame
var err error
if typeByte&0x80 == 0x80 {
frame, err = parseStreamFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidStreamData, err.Error())
}
return frame, err
} else if typeByte&0xc0 == 0x40 {
frame, err = parseAckFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidAckData, err.Error())
}
return frame, err
}
switch typeByte {
case 0x1:
frame, err = parseRstStreamFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidRstStreamData, err.Error())
}
case 0x2:
frame, err = parseConnectionCloseFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error())
}
case 0x3:
frame, err = parseGoawayFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidGoawayData, err.Error())
}
case 0x4:
frame, err = parseWindowUpdateFrame(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidWindowUpdateData, err.Error())
}
case 0x5:
frame, err = parseBlockedFrameLegacy(r, v)
if err != nil {
err = qerr.Error(qerr.InvalidBlockedData, err.Error())
}
case 0x6:
if !v.UsesStopWaitingFrames() {
err = errors.New("STOP_WAITING frames not supported by this QUIC version")
break
}
frame, err = parseStopWaitingFrame(r, hdr.PacketNumber, hdr.PacketNumberLen, v)
if err != nil {
err = qerr.Error(qerr.InvalidStopWaitingData, err.Error())
}
case 0x7:
frame, err = parsePingFrame(r, v)
default:
err = qerr.Error(qerr.InvalidFrameData, fmt.Sprintf("unknown type byte 0x%x", typeByte))
}
return frame, err
func (p *frameParser) SetAckDelayExponent(exp uint8) {
p.ackDelayExponent = exp
}

View File

@@ -1,68 +0,0 @@
package wire
import (
"bytes"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
// A GoawayFrame is a GOAWAY frame
type GoawayFrame struct {
ErrorCode qerr.ErrorCode
LastGoodStream protocol.StreamID
ReasonPhrase string
}
// parseGoawayFrame parses a GOAWAY frame
func parseGoawayFrame(r *bytes.Reader, _ protocol.VersionNumber) (*GoawayFrame, error) {
frame := &GoawayFrame{}
if _, err := r.ReadByte(); err != nil {
return nil, err
}
errorCode, err := utils.BigEndian.ReadUint32(r)
if err != nil {
return nil, err
}
frame.ErrorCode = qerr.ErrorCode(errorCode)
lastGoodStream, err := utils.BigEndian.ReadUint32(r)
if err != nil {
return nil, err
}
frame.LastGoodStream = protocol.StreamID(lastGoodStream)
reasonPhraseLen, err := utils.BigEndian.ReadUint16(r)
if err != nil {
return nil, err
}
if reasonPhraseLen > uint16(protocol.MaxReceivePacketSize) {
return nil, qerr.Error(qerr.InvalidGoawayData, "reason phrase too long")
}
reasonPhrase := make([]byte, reasonPhraseLen)
if _, err := io.ReadFull(r, reasonPhrase); err != nil {
return nil, err
}
frame.ReasonPhrase = string(reasonPhrase)
return frame, nil
}
func (f *GoawayFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
b.WriteByte(0x03)
utils.BigEndian.WriteUint32(b, uint32(f.ErrorCode))
utils.BigEndian.WriteUint32(b, uint32(f.LastGoodStream))
utils.BigEndian.WriteUint16(b, uint16(len(f.ReasonPhrase)))
b.WriteString(f.ReasonPhrase)
return nil
}
// Length of a written frame
func (f *GoawayFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
return protocol.ByteCount(1 + 4 + 4 + 2 + len(f.ReasonPhrase))
}

View File

@@ -2,323 +2,241 @@ package wire
import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// Header is the header of a QUIC packet.
// It contains fields that are only needed for the gQUIC Public Header and the IETF draft Header.
// ParseConnectionID parses the destination connection ID of a packet.
// It uses the data slice for the connection ID.
// That means that the connection ID must not be used after the packet buffer is released.
func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) {
if len(data) == 0 {
return nil, io.EOF
}
isLongHeader := data[0]&0x80 > 0
if !isLongHeader {
if len(data) < shortHeaderConnIDLen+1 {
return nil, io.EOF
}
return protocol.ConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil
}
if len(data) < 6 {
return nil, io.EOF
}
destConnIDLen, _ := decodeConnIDLen(data[5])
if len(data) < 6+destConnIDLen {
return nil, io.EOF
}
return protocol.ConnectionID(data[6 : 6+destConnIDLen]), nil
}
// IsVersionNegotiationPacket says if this is a version negotiation packet
func IsVersionNegotiationPacket(b []byte) bool {
if len(b) < 5 {
return false
}
return b[0]&0x80 > 0 && b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0
}
var errUnsupportedVersion = errors.New("unsupported version")
// The Header is the version independent part of the header
type Header struct {
IsPublicHeader bool
Version protocol.VersionNumber
SrcConnectionID protocol.ConnectionID
DestConnectionID protocol.ConnectionID
Raw []byte
Version protocol.VersionNumber
DestConnectionID protocol.ConnectionID
SrcConnectionID protocol.ConnectionID
OrigDestConnectionID protocol.ConnectionID // only needed in the Retry packet
PacketNumberLen protocol.PacketNumberLen
PacketNumber protocol.PacketNumber
IsVersionNegotiation bool
SupportedVersions []protocol.VersionNumber // Version Number sent in a Version Negotiation Packet by the server
// only needed for the gQUIC Public Header
VersionFlag bool
ResetFlag bool
DiversificationNonce []byte
// only needed for the IETF Header
Type protocol.PacketType
IsLongHeader bool
KeyPhase int
PayloadLen protocol.ByteCount
Token []byte
Type protocol.PacketType
Length protocol.ByteCount
Token []byte
SupportedVersions []protocol.VersionNumber // sent in a Version Negotiation Packet
OrigDestConnectionID protocol.ConnectionID // sent in the Retry packet
typeByte byte
parsedLen protocol.ByteCount // how many bytes were read while parsing this header
}
var errInvalidPacketNumberLen = errors.New("invalid packet number length")
// Write writes the Header.
func (h *Header) Write(b *bytes.Buffer, pers protocol.Perspective, ver protocol.VersionNumber) error {
if !ver.UsesIETFHeaderFormat() {
h.IsPublicHeader = true // save that this is a Public Header, so we can log it correctly later
return h.writePublicHeader(b, pers, ver)
// ParsePacket parses a packet.
// If the packet has a long header, the packet is cut according to the length field.
// If we understand the version, the packet is header up unto the packet number.
// Otherwise, only the invariant part of the header is parsed.
func ParsePacket(data []byte, shortHeaderConnIDLen int) (*Header, []byte /* packet data */, []byte /* rest */, error) {
hdr, err := parseHeader(bytes.NewReader(data), shortHeaderConnIDLen)
if err != nil {
if err == errUnsupportedVersion {
return hdr, nil, nil, nil
}
return nil, nil, nil, err
}
// write an IETF QUIC header
if h.IsLongHeader {
return h.writeLongHeader(b, ver)
var rest []byte
if hdr.IsLongHeader {
if protocol.ByteCount(len(data)) < hdr.ParsedLen()+hdr.Length {
return nil, nil, nil, fmt.Errorf("packet length (%d bytes) is smaller than the expected length (%d bytes)", len(data)-int(hdr.ParsedLen()), hdr.Length)
}
packetLen := int(hdr.ParsedLen() + hdr.Length)
rest = data[packetLen:]
data = data[:packetLen]
}
return h.writeShortHeader(b, ver)
return hdr, data, rest, nil
}
// TODO: add support for the key phase
func (h *Header) writeLongHeader(b *bytes.Buffer, v protocol.VersionNumber) error {
b.WriteByte(byte(0x80 | h.Type))
utils.BigEndian.WriteUint32(b, uint32(h.Version))
connIDLen, err := encodeConnIDLen(h.DestConnectionID, h.SrcConnectionID)
// ParseHeader parses the header.
// For short header packets: up to the packet number.
// For long header packets:
// * if we understand the version: up to the packet number
// * if not, only the invariant part of the header
func parseHeader(b *bytes.Reader, shortHeaderConnIDLen int) (*Header, error) {
startLen := b.Len()
h, err := parseHeaderImpl(b, shortHeaderConnIDLen)
if err != nil {
return h, err
}
h.parsedLen = protocol.ByteCount(startLen - b.Len())
return h, err
}
func parseHeaderImpl(b *bytes.Reader, shortHeaderConnIDLen int) (*Header, error) {
typeByte, err := b.ReadByte()
if err != nil {
return nil, err
}
h := &Header{
typeByte: typeByte,
IsLongHeader: typeByte&0x80 > 0,
}
if !h.IsLongHeader {
if h.typeByte&0x40 == 0 {
return nil, errors.New("not a QUIC packet")
}
if err := h.parseShortHeader(b, shortHeaderConnIDLen); err != nil {
return nil, err
}
return h, nil
}
return h, h.parseLongHeader(b)
}
func (h *Header) parseShortHeader(b *bytes.Reader, shortHeaderConnIDLen int) error {
var err error
h.DestConnectionID, err = protocol.ReadConnectionID(b, shortHeaderConnIDLen)
return err
}
func (h *Header) parseLongHeader(b *bytes.Reader) error {
v, err := utils.BigEndian.ReadUint32(b)
if err != nil {
return err
}
b.WriteByte(connIDLen)
b.Write(h.DestConnectionID.Bytes())
b.Write(h.SrcConnectionID.Bytes())
h.Version = protocol.VersionNumber(v)
if h.Version != 0 && h.typeByte&0x40 == 0 {
return errors.New("not a QUIC packet")
}
connIDLenByte, err := b.ReadByte()
if err != nil {
return err
}
dcil, scil := decodeConnIDLen(connIDLenByte)
h.DestConnectionID, err = protocol.ReadConnectionID(b, dcil)
if err != nil {
return err
}
h.SrcConnectionID, err = protocol.ReadConnectionID(b, scil)
if err != nil {
return err
}
if h.Version == 0 {
return h.parseVersionNegotiationPacket(b)
}
// If we don't understand the version, we have no idea how to interpret the rest of the bytes
if !protocol.IsSupportedVersion(protocol.SupportedVersions, h.Version) {
return errUnsupportedVersion
}
if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() {
utils.WriteVarInt(b, uint64(len(h.Token)))
b.Write(h.Token)
switch (h.typeByte & 0x30) >> 4 {
case 0x0:
h.Type = protocol.PacketTypeInitial
case 0x1:
h.Type = protocol.PacketType0RTT
case 0x2:
h.Type = protocol.PacketTypeHandshake
case 0x3:
h.Type = protocol.PacketTypeRetry
}
if h.Type == protocol.PacketTypeRetry {
odcil, err := encodeSingleConnIDLen(h.OrigDestConnectionID)
odcil := decodeSingleConnIDLen(h.typeByte & 0xf)
h.OrigDestConnectionID, err = protocol.ReadConnectionID(b, odcil)
if err != nil {
return err
}
// randomize the first 4 bits
odcilByte := make([]byte, 1)
_, _ = rand.Read(odcilByte) // it's safe to ignore the error here
odcilByte[0] = (odcilByte[0] & 0xf0) | odcil
b.Write(odcilByte)
b.Write(h.OrigDestConnectionID.Bytes())
b.Write(h.Token)
h.Token = make([]byte, b.Len())
if _, err := io.ReadFull(b, h.Token); err != nil {
return err
}
return nil
}
if v.UsesLengthInHeader() {
utils.WriteVarInt(b, uint64(h.PayloadLen))
}
if v.UsesVarintPacketNumbers() {
return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen)
}
utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
if h.Type == protocol.PacketType0RTT && v == protocol.Version44 {
if len(h.DiversificationNonce) != 32 {
return errors.New("invalid diversification nonce length")
if h.Type == protocol.PacketTypeInitial {
tokenLen, err := utils.ReadVarInt(b)
if err != nil {
return err
}
b.Write(h.DiversificationNonce)
if tokenLen > uint64(b.Len()) {
return io.EOF
}
h.Token = make([]byte, tokenLen)
if _, err := io.ReadFull(b, h.Token); err != nil {
return err
}
}
pl, err := utils.ReadVarInt(b)
if err != nil {
return err
}
h.Length = protocol.ByteCount(pl)
return nil
}
func (h *Header) parseVersionNegotiationPacket(b *bytes.Reader) error {
if b.Len() == 0 {
return errors.New("Version Negoation packet has empty version list")
}
if b.Len()%4 != 0 {
return errors.New("Version Negotation packet has a version list with an invalid length")
}
h.SupportedVersions = make([]protocol.VersionNumber, b.Len()/4)
for i := 0; b.Len() > 0; i++ {
v, err := utils.BigEndian.ReadUint32(b)
if err != nil {
return err
}
h.SupportedVersions[i] = protocol.VersionNumber(v)
}
return nil
}
func (h *Header) writeShortHeader(b *bytes.Buffer, v protocol.VersionNumber) error {
typeByte := byte(0x30)
typeByte |= byte(h.KeyPhase << 6)
if !v.UsesVarintPacketNumbers() {
switch h.PacketNumberLen {
case protocol.PacketNumberLen1:
case protocol.PacketNumberLen2:
typeByte |= 0x1
case protocol.PacketNumberLen4:
typeByte |= 0x2
default:
return errInvalidPacketNumberLen
}
}
b.WriteByte(typeByte)
b.Write(h.DestConnectionID.Bytes())
if !v.UsesVarintPacketNumbers() {
switch h.PacketNumberLen {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(h.PacketNumber))
case protocol.PacketNumberLen2:
utils.BigEndian.WriteUint16(b, uint16(h.PacketNumber))
case protocol.PacketNumberLen4:
utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
}
return nil
}
return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen)
// ParsedLen returns the number of bytes that were consumed when parsing the header
func (h *Header) ParsedLen() protocol.ByteCount {
return h.parsedLen
}
// writePublicHeader writes a Public Header.
func (h *Header) writePublicHeader(b *bytes.Buffer, pers protocol.Perspective, _ protocol.VersionNumber) error {
if h.ResetFlag || (h.VersionFlag && pers == protocol.PerspectiveServer) {
return errors.New("PublicHeader: Can only write regular packets")
}
if h.SrcConnectionID.Len() != 0 {
return errors.New("PublicHeader: SrcConnectionID must not be set")
}
if len(h.DestConnectionID) != 0 && len(h.DestConnectionID) != 8 {
return fmt.Errorf("PublicHeader: wrong length for Connection ID: %d (expected 8)", len(h.DestConnectionID))
}
publicFlagByte := uint8(0x00)
if h.VersionFlag {
publicFlagByte |= 0x01
}
if h.DestConnectionID.Len() > 0 {
publicFlagByte |= 0x08
}
if len(h.DiversificationNonce) > 0 {
if len(h.DiversificationNonce) != 32 {
return errors.New("invalid diversification nonce length")
}
publicFlagByte |= 0x04
}
switch h.PacketNumberLen {
case protocol.PacketNumberLen1:
publicFlagByte |= 0x00
case protocol.PacketNumberLen2:
publicFlagByte |= 0x10
case protocol.PacketNumberLen4:
publicFlagByte |= 0x20
}
b.WriteByte(publicFlagByte)
if h.DestConnectionID.Len() > 0 {
b.Write(h.DestConnectionID)
}
if h.VersionFlag && pers == protocol.PerspectiveClient {
utils.BigEndian.WriteUint32(b, uint32(h.Version))
}
if len(h.DiversificationNonce) > 0 {
b.Write(h.DiversificationNonce)
}
switch h.PacketNumberLen {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(h.PacketNumber))
case protocol.PacketNumberLen2:
utils.BigEndian.WriteUint16(b, uint16(h.PacketNumber))
case protocol.PacketNumberLen4:
utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
case protocol.PacketNumberLen6:
return errInvalidPacketNumberLen
default:
return errors.New("PublicHeader: PacketNumberLen not set")
}
return nil
// ParseExtended parses the version dependent part of the header.
// The Reader has to be set such that it points to the first byte of the header.
func (h *Header) ParseExtended(b *bytes.Reader, ver protocol.VersionNumber) (*ExtendedHeader, error) {
return h.toExtendedHeader().parse(b, ver)
}
// GetLength determines the length of the Header.
func (h *Header) GetLength(v protocol.VersionNumber) (protocol.ByteCount, error) {
if !v.UsesIETFHeaderFormat() {
return h.getPublicHeaderLength()
}
return h.getHeaderLength(v)
}
func (h *Header) getHeaderLength(v protocol.VersionNumber) (protocol.ByteCount, error) {
if h.IsLongHeader {
length := 1 /* type byte */ + 4 /* version */ + 1 /* conn id len byte */ + protocol.ByteCount(h.DestConnectionID.Len()+h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen)
if v.UsesLengthInHeader() {
length += utils.VarIntLen(uint64(h.PayloadLen))
}
if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() {
length += utils.VarIntLen(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token))
}
if h.Type == protocol.PacketType0RTT && v == protocol.Version44 {
length += protocol.ByteCount(len(h.DiversificationNonce))
}
return length, nil
}
length := protocol.ByteCount(1 /* type byte */ + h.DestConnectionID.Len())
if h.PacketNumberLen != protocol.PacketNumberLen1 && h.PacketNumberLen != protocol.PacketNumberLen2 && h.PacketNumberLen != protocol.PacketNumberLen4 {
return 0, fmt.Errorf("invalid packet number length: %d", h.PacketNumberLen)
}
length += protocol.ByteCount(h.PacketNumberLen)
return length, nil
}
// getPublicHeaderLength gets the length of the publicHeader in bytes.
// It can only be called for regular packets.
func (h *Header) getPublicHeaderLength() (protocol.ByteCount, error) {
length := protocol.ByteCount(1) // 1 byte for public flags
if h.PacketNumberLen == protocol.PacketNumberLen6 {
return 0, errInvalidPacketNumberLen
}
if h.PacketNumberLen != protocol.PacketNumberLen1 && h.PacketNumberLen != protocol.PacketNumberLen2 && h.PacketNumberLen != protocol.PacketNumberLen4 {
return 0, errPacketNumberLenNotSet
}
length += protocol.ByteCount(h.PacketNumberLen)
length += protocol.ByteCount(h.DestConnectionID.Len())
// Version Number in packets sent by the client
if h.VersionFlag {
length += 4
}
length += protocol.ByteCount(len(h.DiversificationNonce))
return length, nil
}
// Log logs the Header
func (h *Header) Log(logger utils.Logger) {
if h.IsPublicHeader {
h.logPublicHeader(logger)
} else {
h.logHeader(logger)
}
}
func (h *Header) logHeader(logger utils.Logger) {
if h.IsLongHeader {
if h.Version == 0 {
logger.Debugf("\tVersionNegotiationPacket{DestConnectionID: %s, SrcConnectionID: %s, SupportedVersions: %s}", h.DestConnectionID, h.SrcConnectionID, h.SupportedVersions)
} else {
var token string
if h.Type == protocol.PacketTypeInitial || h.Type == protocol.PacketTypeRetry {
if len(h.Token) == 0 {
token = "Token: (empty), "
} else {
token = fmt.Sprintf("Token: %#x, ", h.Token)
}
}
if h.Type == protocol.PacketTypeRetry {
logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sOrigDestConnectionID: %s, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.OrigDestConnectionID, h.Version)
return
}
if h.Version == protocol.Version44 {
var divNonce string
if h.Type == protocol.PacketType0RTT {
divNonce = fmt.Sprintf("Diversification Nonce: %#x, ", h.DiversificationNonce)
}
logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, %sVersion: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, h.PacketNumber, h.PacketNumberLen, divNonce, h.Version)
return
}
logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %#x, PacketNumberLen: %d, PayloadLen: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.PayloadLen, h.Version)
}
} else {
logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, KeyPhase: %d}", h.DestConnectionID, h.PacketNumber, h.PacketNumberLen, h.KeyPhase)
}
}
func (h *Header) logPublicHeader(logger utils.Logger) {
ver := "(unset)"
if h.Version != 0 {
ver = h.Version.String()
}
logger.Debugf("\tPublic Header{ConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, Version: %s, DiversificationNonce: %#v}", h.DestConnectionID, h.PacketNumber, h.PacketNumberLen, ver, h.DiversificationNonce)
}
func encodeConnIDLen(dest, src protocol.ConnectionID) (byte, error) {
dcil, err := encodeSingleConnIDLen(dest)
if err != nil {
return 0, err
}
scil, err := encodeSingleConnIDLen(src)
if err != nil {
return 0, err
}
return scil | dcil<<4, nil
}
func encodeSingleConnIDLen(id protocol.ConnectionID) (byte, error) {
len := id.Len()
if len == 0 {
return 0, nil
}
if len < 4 || len > 18 {
return 0, fmt.Errorf("invalid connection ID length: %d bytes", len)
}
return byte(len - 3), nil
func (h *Header) toExtendedHeader() *ExtendedHeader {
return &ExtendedHeader{Header: *h}
}
func decodeConnIDLen(enc byte) (int /*dest conn id len*/, int /*src conn id len*/) {

View File

@@ -1,273 +0,0 @@
package wire
import (
"bytes"
"fmt"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
// The InvariantHeader is the version independent part of the header
type InvariantHeader struct {
IsLongHeader bool
Version protocol.VersionNumber
SrcConnectionID protocol.ConnectionID
DestConnectionID protocol.ConnectionID
typeByte byte
}
// ParseInvariantHeader parses the version independent part of the header
func ParseInvariantHeader(b *bytes.Reader, shortHeaderConnIDLen int) (*InvariantHeader, error) {
typeByte, err := b.ReadByte()
if err != nil {
return nil, err
}
h := &InvariantHeader{typeByte: typeByte}
h.IsLongHeader = typeByte&0x80 > 0
// If this is not a Long Header, it could either be a Public Header or a Short Header.
if !h.IsLongHeader {
// In the Public Header 0x8 is the Connection ID Flag.
// In the IETF Short Header:
// * 0x8 it is the gQUIC Demultiplexing bit, and always 0.
// * 0x20 and 0x10 are always 1.
var connIDLen int
if typeByte&0x8 > 0 { // Public Header containing a connection ID
connIDLen = 8
}
if typeByte&0x38 == 0x30 { // Short Header
connIDLen = shortHeaderConnIDLen
}
if connIDLen > 0 {
h.DestConnectionID, err = protocol.ReadConnectionID(b, connIDLen)
if err != nil {
return nil, err
}
}
return h, nil
}
// Long Header
v, err := utils.BigEndian.ReadUint32(b)
if err != nil {
return nil, err
}
h.Version = protocol.VersionNumber(v)
connIDLenByte, err := b.ReadByte()
if err != nil {
return nil, err
}
dcil, scil := decodeConnIDLen(connIDLenByte)
h.DestConnectionID, err = protocol.ReadConnectionID(b, dcil)
if err != nil {
return nil, err
}
h.SrcConnectionID, err = protocol.ReadConnectionID(b, scil)
if err != nil {
return nil, err
}
return h, nil
}
// Parse parses the version dependent part of the header
func (iv *InvariantHeader) Parse(b *bytes.Reader, sentBy protocol.Perspective, ver protocol.VersionNumber) (*Header, error) {
if iv.IsLongHeader {
if iv.Version == 0 { // Version Negotiation Packet
return iv.parseVersionNegotiationPacket(b)
}
return iv.parseLongHeader(b, sentBy, ver)
}
// The Public Header never uses 6 byte packet numbers.
// Therefore, the third and fourth bit will never be 11.
// For the Short Header, the third and fourth bit are always 11.
if iv.typeByte&0x30 != 0x30 {
if sentBy == protocol.PerspectiveServer && iv.typeByte&0x1 > 0 {
return iv.parseVersionNegotiationPacket(b)
}
return iv.parsePublicHeader(b, sentBy, ver)
}
return iv.parseShortHeader(b, ver)
}
func (iv *InvariantHeader) toHeader() *Header {
return &Header{
IsLongHeader: iv.IsLongHeader,
DestConnectionID: iv.DestConnectionID,
SrcConnectionID: iv.SrcConnectionID,
Version: iv.Version,
}
}
func (iv *InvariantHeader) parseVersionNegotiationPacket(b *bytes.Reader) (*Header, error) {
h := iv.toHeader()
h.VersionFlag = true
if b.Len() == 0 {
return nil, qerr.Error(qerr.InvalidVersionNegotiationPacket, "empty version list")
}
h.IsVersionNegotiation = true
h.SupportedVersions = make([]protocol.VersionNumber, b.Len()/4)
for i := 0; b.Len() > 0; i++ {
v, err := utils.BigEndian.ReadUint32(b)
if err != nil {
return nil, qerr.InvalidVersionNegotiationPacket
}
h.SupportedVersions[i] = protocol.VersionNumber(v)
}
return h, nil
}
func (iv *InvariantHeader) parseLongHeader(b *bytes.Reader, sentBy protocol.Perspective, v protocol.VersionNumber) (*Header, error) {
h := iv.toHeader()
h.Type = protocol.PacketType(iv.typeByte & 0x7f)
if h.Type != protocol.PacketTypeInitial && h.Type != protocol.PacketTypeRetry && h.Type != protocol.PacketType0RTT && h.Type != protocol.PacketTypeHandshake {
return nil, qerr.Error(qerr.InvalidPacketHeader, fmt.Sprintf("Received packet with invalid packet type: %d", h.Type))
}
if h.Type == protocol.PacketTypeRetry {
odcilByte, err := b.ReadByte()
if err != nil {
return nil, err
}
odcil := decodeSingleConnIDLen(odcilByte & 0xf)
h.OrigDestConnectionID, err = protocol.ReadConnectionID(b, odcil)
if err != nil {
return nil, err
}
h.Token = make([]byte, b.Len())
if _, err := io.ReadFull(b, h.Token); err != nil {
return nil, err
}
return h, nil
}
if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() {
tokenLen, err := utils.ReadVarInt(b)
if err != nil {
return nil, err
}
if tokenLen > uint64(b.Len()) {
return nil, io.EOF
}
h.Token = make([]byte, tokenLen)
if _, err := io.ReadFull(b, h.Token); err != nil {
return nil, err
}
}
if v.UsesLengthInHeader() {
pl, err := utils.ReadVarInt(b)
if err != nil {
return nil, err
}
h.PayloadLen = protocol.ByteCount(pl)
}
if v.UsesVarintPacketNumbers() {
pn, pnLen, err := utils.ReadVarIntPacketNumber(b)
if err != nil {
return nil, err
}
h.PacketNumber = pn
h.PacketNumberLen = pnLen
} else {
pn, err := utils.BigEndian.ReadUint32(b)
if err != nil {
return nil, err
}
h.PacketNumber = protocol.PacketNumber(pn)
h.PacketNumberLen = protocol.PacketNumberLen4
}
if h.Type == protocol.PacketType0RTT && v == protocol.Version44 && sentBy == protocol.PerspectiveServer {
h.DiversificationNonce = make([]byte, 32)
if _, err := io.ReadFull(b, h.DiversificationNonce); err != nil {
if err == io.ErrUnexpectedEOF {
return nil, io.EOF
}
return nil, err
}
}
return h, nil
}
func (iv *InvariantHeader) parseShortHeader(b *bytes.Reader, v protocol.VersionNumber) (*Header, error) {
h := iv.toHeader()
h.KeyPhase = int(iv.typeByte&0x40) >> 6
if v.UsesVarintPacketNumbers() {
pn, pnLen, err := utils.ReadVarIntPacketNumber(b)
if err != nil {
return nil, err
}
h.PacketNumber = pn
h.PacketNumberLen = pnLen
} else {
switch iv.typeByte & 0x3 {
case 0x0:
h.PacketNumberLen = protocol.PacketNumberLen1
case 0x1:
h.PacketNumberLen = protocol.PacketNumberLen2
case 0x2:
h.PacketNumberLen = protocol.PacketNumberLen4
default:
return nil, errInvalidPacketNumberLen
}
p, err := utils.BigEndian.ReadUintN(b, uint8(h.PacketNumberLen))
if err != nil {
return nil, err
}
h.PacketNumber = protocol.PacketNumber(p)
}
return h, nil
}
func (iv *InvariantHeader) parsePublicHeader(b *bytes.Reader, sentBy protocol.Perspective, ver protocol.VersionNumber) (*Header, error) {
h := iv.toHeader()
h.IsPublicHeader = true
h.ResetFlag = iv.typeByte&0x2 > 0
if h.ResetFlag {
return h, nil
}
h.VersionFlag = iv.typeByte&0x1 > 0
if h.VersionFlag && sentBy == protocol.PerspectiveClient {
v, err := utils.BigEndian.ReadUint32(b)
if err != nil {
return nil, err
}
h.Version = protocol.VersionNumber(v)
}
// Contrary to what the gQUIC wire spec says, the 0x4 bit only indicates the presence of the diversification nonce for packets sent by the server.
// It doesn't have any meaning when sent by the client.
if sentBy == protocol.PerspectiveServer && iv.typeByte&0x4 > 0 {
h.DiversificationNonce = make([]byte, 32)
if _, err := io.ReadFull(b, h.DiversificationNonce); err != nil {
if err == io.ErrUnexpectedEOF {
return nil, io.EOF
}
return nil, err
}
}
switch iv.typeByte & 0x30 {
case 0x00:
h.PacketNumberLen = protocol.PacketNumberLen1
case 0x10:
h.PacketNumberLen = protocol.PacketNumberLen2
case 0x20:
h.PacketNumberLen = protocol.PacketNumberLen4
}
pn, err := utils.BigEndian.ReadUintN(b, uint8(h.PacketNumberLen))
if err != nil {
return nil, err
}
h.PacketNumber = protocol.PacketNumber(pn)
return h, nil
}

View File

@@ -11,3 +11,9 @@ type Frame interface {
Write(b *bytes.Buffer, version protocol.VersionNumber) error
Length(version protocol.VersionNumber) protocol.ByteCount
}
// A FrameParser parses QUIC frames, one by one.
type FrameParser interface {
ParseNext(*bytes.Reader, protocol.EncryptionLevel) (Frame, error)
SetAckDelayExponent(uint8)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
@@ -17,14 +18,11 @@ func LogFrame(logger utils.Logger, frame Frame, sent bool) {
dir = "->"
}
switch f := frame.(type) {
case *CryptoFrame:
dataLen := protocol.ByteCount(len(f.Data))
logger.Debugf("\t%s &wire.CryptoFrame{Offset: 0x%x, Data length: 0x%x, Offset + Data length: 0x%x}", dir, f.Offset, dataLen, f.Offset+dataLen)
case *StreamFrame:
logger.Debugf("\t%s &wire.StreamFrame{StreamID: %d, FinBit: %t, Offset: 0x%x, Data length: 0x%x, Offset + Data length: 0x%x}", dir, f.StreamID, f.FinBit, f.Offset, f.DataLen(), f.Offset+f.DataLen())
case *StopWaitingFrame:
if sent {
logger.Debugf("\t%s &wire.StopWaitingFrame{LeastUnacked: 0x%x, PacketNumberLen: 0x%x}", dir, f.LeastUnacked, f.PacketNumberLen)
} else {
logger.Debugf("\t%s &wire.StopWaitingFrame{LeastUnacked: 0x%x}", dir, f.LeastUnacked)
}
case *AckFrame:
if len(f.AckRanges) > 1 {
ackRanges := make([]string, len(f.AckRanges))
@@ -35,6 +33,10 @@ func LogFrame(logger utils.Logger, frame Frame, sent bool) {
} else {
logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %#x, LowestAcked: %#x, DelayTime: %s}", dir, f.LargestAcked(), f.LowestAcked(), f.DelayTime.String())
}
case *NewConnectionIDFrame:
logger.Debugf("\t%s &wire.NewConnectionIDFrame{SequenceNumber: %d, ConnectionID: %s, StatelessResetToken: %#x}", dir, f.SequenceNumber, f.ConnectionID, f.StatelessResetToken)
case *NewTokenFrame:
logger.Debugf("\t%s &wire.NewTokenFrame{Token: %#x}", dir, f.Token)
default:
logger.Debugf("\t%s %#v", dir, frame)
}

View File

@@ -14,7 +14,6 @@ type MaxDataFrame struct {
// parseMaxDataFrame parses a MAX_DATA frame
func parseMaxDataFrame(r *bytes.Reader, version protocol.VersionNumber) (*MaxDataFrame, error) {
// read the TypeByte
if _, err := r.ReadByte(); err != nil {
return nil, err
}
@@ -30,22 +29,12 @@ func parseMaxDataFrame(r *bytes.Reader, version protocol.VersionNumber) (*MaxDat
//Write writes a MAX_STREAM_DATA frame
func (f *MaxDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
if !version.UsesIETFFrameFormat() {
// write a gQUIC WINDOW_UPDATE frame (with stream ID 0, which means connection-level there)
return (&windowUpdateFrame{
StreamID: 0,
ByteOffset: f.ByteOffset,
}).Write(b, version)
}
b.WriteByte(0x4)
b.WriteByte(0x10)
utils.WriteVarInt(b, uint64(f.ByteOffset))
return nil
}
// Length of a written frame
func (f *MaxDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
if !version.UsesIETFFrameFormat() { // writing this frame would result in a gQUIC WINDOW_UPDATE being written, which is longer
return 1 + 4 + 8
}
return 1 + utils.VarIntLen(uint64(f.ByteOffset))
}

View File

@@ -7,17 +7,13 @@ import (
"github.com/lucas-clemente/quic-go/internal/utils"
)
// A MaxStreamDataFrame carries flow control information for a stream
// A MaxStreamDataFrame is a MAX_STREAM_DATA frame
type MaxStreamDataFrame struct {
StreamID protocol.StreamID
ByteOffset protocol.ByteCount
}
// parseMaxStreamDataFrame parses a MAX_STREAM_DATA frame
func parseMaxStreamDataFrame(r *bytes.Reader, version protocol.VersionNumber) (*MaxStreamDataFrame, error) {
frame := &MaxStreamDataFrame{}
// read the TypeByte
if _, err := r.ReadByte(); err != nil {
return nil, err
}
@@ -26,25 +22,19 @@ func parseMaxStreamDataFrame(r *bytes.Reader, version protocol.VersionNumber) (*
if err != nil {
return nil, err
}
frame.StreamID = protocol.StreamID(sid)
byteOffset, err := utils.ReadVarInt(r)
offset, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
frame.ByteOffset = protocol.ByteCount(byteOffset)
return frame, nil
return &MaxStreamDataFrame{
StreamID: protocol.StreamID(sid),
ByteOffset: protocol.ByteCount(offset),
}, nil
}
// Write writes a MAX_STREAM_DATA frame
func (f *MaxStreamDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
if !version.UsesIETFFrameFormat() {
return (&windowUpdateFrame{
StreamID: f.StreamID,
ByteOffset: f.ByteOffset,
}).Write(b, version)
}
b.WriteByte(0x5)
b.WriteByte(0x11)
utils.WriteVarInt(b, uint64(f.StreamID))
utils.WriteVarInt(b, uint64(f.ByteOffset))
return nil
@@ -52,9 +42,5 @@ func (f *MaxStreamDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumb
// Length of a written frame
func (f *MaxStreamDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
// writing this frame would result in a gQUIC WINDOW_UPDATE being written, which has a different length
if !version.UsesIETFFrameFormat() {
return 1 + 4 + 8
}
return 1 + utils.VarIntLen(uint64(f.StreamID)) + utils.VarIntLen(uint64(f.ByteOffset))
}

View File

@@ -1,37 +0,0 @@
package wire
import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// A MaxStreamIDFrame is a MAX_STREAM_ID frame
type MaxStreamIDFrame struct {
StreamID protocol.StreamID
}
// parseMaxStreamIDFrame parses a MAX_STREAM_ID frame
func parseMaxStreamIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStreamIDFrame, error) {
// read the Type byte
if _, err := r.ReadByte(); err != nil {
return nil, err
}
streamID, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
return &MaxStreamIDFrame{StreamID: protocol.StreamID(streamID)}, nil
}
func (f *MaxStreamIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
b.WriteByte(0x6)
utils.WriteVarInt(b, uint64(f.StreamID))
return nil
}
// Length of a written frame
func (f *MaxStreamIDFrame) Length(protocol.VersionNumber) protocol.ByteCount {
return 1 + utils.VarIntLen(uint64(f.StreamID))
}

View File

@@ -0,0 +1,51 @@
package wire
import (
"bytes"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// A MaxStreamsFrame is a MAX_STREAMS frame
type MaxStreamsFrame struct {
Type protocol.StreamType
MaxStreams uint64
}
func parseMaxStreamsFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStreamsFrame, error) {
typeByte, err := r.ReadByte()
if err != nil {
return nil, err
}
f := &MaxStreamsFrame{}
switch typeByte {
case 0x12:
f.Type = protocol.StreamTypeBidi
case 0x13:
f.Type = protocol.StreamTypeUni
}
streamID, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
f.MaxStreams = streamID
return f, nil
}
func (f *MaxStreamsFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
switch f.Type {
case protocol.StreamTypeBidi:
b.WriteByte(0x12)
case protocol.StreamTypeUni:
b.WriteByte(0x13)
}
utils.WriteVarInt(b, f.MaxStreams)
return nil
}
// Length of a written frame
func (f *MaxStreamsFrame) Length(protocol.VersionNumber) protocol.ByteCount {
return 1 + utils.VarIntLen(f.MaxStreams)
}

View File

@@ -0,0 +1,70 @@
package wire
import (
"bytes"
"fmt"
"io"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
// A NewConnectionIDFrame is a NEW_CONNECTION_ID frame
type NewConnectionIDFrame struct {
SequenceNumber uint64
ConnectionID protocol.ConnectionID
StatelessResetToken [16]byte
}
func parseNewConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewConnectionIDFrame, error) {
if _, err := r.ReadByte(); err != nil {
return nil, err
}
seq, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
connIDLen, err := r.ReadByte()
if err != nil {
return nil, err
}
if connIDLen < 4 || connIDLen > 18 {
return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen)
}
connID, err := protocol.ReadConnectionID(r, int(connIDLen))
if err != nil {
return nil, err
}
frame := &NewConnectionIDFrame{
SequenceNumber: seq,
ConnectionID: connID,
}
if _, err := io.ReadFull(r, frame.StatelessResetToken[:]); err != nil {
if err == io.ErrUnexpectedEOF {
return nil, io.EOF
}
return nil, err
}
return frame, nil
}
func (f *NewConnectionIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
b.WriteByte(0x18)
utils.WriteVarInt(b, f.SequenceNumber)
connIDLen := f.ConnectionID.Len()
if connIDLen < 4 || connIDLen > 18 {
return fmt.Errorf("invalid connection ID length: %d", connIDLen)
}
b.WriteByte(uint8(connIDLen))
b.Write(f.ConnectionID.Bytes())
b.Write(f.StatelessResetToken[:])
return nil
}
// Length of a written frame
func (f *NewConnectionIDFrame) Length(protocol.VersionNumber) protocol.ByteCount {
return 1 + utils.VarIntLen(f.SequenceNumber) + 1 /* connection ID length */ + protocol.ByteCount(f.ConnectionID.Len()) + 16
}

View File

@@ -0,0 +1,44 @@
package wire
import (
"bytes"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
)
// A NewTokenFrame is a NEW_TOKEN frame
type NewTokenFrame struct {
Token []byte
}
func parseNewTokenFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewTokenFrame, error) {
if _, err := r.ReadByte(); err != nil {
return nil, err
}
tokenLen, err := utils.ReadVarInt(r)
if err != nil {
return nil, err
}
if uint64(r.Len()) < tokenLen {
return nil, io.EOF
}
token := make([]byte, int(tokenLen))
if _, err := io.ReadFull(r, token); err != nil {
return nil, err
}
return &NewTokenFrame{Token: token}, nil
}
func (f *NewTokenFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
b.WriteByte(0x7)
utils.WriteVarInt(b, uint64(len(f.Token)))
b.Write(f.Token)
return nil
}
// Length of a written frame
func (f *NewTokenFrame) Length(protocol.VersionNumber) protocol.ByteCount {
return 1 + utils.VarIntLen(uint64(len(f.Token))) + protocol.ByteCount(len(f.Token))
}

View File

@@ -27,8 +27,7 @@ func parsePathChallengeFrame(r *bytes.Reader, version protocol.VersionNumber) (*
}
func (f *PathChallengeFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error {
typeByte := uint8(0x0e)
b.WriteByte(typeByte)
b.WriteByte(0x1a)
b.Write(f.Data[:])
return nil
}

Some files were not shown because too many files have changed in this diff Show More