move stuff from example server to new Session and SessionConfig classes

This commit is contained in:
Lucas Clemente 2016-04-14 17:56:19 +02:00
parent 49ccd0bb01
commit 09879ed4f7
5 changed files with 225 additions and 122 deletions

View file

@ -12,7 +12,7 @@ import (
)
const (
// QuicVersion32Bytes is the QUIC protocol version
// QuicVersionNumber32 is the QUIC protocol version
QuicVersionNumber32 = 32
)
@ -25,6 +25,11 @@ func main() {
panic(err)
}
serverConfig := quic.NewServerConfig(crypto.NewCurve25519KEX(), keyData)
// TODO: When should a session be created?
sessions := map[uint64]*quic.Session{}
addr, err := net.ResolveUDPAddr("udp", "localhost:6121")
if err != nil {
panic(err)
@ -35,121 +40,45 @@ func main() {
panic(err)
}
data := make([]byte, 0x10000)
n, remoteAddr, err := conn.ReadFromUDP(data)
if err != nil {
panic(err)
}
data = data[:n]
r := bytes.NewReader(data)
fmt.Printf("Remote addr: %v\n", remoteAddr)
publicHeader, err := quic.ParsePublicHeader(r)
if err != nil {
panic(err)
}
// send Version Negotiation Packet if the client is speaking a different protocol version
if publicHeader.VersionFlag && publicHeader.QuicVersion != QuicVersion32 {
fmt.Println("Sending VersionNegotiationPacket")
fullReply := &bytes.Buffer{}
responsePublicHeader := quic.PublicHeader{ConnectionID: publicHeader.ConnectionID, PacketNumber: 1, VersionFlag: true}
err = responsePublicHeader.WritePublicHeader(fullReply)
for {
data := make([]byte, 0x10000)
n, remoteAddr, err := conn.ReadFromUDP(data)
if err != nil {
panic(err)
}
utils.WriteUint32BigEndian(fullReply, QuicVersion32)
conn.WriteToUDP(fullReply.Bytes(), remoteAddr)
data = data[:n]
r := bytes.NewReader(data)
fmt.Printf("Received %d bytes from %v\n", n, remoteAddr)
publicHeader, err := quic.ParsePublicHeader(r)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", publicHeader)
// Send Version Negotiation Packet if the client is speaking a different protocol version
if publicHeader.VersionFlag && publicHeader.QuicVersion != QuicVersion32 {
fmt.Println("Sending VersionNegotiationPacket")
fullReply := &bytes.Buffer{}
responsePublicHeader := quic.PublicHeader{ConnectionID: publicHeader.ConnectionID, PacketNumber: 1, VersionFlag: true}
err = responsePublicHeader.WritePublicHeader(fullReply)
if err != nil {
panic(err)
}
utils.WriteUint32BigEndian(fullReply, QuicVersion32)
_, err := conn.WriteToUDP(fullReply.Bytes(), remoteAddr)
if err != nil {
panic(err)
}
continue
}
session, ok := sessions[publicHeader.ConnectionID]
if !ok {
session = quic.NewSession(conn, publicHeader.ConnectionID, serverConfig)
sessions[publicHeader.ConnectionID] = session
}
session.HandlePacket(remoteAddr, data[0:n-r.Len()], publicHeader, r)
}
nullAEAD := &crypto.NullAEAD{}
r, err = nullAEAD.Open(0, data[0:int(r.Size())-r.Len()], r)
if err != nil {
panic(err)
}
privateFlag, err := r.ReadByte()
if err != nil {
panic(err)
}
var entropyAcc quic.EntropyAccumulator
entropyAcc.Add(publicHeader.PacketNumber, privateFlag&0x01 > 0)
frame, err := quic.ParseStreamFrame(r)
if err != nil {
panic(err)
}
messageTag, cryptoData, err := quic.ParseCryptoMessage(frame.Data)
if err != nil {
panic(err)
}
if messageTag != quic.TagCHLO {
panic("expected CHLO")
}
fmt.Printf("Talking to: %q\n", cryptoData[quic.TagUAID])
kex := crypto.NewCurve25519KEX()
serverConfig := &bytes.Buffer{}
quic.WriteCryptoMessage(serverConfig, quic.TagSCFG, map[quic.Tag][]byte{
quic.TagSCID: []byte{0xC5, 0x1C, 0x73, 0x6B, 0x8F, 0x48, 0x49, 0xAE, 0xB3, 0x00, 0xA2, 0xD4, 0x4B, 0xA0, 0xCF, 0xDF},
quic.TagKEXS: []byte("C255"),
quic.TagAEAD: []byte("AESG"),
quic.TagPUBS: append([]byte{0x20, 0x00, 0x00}, kex.PublicKey()...),
quic.TagOBIT: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
quic.TagEXPY: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
quic.TagVER: []byte("Q032"),
})
proof, err := keyData.SignServerProof(frame.Data, serverConfig.Bytes())
if err != nil {
panic(err)
}
serverReply := &bytes.Buffer{}
quic.WriteCryptoMessage(serverReply, quic.TagREJ, map[quic.Tag][]byte{
quic.TagSCFG: serverConfig.Bytes(),
quic.TagCERT: keyData.GetCERTdata(),
quic.TagPROF: proof,
})
replyFrame := &bytes.Buffer{}
replyFrame.WriteByte(0) // Private header
quic.WriteAckFrame(replyFrame, &quic.AckFrame{
Entropy: entropyAcc.Get(),
LargestObserved: 1,
})
quic.WriteStreamFrame(replyFrame, &quic.StreamFrame{
StreamID: 1,
Data: serverReply.Bytes(),
})
fullReply := &bytes.Buffer{}
responsePublicHeader := quic.PublicHeader{ConnectionID: publicHeader.ConnectionID, PacketNumber: 1}
fmt.Println(responsePublicHeader)
err = responsePublicHeader.WritePublicHeader(fullReply)
if err != nil {
panic(err)
}
nullAEAD.Seal(0, fullReply, fullReply.Bytes(), replyFrame.Bytes())
conn.WriteToUDP(fullReply.Bytes(), remoteAddr)
n, _, err = conn.ReadFromUDP(data)
if err != nil {
panic(err)
}
data = data[:n]
r = bytes.NewReader(data)
publicHeader, err = quic.ParsePublicHeader(r)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", publicHeader)
}

View file

@ -7,6 +7,10 @@ import (
"github.com/lucas-clemente/quic-go/utils"
)
type Frame interface {
Write(b *bytes.Buffer) error
}
// A StreamFrame of QUIC
type StreamFrame struct {
FinBit bool
@ -67,7 +71,7 @@ func ParseStreamFrame(r *bytes.Reader) (*StreamFrame, error) {
}
// WriteStreamFrame writes a stream frame.
func WriteStreamFrame(b *bytes.Buffer, f *StreamFrame) {
func (f *StreamFrame) Write(b *bytes.Buffer) error {
typeByte := uint8(0x80)
if f.FinBit {
typeByte ^= 0x40
@ -84,6 +88,7 @@ func WriteStreamFrame(b *bytes.Buffer, f *StreamFrame) {
}
utils.WriteUint16(b, uint16(len(f.Data)))
b.Write(f.Data)
return nil
}
// An AckFrame in QUIC
@ -93,7 +98,7 @@ type AckFrame struct {
}
// WriteAckFrame writes an ack frame.
func WriteAckFrame(b *bytes.Buffer, f *AckFrame) {
func (f *AckFrame) Write(b *bytes.Buffer) error {
typeByte := uint8(0x48)
b.WriteByte(typeByte)
b.WriteByte(f.Entropy)
@ -102,4 +107,5 @@ func WriteAckFrame(b *bytes.Buffer, f *AckFrame) {
b.WriteByte(0x01) // Just one timestamp
b.WriteByte(0x00) // Largest observed
utils.WriteUint32(b, 0) // First timestamp
return nil
}

View file

@ -34,20 +34,20 @@ var _ = Describe("Frame", func() {
Context("when writing", func() {
It("writes sample frame", func() {
b := &bytes.Buffer{}
WriteStreamFrame(b, &StreamFrame{
(&StreamFrame{
StreamID: 1,
Data: []byte("foobar"),
})
}).Write(b)
Expect(b.Bytes()).To(Equal([]byte{0xa3, 0x1, 0, 0, 0, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'}))
})
It("writes offsets", func() {
b := &bytes.Buffer{}
WriteStreamFrame(b, &StreamFrame{
(&StreamFrame{
StreamID: 1,
Offset: 16,
Data: []byte("foobar"),
})
}).Write(b)
Expect(b.Bytes()).To(Equal([]byte{0xbf, 0x1, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0x06, 0x00, 'f', 'o', 'o', 'b', 'a', 'r'}))
})
})
@ -57,10 +57,10 @@ var _ = Describe("Frame", func() {
Context("when writing", func() {
It("writes simple frames", func() {
b := &bytes.Buffer{}
WriteAckFrame(b, &AckFrame{
(&AckFrame{
Entropy: 2,
LargestObserved: 1,
})
}).Write(b)
Expect(b.Bytes()).To(Equal([]byte{0x48, 0x02, 0x01, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}))
})
})

46
server_config.go Normal file
View file

@ -0,0 +1,46 @@
package quic
import (
"bytes"
"github.com/lucas-clemente/quic-go/crypto"
)
// ServerConfig is a server config
type ServerConfig struct {
kex crypto.KeyExchange
kd *crypto.KeyData
}
// NewServerConfig creates a new server config
func NewServerConfig(kex crypto.KeyExchange, kd *crypto.KeyData) *ServerConfig {
return &ServerConfig{
kex: kex,
kd: kd,
}
}
// Get the server config binary representation
func (s *ServerConfig) Get() []byte {
var serverConfig bytes.Buffer
WriteCryptoMessage(&serverConfig, TagSCFG, map[Tag][]byte{
TagSCID: []byte{0xC5, 0x1C, 0x73, 0x6B, 0x8F, 0x48, 0x49, 0xAE, 0xB3, 0x00, 0xA2, 0xD4, 0x4B, 0xA0, 0xCF, 0xDF},
TagKEXS: []byte("C255"),
TagAEAD: []byte("AESG"),
TagPUBS: append([]byte{0x20, 0x00, 0x00}, s.kex.PublicKey()...),
TagOBIT: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
TagEXPY: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
TagVER: []byte("Q032"),
})
return serverConfig.Bytes()
}
// Sign the server config and CHLO with the server's keyData
func (s *ServerConfig) Sign(chlo []byte) ([]byte, error) {
return s.kd.SignServerProof(chlo, s.Get())
}
// GetCertData returns the certificate data
func (s *ServerConfig) GetCertData() []byte {
return s.kd.GetCERTdata()
}

122
session.go Normal file
View file

@ -0,0 +1,122 @@
package quic
import (
"bytes"
"fmt"
"net"
"github.com/lucas-clemente/quic-go/crypto"
)
// A Session is a QUIC session
type Session struct {
ConnectionID uint64
ServerConfig *ServerConfig
Connection *net.UDPConn
CurrentRemoteAddr *net.UDPAddr
Entropy EntropyAccumulator
}
// NewSession makes a new session
func NewSession(conn *net.UDPConn, connectionID uint64, sCfg *ServerConfig) *Session {
return &Session{
Connection: conn,
ConnectionID: connectionID,
ServerConfig: sCfg,
}
}
// HandlePacket handles a packet
func (s *Session) HandlePacket(addr *net.UDPAddr, publicHeaderBinary []byte, publicHeader *PublicHeader, r *bytes.Reader) error {
// TODO: Only do this after authenticating
if addr != s.CurrentRemoteAddr {
s.CurrentRemoteAddr = addr
}
nullAEAD := &crypto.NullAEAD{}
r, err := nullAEAD.Open(0, publicHeaderBinary, r)
if err != nil {
return err
}
privateFlag, err := r.ReadByte()
if err != nil {
return err
}
s.Entropy.Add(publicHeader.PacketNumber, privateFlag&0x01 > 0)
// TODO: Switch frame type here
frame, err := ParseStreamFrame(r)
if err != nil {
return err
}
// TODO: Switch stream here
if frame.StreamID != 1 {
panic("streamid not 1")
}
messageTag, cryptoData, err := ParseCryptoMessage(frame.Data)
if err != nil {
panic(err)
}
// TODO: Switch client messages here
if messageTag != TagCHLO {
panic("expected CHLO")
}
if _, ok := cryptoData[TagPUBS]; ok {
panic("received CHLO with PUBS")
}
proof, err := s.ServerConfig.Sign(frame.Data)
if err != nil {
return err
}
var serverReply bytes.Buffer
WriteCryptoMessage(&serverReply, TagREJ, map[Tag][]byte{
TagSCFG: s.ServerConfig.Get(),
TagCERT: s.ServerConfig.GetCertData(),
TagPROF: proof,
})
s.SendFrames([]Frame{
&AckFrame{
Entropy: s.Entropy.Get(),
LargestObserved: 1,
},
&StreamFrame{
StreamID: 1,
Data: serverReply.Bytes(),
},
})
return nil
}
// SendFrames sends a number of frames to the client
func (s *Session) SendFrames(frames []Frame) error {
var framesData bytes.Buffer
framesData.WriteByte(0) // TODO: entropy
for _, f := range frames {
if err := f.Write(&framesData); err != nil {
return err
}
}
var fullReply bytes.Buffer
responsePublicHeader := PublicHeader{ConnectionID: s.ConnectionID, PacketNumber: 1}
fmt.Printf("%#v\n", responsePublicHeader)
if err := responsePublicHeader.WritePublicHeader(&fullReply); err != nil {
return err
}
(&crypto.NullAEAD{}).Seal(0, &fullReply, fullReply.Bytes(), framesData.Bytes())
_, err := s.Connection.WriteToUDP(fullReply.Bytes(), s.CurrentRemoteAddr)
return err
}