forked from QUIC-Tracker/quic-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypto.go
157 lines (134 loc) · 4.62 KB
/
crypto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package quictracker
import (
"bytes"
"encoding/binary"
"github.com/mpiraux/pigotls"
)
var quicVersionSalt = []byte{ // See https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c,
0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0,
0x43, 0x90, 0xa8, 0x99,
}
const (
clientInitialLabel = "client in"
serverInitialLabel = "server in"
)
type EncryptionLevel int
const (
EncryptionLevelNone EncryptionLevel = iota
EncryptionLevelInitial
EncryptionLevel0RTT
EncryptionLevelHandshake
EncryptionLevel1RTT
EncryptionLevelBest // A special flag to indicate that the best encryption level available should be used
EncryptionLevelBestAppData // A special flag to indicate that the best app data encryption level available should be used
)
func (eL EncryptionLevel) String() string {
return encryptionLevelToString[eL]
}
var encryptionLevelToString = map[EncryptionLevel]string {
EncryptionLevelNone: "None",
EncryptionLevelInitial: "Initial",
EncryptionLevelHandshake: "Handshake",
EncryptionLevel0RTT: "0RTT",
EncryptionLevel1RTT: "1RTT",
EncryptionLevelBest: "Best",
EncryptionLevelBestAppData: "BestAppData",
}
var EncryptionLevelToPNSpace = map[EncryptionLevel]PNSpace {
EncryptionLevelNone: PNSpaceNoSpace,
EncryptionLevelInitial: PNSpaceInitial,
EncryptionLevelHandshake: PNSpaceHandshake,
EncryptionLevel0RTT: PNSpaceAppData,
EncryptionLevel1RTT: PNSpaceAppData,
EncryptionLevelBest: PNSpaceNoSpace,
EncryptionLevelBestAppData: PNSpaceAppData,
}
var EncryptionLevelToPacketType = map[EncryptionLevel]PacketType{
EncryptionLevelInitial: Initial,
EncryptionLevelHandshake: Handshake,
EncryptionLevel0RTT: ZeroRTTProtected,
EncryptionLevel1RTT: ShortHeaderPacket,
}
var packetTypeToEncryptionLevel = map[PacketType]EncryptionLevel{
Initial: EncryptionLevelInitial,
Retry: EncryptionLevelNone,
Handshake: EncryptionLevelHandshake,
ZeroRTTProtected: EncryptionLevel0RTT,
ShortHeaderPacket: EncryptionLevel1RTT,
}
var EpochToEncryptionLevel = map[pigotls.Epoch]EncryptionLevel {
pigotls.EpochInitial: EncryptionLevelInitial,
pigotls.Epoch0RTT: EncryptionLevel0RTT,
pigotls.EpochHandshake: EncryptionLevelHandshake,
pigotls.Epoch1RTT: EncryptionLevel1RTT,
}
type DirectionalEncryptionLevel struct {
EncryptionLevel EncryptionLevel
Read bool
Available bool
}
type CryptoState struct {
Read *pigotls.AEAD
Write *pigotls.AEAD
HeaderRead *pigotls.Cipher
HeaderWrite *pigotls.Cipher
}
type RetryPseudoPacket struct {
OriginalDestinationCID ConnectionID
UnusedByte byte
Version uint32
DestinationCID ConnectionID
SourceCID ConnectionID
RetryToken []byte
}
func (r *RetryPseudoPacket) Encode() []byte {
buf := bytes.NewBuffer(nil)
r.OriginalDestinationCID.WriteTo(buf)
buf.WriteByte(r.UnusedByte)
binary.Write(buf, binary.BigEndian, &r.Version)
r.DestinationCID.WriteTo(buf)
r.SourceCID.WriteTo(buf)
buf.Write(r.RetryToken)
return buf.Bytes()
}
func (s *CryptoState) InitRead(tls *pigotls.Connection, readSecret []byte) {
s.Read = tls.NewAEAD(readSecret, false)
s.HeaderRead = tls.NewCipher(tls.HkdfExpandLabel(readSecret, "hp", nil, tls.AEADKeySize(), pigotls.QuicBaseLabel))
}
func (s *CryptoState) InitWrite(tls *pigotls.Connection, writeSecret []byte) {
s.Write = tls.NewAEAD(writeSecret, true)
s.HeaderWrite = tls.NewCipher(tls.HkdfExpandLabel(writeSecret, "hp", nil, tls.AEADKeySize(), pigotls.QuicBaseLabel))
}
func NewInitialPacketProtection(conn *Connection) *CryptoState {
initialSecret := conn.Tls.HkdfExtract(quicVersionSalt, conn.DestinationCID)
readSecret := conn.Tls.HkdfExpandLabel(initialSecret, serverInitialLabel, nil, conn.Tls.HashDigestSize(), pigotls.BaseLabel)
writeSecret := conn.Tls.HkdfExpandLabel(initialSecret, clientInitialLabel, nil, conn.Tls.HashDigestSize(), pigotls.BaseLabel)
return NewProtectedCryptoState(conn.Tls, readSecret, writeSecret)
}
func NewProtectedCryptoState(tls *pigotls.Connection, readSecret []byte, writeSecret []byte) *CryptoState {
s := new(CryptoState)
if len(readSecret) > 0 {
s.InitRead(tls, readSecret)
}
if len(writeSecret) > 0 {
s.InitWrite(tls, writeSecret)
}
return s
}
func GetPacketSample(header Header, packetBytes []byte) ([]byte, int) {
var pnOffset int
sampleLength := 16
switch h := header.(type) {
case *LongHeader:
pnOffset = h.HeaderLength() - h.TruncatedPN().Length
case *ShortHeader:
pnOffset = 1 + len(h.DestinationCID)
}
sampleOffset := pnOffset + 4
if sampleOffset+sampleLength > len(packetBytes) {
// Packet is too short for sampling header protection, it must be padded first
return nil, pnOffset
}
return packetBytes[sampleOffset:sampleOffset+sampleLength], pnOffset
}