apply header encryption when packing and unpacking packets

This commit is contained in:
Marten Seemann 2018-12-14 21:34:00 +06:30
parent 5a68ba0a02
commit a638185f97
4 changed files with 748 additions and 626 deletions

View file

@ -421,7 +421,7 @@ func (p *packetPacker) writeAndSealPacket(
if err := header.Write(buffer, p.version); err != nil {
return nil, err
}
payloadStartIndex := buffer.Len()
payloadOffset := buffer.Len()
// write all frames but the last one
for _, frame := range frames[:len(frames)-1] {
@ -436,7 +436,7 @@ func (p *packetPacker) writeAndSealPacket(
sf.DataLenPresent = true
}
} else {
payloadLen := buffer.Len() - payloadStartIndex + int(lastFrame.Length(p.version))
payloadLen := buffer.Len() - payloadOffset + int(lastFrame.Length(p.version))
if paddingLen := 4 - int(header.PacketNumberLen) - payloadLen; paddingLen > 0 {
// Pad the packet such that packet number length + payload length is 4 bytes.
// This is needed to enable the peer to get a 16 byte sample for header protection.
@ -459,9 +459,16 @@ func (p *packetPacker) writeAndSealPacket(
}
raw = raw[0:buffer.Len()]
_ = sealer.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], header.PacketNumber, raw[:payloadStartIndex])
_ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], header.PacketNumber, raw[:payloadOffset])
raw = raw[0 : buffer.Len()+sealer.Overhead()]
pnOffset := payloadOffset - int(header.PacketNumberLen)
sealer.EncryptHeader(
raw[pnOffset+4:pnOffset+4+16],
&raw[0],
raw[pnOffset:payloadOffset],
)
num := p.pnManager.PopPacketNumber()
if num != header.PacketNumber {
return nil, errors.New("packetPacker BUG: Peeked and Popped packet numbers do not match")

View file

@ -25,7 +25,6 @@ var _ = Describe("Packet packer", func() {
initialStream *MockCryptoStream
handshakeStream *MockCryptoStream
sealingManager *MockSealingManager
sealer *mocks.MockSealer
pnManager *mockackhandler.MockSentPacketHandler
token []byte
)
@ -58,6 +57,7 @@ var _ = Describe("Packet packer", func() {
BeforeEach(func() {
rand.Seed(GinkgoRandomSeed())
version := protocol.VersionTLS
token = []byte("initial token")
mockSender := NewMockStreamSender(mockCtrl)
mockSender.EXPECT().onHasStreamData(gomock.Any()).AnyTimes()
initialStream = NewMockCryptoStream(mockCtrl)
@ -66,13 +66,6 @@ var _ = Describe("Packet packer", func() {
ackFramer = NewMockAckFrameSource(mockCtrl)
sealingManager = NewMockSealingManager(mockCtrl)
pnManager = mockackhandler.NewMockSentPacketHandler(mockCtrl)
sealer = mocks.NewMockSealer(mockCtrl)
sealer.EXPECT().Overhead().Return(7).AnyTimes()
sealer.EXPECT().Seal(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(dst, src []byte, pn protocol.PacketNumber, associatedData []byte) []byte {
return append(src, bytes.Repeat([]byte{0}, sealer.Overhead())...)
}).AnyTimes()
token = []byte("initial token")
packer = newPacketPacker(
protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
@ -156,6 +149,60 @@ var _ = Describe("Packet packer", func() {
})
})
Context("encrypting packets", func() {
It("encrypts a packet", func() {
initialStream.EXPECT().HasData()
handshakeStream.EXPECT().HasData()
pnManager.EXPECT().PeekPacketNumber().Return(protocol.PacketNumber(0x1337), protocol.PacketNumberLen2)
pnManager.EXPECT().PopPacketNumber().Return(protocol.PacketNumber(0x1337))
sealer := mocks.NewMockSealer(mockCtrl)
sealer.EXPECT().Overhead().Return(4).AnyTimes()
var hdrRaw []byte
gomock.InOrder(
sealer.EXPECT().Seal(gomock.Any(), gomock.Any(), protocol.PacketNumber(0x1337), gomock.Any()).DoAndReturn(func(_, src []byte, _ protocol.PacketNumber, aad []byte) []byte {
hdrRaw = append([]byte{}, aad...)
return append(src, []byte{0xde, 0xca, 0xfb, 0xad}...)
}),
sealer.EXPECT().EncryptHeader(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(sample []byte, firstByte *byte, pnBytes []byte) {
Expect(firstByte).To(Equal(&hdrRaw[0]))
Expect(pnBytes).To(Equal(hdrRaw[len(hdrRaw)-2:]))
*firstByte ^= 0xff // invert the first byte
// invert the packet number bytes
for i := range pnBytes {
pnBytes[i] ^= 0xff
}
}),
)
sealingManager.EXPECT().GetSealer().Return(protocol.Encryption1RTT, sealer)
ackFramer.EXPECT().GetAckFrame()
expectAppendControlFrames()
f := &wire.StreamFrame{Data: []byte{0xde, 0xca, 0xfb, 0xad}}
expectAppendStreamFrames(f)
p, err := packer.PackPacket()
Expect(err).ToNot(HaveOccurred())
Expect(p).ToNot(BeNil())
Expect(p.frames).To(Equal([]wire.Frame{f}))
hdrRawEncrypted := append([]byte{}, hdrRaw...)
hdrRawEncrypted[0] ^= 0xff
hdrRawEncrypted[len(hdrRaw)-2] ^= 0xff
hdrRawEncrypted[len(hdrRaw)-1] ^= 0xff
Expect(p.raw[0:len(hdrRaw)]).To(Equal(hdrRawEncrypted))
Expect(p.raw[len(p.raw)-4:]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad}))
})
})
Context("packing packets", func() {
var sealer *mocks.MockSealer
BeforeEach(func() {
sealer = mocks.NewMockSealer(mockCtrl)
sealer.EXPECT().Overhead().Return(7).AnyTimes()
sealer.EXPECT().EncryptHeader(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
sealer.EXPECT().Seal(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(dst, src []byte, pn protocol.PacketNumber, associatedData []byte) []byte {
return append(src, bytes.Repeat([]byte{0}, sealer.Overhead())...)
}).AnyTimes()
})
Context("packing normal packets", func() {
BeforeEach(func() {
initialStream.EXPECT().HasData().AnyTimes()
@ -824,4 +871,5 @@ var _ = Describe("Packet packer", func() {
})
})
})
})
})

View file

@ -47,23 +47,6 @@ func (u *packetUnpacker) Unpack(hdr *wire.Header, data []byte) (*unpackedPacket,
// TODO(#1312): implement parsing of compound packets
}
extHdr, err := hdr.ParseExtended(r, u.version)
if err != nil {
return nil, fmt.Errorf("error parsing extended header: %s", err)
}
extHdr.Raw = data[:len(data)-r.Len()]
data = data[len(data)-r.Len():]
pn := protocol.DecodePacketNumber(
extHdr.PacketNumberLen,
u.largestRcvdPacketNumber,
extHdr.PacketNumber,
)
buf := *getPacketBuffer()
buf = buf[:0]
defer putPacketBuffer(&buf)
var encLevel protocol.EncryptionLevel
switch hdr.Type {
case protocol.PacketTypeInitial:
@ -80,6 +63,40 @@ func (u *packetUnpacker) Unpack(hdr *wire.Header, data []byte) (*unpackedPacket,
if err != nil {
return nil, qerr.Error(qerr.DecryptionFailure, err.Error())
}
hdrLen := int(hdr.ParsedLen())
// The packet number can be up to 4 bytes long, but we won't know the length until we decrypt it.
// 1. save a copy of the 4 bytes
origPNBytes := make([]byte, 4)
copy(origPNBytes, data[hdrLen:hdrLen+4])
// 2. decrypt the header, assuming a 4 byte packet number
opener.DecryptHeader(
data[hdrLen+4:hdrLen+4+16],
&data[0],
data[hdrLen:hdrLen+4],
)
// 3. parse the header (and learn the actual length of the packet number)
extHdr, err := hdr.ParseExtended(r, u.version)
if err != nil {
return nil, fmt.Errorf("error parsing extended header: %s", err)
}
extHdr.Raw = data[:hdrLen+int(extHdr.PacketNumberLen)]
// 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier
if extHdr.PacketNumberLen != protocol.PacketNumberLen4 {
copy(data[hdrLen+int(extHdr.PacketNumberLen):hdrLen+4], origPNBytes[int(extHdr.PacketNumberLen):])
}
data = data[hdrLen+int(extHdr.PacketNumberLen):]
pn := protocol.DecodePacketNumber(
extHdr.PacketNumberLen,
u.largestRcvdPacketNumber,
extHdr.PacketNumber,
)
buf := *getPacketBuffer()
buf = buf[:0]
defer putPacketBuffer(&buf)
decrypted, err := opener.Open(buf, data, pn, extHdr.Raw)
if err != nil {
return nil, qerr.Error(qerr.DecryptionFailure, err.Error())

View file

@ -46,6 +46,7 @@ var _ = Describe("Packet Unpacker", func() {
// return an empty (unencrypted) payload
opener := mocks.NewMockOpener(mockCtrl)
cs.EXPECT().GetOpener(protocol.Encryption1RTT).Return(opener, nil)
opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
opener.EXPECT().Open(gomock.Any(), []byte("foobar"), extHdr.PacketNumber, hdrRaw).Return([]byte{}, nil)
_, err := unpacker.Unpack(hdr, data)
Expect(err).To(MatchError(qerr.MissingPayload))
@ -66,6 +67,7 @@ var _ = Describe("Packet Unpacker", func() {
hdr, hdrRaw := getHeader(extHdr)
opener := mocks.NewMockOpener(mockCtrl)
cs.EXPECT().GetOpener(protocol.EncryptionInitial).Return(opener, nil)
opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
opener.EXPECT().Open(gomock.Any(), []byte("foobar"), extHdr.PacketNumber, hdrRaw).Return([]byte{0}, nil)
packet, err := unpacker.Unpack(hdr, append(hdrRaw, []byte("foobar")...))
Expect(err).ToNot(HaveOccurred())
@ -106,6 +108,7 @@ var _ = Describe("Packet Unpacker", func() {
data := append(hdrRaw, make([]byte, payloadLen)...)
opener := mocks.NewMockOpener(mockCtrl)
cs.EXPECT().GetOpener(protocol.EncryptionHandshake).Return(opener, nil)
opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
opener.EXPECT().Open(gomock.Any(), gomock.Any(), extHdr.PacketNumber, hdrRaw).DoAndReturn(func(_, payload []byte, _ protocol.PacketNumber, _ []byte) ([]byte, error) {
Expect(payload).To(HaveLen(payloadLen))
return []byte{0}, nil
@ -141,11 +144,55 @@ var _ = Describe("Packet Unpacker", func() {
hdr, hdrRaw := getHeader(extHdr)
opener := mocks.NewMockOpener(mockCtrl)
cs.EXPECT().GetOpener(protocol.EncryptionHandshake).Return(opener, nil)
opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test err"))
_, err := unpacker.Unpack(hdr, hdrRaw)
Expect(err).To(MatchError(qerr.Error(qerr.DecryptionFailure, "test err")))
})
It("decrypts the header", func() {
extHdr := &wire.ExtendedHeader{
Header: wire.Header{
IsLongHeader: true,
Type: protocol.PacketTypeHandshake,
Length: 3, // packet number len
DestConnectionID: connID,
Version: version,
},
PacketNumber: 0x1337,
PacketNumberLen: 2,
}
hdr, hdrRaw := getHeader(extHdr)
origHdrRaw := append([]byte{}, hdrRaw...) // save a copy of the header
firstHdrByte := hdrRaw[0]
hdrRaw[0] ^= 0xff // invert the first byte
hdrRaw[len(hdrRaw)-2] ^= 0xff // invert the packet number
hdrRaw[len(hdrRaw)-1] ^= 0xff // invert the packet number
Expect(hdrRaw[0]).ToNot(Equal(firstHdrByte))
opener := mocks.NewMockOpener(mockCtrl)
cs.EXPECT().GetOpener(protocol.EncryptionHandshake).Return(opener, nil)
gomock.InOrder(
// we're using a 2 byte packet number, so the sample starts at the 3rd payload byte
opener.EXPECT().DecryptHeader(
[]byte{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
&hdrRaw[0],
append(hdrRaw[len(hdrRaw)-2:], []byte{1, 2}...)).Do(func(_ []byte, firstByte *byte, pnBytes []byte) {
*firstByte ^= 0xff // invert the first byte back
for i := range pnBytes {
pnBytes[i] ^= 0xff // invert the packet number bytes
}
}),
opener.EXPECT().Open(gomock.Any(), gomock.Any(), protocol.PacketNumber(0x1337), origHdrRaw).Return([]byte{0}, nil),
)
data := hdrRaw
for i := 1; i <= 100; i++ {
data = append(data, uint8(i))
}
packet, err := unpacker.Unpack(hdr, data)
Expect(err).ToNot(HaveOccurred())
Expect(packet.packetNumber).To(Equal(protocol.PacketNumber(0x1337)))
})
It("decodes the packet number", func() {
firstHdr := &wire.ExtendedHeader{
Header: wire.Header{DestConnectionID: connID},
@ -154,6 +201,7 @@ var _ = Describe("Packet Unpacker", func() {
}
opener := mocks.NewMockOpener(mockCtrl)
cs.EXPECT().GetOpener(protocol.Encryption1RTT).Return(opener, nil).Times(2)
opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
opener.EXPECT().Open(gomock.Any(), gomock.Any(), firstHdr.PacketNumber, gomock.Any()).Return([]byte{0}, nil)
packet, err := unpacker.Unpack(getHeader(firstHdr))
Expect(err).ToNot(HaveOccurred())
@ -165,6 +213,7 @@ var _ = Describe("Packet Unpacker", func() {
PacketNumberLen: 1,
}
// expect the call with the decoded packet number
opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
opener.EXPECT().Open(gomock.Any(), gomock.Any(), protocol.PacketNumber(0x1338), gomock.Any()).Return([]byte{0}, nil)
packet, err = unpacker.Unpack(getHeader(secondHdr))
Expect(err).ToNot(HaveOccurred())
@ -182,6 +231,7 @@ var _ = Describe("Packet Unpacker", func() {
(&wire.DataBlockedFrame{}).Write(buf, protocol.VersionWhatever)
hdr, hdrRaw := getHeader(extHdr)
opener := mocks.NewMockOpener(mockCtrl)
opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
cs.EXPECT().GetOpener(protocol.Encryption1RTT).Return(opener, nil)
opener.EXPECT().Open(gomock.Any(), gomock.Any(), extHdr.PacketNumber, hdrRaw).Return(buf.Bytes(), nil)
packet, err := unpacker.Unpack(hdr, append(hdrRaw, buf.Bytes()...))