network-core 0.5.4__tar.gz → 0.5.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- network_core-0.5.6/MANIFEST.in +1 -0
- {network_core-0.5.4 → network_core-0.5.6}/PKG-INFO +1 -1
- network_core-0.5.6/go_src/commons/constants.go +31 -0
- network_core-0.5.6/go_src/commons/data.go +84 -0
- network_core-0.5.6/go_src/commons/flowOps.go +45 -0
- network_core-0.5.6/go_src/commons/packetOps.go +67 -0
- network_core-0.5.6/go_src/commons/quicCrypto.go +124 -0
- network_core-0.5.6/go_src/commons/quicParser.go +277 -0
- network_core-0.5.6/go_src/commons/savingMethods.go +41 -0
- network_core-0.5.6/go_src/commons/tlsParser.go +238 -0
- network_core-0.5.6/go_src/commons/utils.go +155 -0
- network_core-0.5.6/go_src/core/driver.go +146 -0
- network_core-0.5.6/go_src/core/flowGatherer.go +201 -0
- network_core-0.5.6/go_src/core/saving.go +96 -0
- network_core-0.5.6/go_src/core/testing.go +22 -0
- network_core-0.5.6/go_src/core/utils.go +1 -0
- network_core-0.5.6/go_src/go.mod +9 -0
- network_core-0.5.6/go_src/go.sum +18 -0
- network_core-0.5.6/go_src/main.go +18 -0
- network_core-0.5.6/network_core/bin/process_pcap +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/PKG-INFO +1 -1
- {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/SOURCES.txt +19 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/top_level.txt +1 -0
- {network_core-0.5.4 → network_core-0.5.6}/pyproject.toml +1 -1
- network_core-0.5.6/setup.py +33 -0
- network_core-0.5.4/network_core/bin/process_pcap +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/__init__.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/analysis.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/conn.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/connOps.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/dataModels.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/http/__init__.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/__init__.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/helpers.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/parser.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/pdh.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/pdml_parser.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpIO.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/pcap.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/__init__.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/clientHello.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/constants.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/crypto.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/helpers.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/parsers.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/__init__.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/csvIO.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/dt.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/jsonIO.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/pcapIO.py +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/dependency_links.txt +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/requires.txt +0 -0
- {network_core-0.5.4 → network_core-0.5.6}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
recursive-include go_src *
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package commons
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/hex"
|
|
5
|
+
"time"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
var PACKET_BATCH_WRITE_SIZE int = 8000
|
|
9
|
+
var TIME_BUFFER time.Duration = 5 * time.Minute
|
|
10
|
+
|
|
11
|
+
var MIN_FLOW_DURATION time.Duration = 2 * time.Second
|
|
12
|
+
var MIN_FLOW_PACKETS int = 10
|
|
13
|
+
|
|
14
|
+
var SNI_MAX_PACKETS int = 20
|
|
15
|
+
|
|
16
|
+
var TCP_PROTO int = 6
|
|
17
|
+
var UDP_PROTO int = 17
|
|
18
|
+
|
|
19
|
+
// SNI stuff
|
|
20
|
+
|
|
21
|
+
var QUIC_TYPE_MAP = map[int]string{
|
|
22
|
+
0: "initial",
|
|
23
|
+
1: "0-RTT",
|
|
24
|
+
2: "handshake",
|
|
25
|
+
3: "retry",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var QuicSalt, _ = hex.DecodeString("38762cf7f55934b34d179ae6a4c80cadccbb7f0a")
|
|
29
|
+
var TLS_SNI_EXT_TYPE int = 0
|
|
30
|
+
var TLS_ALPN_EXT_TYPE int = 16
|
|
31
|
+
var QUIC_INITIAL_TYPE int = 0
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package commons
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"time"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
type Flow struct {
|
|
9
|
+
FlowID int
|
|
10
|
+
FiveTuple *FiveTuple
|
|
11
|
+
StartTime time.Time
|
|
12
|
+
EndTime time.Time
|
|
13
|
+
Packets []PacketInfo
|
|
14
|
+
SNI string
|
|
15
|
+
ALPN string // first ALPN protocol from TLS ClientHello, or "nan"
|
|
16
|
+
Closed bool
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type PacketInfo struct {
|
|
20
|
+
PacketLength int
|
|
21
|
+
Timestamp time.Time
|
|
22
|
+
Direction uint8
|
|
23
|
+
Transport_payload []byte
|
|
24
|
+
TCP_SEQ uint32
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type FiveTuple struct {
|
|
28
|
+
ClientIP string
|
|
29
|
+
ServerIP string
|
|
30
|
+
ClientPort uint16
|
|
31
|
+
ServerPort uint16
|
|
32
|
+
Protocol uint8
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func (ft *FiveTuple) String() string {
|
|
36
|
+
return fmt.Sprintf("%s:%d->%s:%d->(Protocol: %d)",
|
|
37
|
+
ft.ClientIP, ft.ClientPort, ft.ServerIP, ft.ServerPort, ft.Protocol)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type SavePacket struct {
|
|
41
|
+
FlowId int
|
|
42
|
+
PacketLength int
|
|
43
|
+
Timestamp time.Time
|
|
44
|
+
Direction uint8
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type SaveFlow struct {
|
|
48
|
+
FlowId int
|
|
49
|
+
FiveTuple *FiveTuple
|
|
50
|
+
StartTime time.Time
|
|
51
|
+
EndTime time.Time
|
|
52
|
+
SNI string
|
|
53
|
+
ALPN string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type SaveJob struct {
|
|
57
|
+
Flows []*Flow
|
|
58
|
+
SaveFlowData bool
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// SNIInfo holds the resolved SNI and ALPN for a 5-tuple; used for carry-forward
|
|
62
|
+
// across session-resumed flows that share the same TCP socket.
|
|
63
|
+
type SNIInfo struct {
|
|
64
|
+
SNI string
|
|
65
|
+
ALPN string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// SNI related
|
|
69
|
+
|
|
70
|
+
type TLS_CH_Parsed struct {
|
|
71
|
+
Valid bool
|
|
72
|
+
SNIs []string
|
|
73
|
+
ALPN []string // e.g. ["h2"] or ["http/1.1"]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type QUIC_CRYPTO_Frame struct {
|
|
77
|
+
offset int
|
|
78
|
+
payload []byte
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type QUIC_LH_PARSED struct {
|
|
82
|
+
valid bool
|
|
83
|
+
frames []QUIC_CRYPTO_Frame
|
|
84
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package commons
|
|
2
|
+
|
|
3
|
+
func alpnFirst(alpns []string) string {
|
|
4
|
+
if len(alpns) > 0 {
|
|
5
|
+
return alpns[0]
|
|
6
|
+
}
|
|
7
|
+
return "nan"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
func (flow *Flow) Get_Set_SNI() {
|
|
11
|
+
|
|
12
|
+
if flow.FiveTuple.Protocol == uint8(TCP_PROTO) {
|
|
13
|
+
// I will try both direction
|
|
14
|
+
|
|
15
|
+
tls_sni := TLS_sni_extractor(get_transport_packets_of_direction(flow, 0, SNI_MAX_PACKETS))
|
|
16
|
+
if !tls_sni.Valid {
|
|
17
|
+
tls_sni = TLS_sni_extractor(get_transport_packets_of_direction(flow, 1, SNI_MAX_PACKETS))
|
|
18
|
+
}
|
|
19
|
+
if tls_sni.Valid && len(tls_sni.SNIs) > 0 {
|
|
20
|
+
flow.SNI = tls_sni.SNIs[0]
|
|
21
|
+
} else {
|
|
22
|
+
flow.SNI = "nan"
|
|
23
|
+
}
|
|
24
|
+
if tls_sni.Valid {
|
|
25
|
+
flow.ALPN = alpnFirst(tls_sni.ALPN)
|
|
26
|
+
} else {
|
|
27
|
+
flow.ALPN = "nan"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
} else if flow.FiveTuple.Protocol == uint8(UDP_PROTO) {
|
|
31
|
+
|
|
32
|
+
quic_sni := QUIC_SNI_extractor(get_transport_packets_of_direction(flow, 0, SNI_MAX_PACKETS))
|
|
33
|
+
if !quic_sni.Valid {
|
|
34
|
+
quic_sni = QUIC_SNI_extractor(get_transport_packets_of_direction(flow, 1, SNI_MAX_PACKETS))
|
|
35
|
+
}
|
|
36
|
+
if quic_sni.Valid {
|
|
37
|
+
flow.SNI = quic_sni.SNIs[0]
|
|
38
|
+
} else {
|
|
39
|
+
flow.SNI = "nan"
|
|
40
|
+
}
|
|
41
|
+
flow.ALPN = "nan" // QUIC path does not extract ALPN yet
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
package commons
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"github.com/google/gopacket"
|
|
5
|
+
"github.com/google/gopacket/layers"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
func GetFiveTupleFromPacket(packet *gopacket.Packet) (FiveTuple, bool) {
|
|
9
|
+
var clientIP, serverIP string
|
|
10
|
+
// Check for IPv4 layer
|
|
11
|
+
|
|
12
|
+
ipv4Layer := (*packet).Layer(layers.LayerTypeIPv4)
|
|
13
|
+
if ipv4Layer != nil {
|
|
14
|
+
ipv4, _ := ipv4Layer.(*layers.IPv4)
|
|
15
|
+
clientIP = ipv4.SrcIP.String()
|
|
16
|
+
serverIP = ipv4.DstIP.String()
|
|
17
|
+
} else {
|
|
18
|
+
// Check for IPv6 layer
|
|
19
|
+
ipv6Layer := (*packet).Layer(layers.LayerTypeIPv6)
|
|
20
|
+
if ipv6Layer != nil {
|
|
21
|
+
ipv6, _ := ipv6Layer.(*layers.IPv6)
|
|
22
|
+
clientIP = ipv6.SrcIP.String()
|
|
23
|
+
serverIP = ipv6.DstIP.String()
|
|
24
|
+
} else {
|
|
25
|
+
// Neither IPv4 nor IPv6 present, return false
|
|
26
|
+
return FiveTuple{}, false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Extract TCP/UDP information
|
|
31
|
+
var srcPort, dstPort uint16
|
|
32
|
+
var protocol uint8
|
|
33
|
+
if tcpLayer := (*packet).Layer(layers.LayerTypeTCP); tcpLayer != nil {
|
|
34
|
+
tcp, _ := tcpLayer.(*layers.TCP)
|
|
35
|
+
srcPort = uint16(tcp.SrcPort)
|
|
36
|
+
dstPort = uint16(tcp.DstPort)
|
|
37
|
+
protocol = 6 // TCP
|
|
38
|
+
} else if udpLayer := (*packet).Layer(layers.LayerTypeUDP); udpLayer != nil {
|
|
39
|
+
udp, _ := udpLayer.(*layers.UDP)
|
|
40
|
+
srcPort = uint16(udp.SrcPort)
|
|
41
|
+
dstPort = uint16(udp.DstPort)
|
|
42
|
+
protocol = 17 // UDP
|
|
43
|
+
} else {
|
|
44
|
+
return FiveTuple{}, false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Construct tuple
|
|
48
|
+
tp := FiveTuple{
|
|
49
|
+
ClientIP: clientIP,
|
|
50
|
+
ServerIP: serverIP,
|
|
51
|
+
ClientPort: srcPort,
|
|
52
|
+
ServerPort: dstPort,
|
|
53
|
+
Protocol: protocol,
|
|
54
|
+
}
|
|
55
|
+
return tp, true
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func GetRevFromFiveTuple(five_tuple FiveTuple) FiveTuple {
|
|
60
|
+
return FiveTuple{
|
|
61
|
+
ClientIP: five_tuple.ServerIP,
|
|
62
|
+
ServerIP: five_tuple.ClientIP,
|
|
63
|
+
ClientPort: five_tuple.ServerPort,
|
|
64
|
+
ServerPort: five_tuple.ClientPort,
|
|
65
|
+
Protocol: five_tuple.Protocol,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
package commons
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"crypto/aes"
|
|
5
|
+
"crypto/cipher"
|
|
6
|
+
"crypto/hmac"
|
|
7
|
+
"crypto/sha256"
|
|
8
|
+
"encoding/binary"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func hkdfExtract(salt, ikm []byte) []byte {
|
|
12
|
+
mac := hmac.New(sha256.New, salt)
|
|
13
|
+
mac.Write(ikm)
|
|
14
|
+
return mac.Sum(nil)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func hkdfExpand(secret, info []byte, length int) []byte {
|
|
18
|
+
var result []byte
|
|
19
|
+
var t []byte
|
|
20
|
+
counter := byte(1)
|
|
21
|
+
|
|
22
|
+
for len(result) < length {
|
|
23
|
+
h := hmac.New(sha256.New, secret)
|
|
24
|
+
h.Write(t)
|
|
25
|
+
h.Write(info)
|
|
26
|
+
h.Write([]byte{counter})
|
|
27
|
+
t = h.Sum(nil)
|
|
28
|
+
result = append(result, t...)
|
|
29
|
+
counter++
|
|
30
|
+
}
|
|
31
|
+
return result[:length]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
func hkdfExpandLabel(secret []byte, label string, length int) []byte {
|
|
35
|
+
fullLabel := append([]byte("tls13 "), []byte(label)...)
|
|
36
|
+
|
|
37
|
+
info := make([]byte, 0, 2+1+len(fullLabel)+1)
|
|
38
|
+
tmp := make([]byte, 2)
|
|
39
|
+
binary.BigEndian.PutUint16(tmp, uint16(length))
|
|
40
|
+
info = append(info, tmp...)
|
|
41
|
+
info = append(info, byte(len(fullLabel)))
|
|
42
|
+
info = append(info, fullLabel...)
|
|
43
|
+
info = append(info, 0x00)
|
|
44
|
+
|
|
45
|
+
return hkdfExpand(secret, info, length)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func getSecretsAndKeys(dcid []byte, salt []byte) (hpKey, clientKey, clientIV []byte) {
|
|
49
|
+
initialSecret := hkdfExtract(salt, dcid)
|
|
50
|
+
clientSecret := hkdfExpandLabel(initialSecret, "client in", 32)
|
|
51
|
+
|
|
52
|
+
hpKey = hkdfExpandLabel(clientSecret, "quic hp", 16)
|
|
53
|
+
clientKey = hkdfExpandLabel(clientSecret, "quic key", 16)
|
|
54
|
+
clientIV = hkdfExpandLabel(clientSecret, "quic iv", 12)
|
|
55
|
+
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func removeHeaderProtection(payload []byte, pnOffset int, hpKey []byte) (packetNumber uint64, pnLen int, unprotected []byte, ok bool) {
|
|
60
|
+
if pnOffset+4+16 > len(payload) {
|
|
61
|
+
return 0, 0, nil, false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
sample := payload[pnOffset+4 : pnOffset+4+16]
|
|
65
|
+
|
|
66
|
+
block, err := aes.NewCipher(hpKey)
|
|
67
|
+
if err != nil {
|
|
68
|
+
return 0, 0, nil, false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
mask := make([]byte, 16)
|
|
72
|
+
block.Encrypt(mask, sample)
|
|
73
|
+
|
|
74
|
+
unprotected = make([]byte, len(payload))
|
|
75
|
+
copy(unprotected, payload)
|
|
76
|
+
|
|
77
|
+
unprotected[0] ^= mask[0] & 0x0f
|
|
78
|
+
pnLen = int(unprotected[0]&0x03) + 1
|
|
79
|
+
|
|
80
|
+
for i := 0; i < pnLen; i++ {
|
|
81
|
+
unprotected[pnOffset+i] ^= mask[i+1]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
var pn uint64
|
|
85
|
+
for i := 0; i < pnLen; i++ {
|
|
86
|
+
pn = (pn << 8) | uint64(unprotected[pnOffset+i])
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return pn, pnLen, unprotected, true
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func decryptQUICPayload(
|
|
93
|
+
encryptedPayload []byte,
|
|
94
|
+
packetNumber uint64,
|
|
95
|
+
clientKey []byte,
|
|
96
|
+
clientIV []byte,
|
|
97
|
+
associatedData []byte,
|
|
98
|
+
) ([]byte, bool) {
|
|
99
|
+
|
|
100
|
+
block, err := aes.NewCipher(clientKey)
|
|
101
|
+
if err != nil {
|
|
102
|
+
return nil, false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
aead, err := cipher.NewGCM(block)
|
|
106
|
+
if err != nil {
|
|
107
|
+
return nil, false
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
pnBytes := make([]byte, 12)
|
|
111
|
+
binary.BigEndian.PutUint64(pnBytes[4:], packetNumber)
|
|
112
|
+
|
|
113
|
+
nonce := make([]byte, 12)
|
|
114
|
+
for i := 0; i < 12; i++ {
|
|
115
|
+
nonce[i] = clientIV[i] ^ pnBytes[i]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
plaintext, err := aead.Open(nil, nonce, encryptedPayload, associatedData)
|
|
119
|
+
if err != nil {
|
|
120
|
+
return nil, false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return plaintext, true
|
|
124
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
package commons
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"slices"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
|
|
9
|
+
def is_quic_long_header(payload):
|
|
10
|
+
"""
|
|
11
|
+
Checks if the given payload is a QUIC long header packet.
|
|
12
|
+
1. It first checks if the payload looks like QUIC using looks_like_quic
|
|
13
|
+
2. Then it checks if the first byte indicates a long header (most significant bit is 1)
|
|
14
|
+
3. Finally, it checks if the version field (bytes 1-4) is non-zero.
|
|
15
|
+
Returns True if all conditions are met, False otherwise.
|
|
16
|
+
"""
|
|
17
|
+
if looks_like_quic(payload) == False:
|
|
18
|
+
return False
|
|
19
|
+
if len(payload) < 6:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
if (payload[0] & 0x80) == 0:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
version_id = int.from_bytes(payload[1:5], byteorder="big")
|
|
26
|
+
if version_id == 0:
|
|
27
|
+
return False
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
func is_quic_long_header(payload []byte) bool {
|
|
34
|
+
/*
|
|
35
|
+
1 1 T T P P N N
|
|
36
|
+
| | | | | | | |
|
|
37
|
+
| | | | | | +-+- Packet Number Length
|
|
38
|
+
| | | | +-+-+--- Packet Type (Initial/0-RTT/Handshake/Retry)
|
|
39
|
+
| | +-+--------- Reserved bits
|
|
40
|
+
| +------------- Fixed bit (must be 1)
|
|
41
|
+
+--------------- Header Form (1 = Long header)
|
|
42
|
+
|
|
43
|
+
*/
|
|
44
|
+
if len(payload) < 6 {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (payload[0] & 0x40) == 0 {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (payload[0] & 0x80) == 0 {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
version_id, ok := readUint32(payload, 1)
|
|
57
|
+
if !ok {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
if version_id == 0 {
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func parse_quic_plain_text(plainText []byte) ([]QUIC_CRYPTO_Frame, bool) {
|
|
68
|
+
parseIndex := 0
|
|
69
|
+
frames := make([]QUIC_CRYPTO_Frame, 0)
|
|
70
|
+
|
|
71
|
+
for parseIndex < len(plainText) {
|
|
72
|
+
frameType, newIndex, ok := read_var_int(plainText, parseIndex)
|
|
73
|
+
if !ok {
|
|
74
|
+
return frames, false
|
|
75
|
+
}
|
|
76
|
+
parseIndex = newIndex
|
|
77
|
+
|
|
78
|
+
switch frameType {
|
|
79
|
+
|
|
80
|
+
case 0x00: // PADDING
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
case 0x01: // PING
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
case 0x06: // CRYPTO
|
|
87
|
+
offset, parseIndex2, ok := read_var_int(plainText, parseIndex)
|
|
88
|
+
if !ok {
|
|
89
|
+
return frames, false
|
|
90
|
+
}
|
|
91
|
+
parseIndex = parseIndex2
|
|
92
|
+
|
|
93
|
+
length, parseIndex2, ok := read_var_int(plainText, parseIndex)
|
|
94
|
+
if !ok {
|
|
95
|
+
return frames, false
|
|
96
|
+
}
|
|
97
|
+
parseIndex = parseIndex2
|
|
98
|
+
|
|
99
|
+
if parseIndex+int(length) > len(plainText) {
|
|
100
|
+
return frames, false
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
frames = append(frames, QUIC_CRYPTO_Frame{
|
|
104
|
+
offset: int(offset),
|
|
105
|
+
payload: plainText[parseIndex : parseIndex+int(length)],
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
parseIndex += int(length)
|
|
109
|
+
|
|
110
|
+
default:
|
|
111
|
+
// Stop parsing but keep what we found
|
|
112
|
+
return frames, len(frames) > 0
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return frames, len(frames) > 0
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func quic_long_header_parser(payload []byte) QUIC_LH_PARSED {
|
|
120
|
+
if !is_quic_long_header(payload) {
|
|
121
|
+
return QUIC_LH_PARSED{valid: false}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
parse_index := 0
|
|
125
|
+
|
|
126
|
+
// checking if the packet is initial
|
|
127
|
+
if int((payload[parse_index]>>4)&0x03) != QUIC_INITIAL_TYPE {
|
|
128
|
+
return QUIC_LH_PARSED{valid: false}
|
|
129
|
+
}
|
|
130
|
+
parse_index += 1
|
|
131
|
+
|
|
132
|
+
// version
|
|
133
|
+
_, ok := readUint32(payload, parse_index)
|
|
134
|
+
if !ok {
|
|
135
|
+
return QUIC_LH_PARSED{valid: false}
|
|
136
|
+
}
|
|
137
|
+
parse_index += 4
|
|
138
|
+
|
|
139
|
+
// dcid
|
|
140
|
+
dcid_len, ok := readUint8(payload, parse_index)
|
|
141
|
+
if !ok {
|
|
142
|
+
return QUIC_LH_PARSED{valid: false}
|
|
143
|
+
}
|
|
144
|
+
parse_index += 1
|
|
145
|
+
if parse_index+dcid_len > len(payload) {
|
|
146
|
+
return QUIC_LH_PARSED{valid: false}
|
|
147
|
+
}
|
|
148
|
+
dcid := payload[parse_index : parse_index+dcid_len]
|
|
149
|
+
parse_index += dcid_len
|
|
150
|
+
|
|
151
|
+
// scid
|
|
152
|
+
scid_len, ok := readUint8(payload, parse_index)
|
|
153
|
+
if !ok {
|
|
154
|
+
return QUIC_LH_PARSED{valid: false}
|
|
155
|
+
}
|
|
156
|
+
parse_index += 1
|
|
157
|
+
parse_index += scid_len
|
|
158
|
+
|
|
159
|
+
if parse_index >= len(payload) {
|
|
160
|
+
return QUIC_LH_PARSED{valid: false}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// token
|
|
164
|
+
token_len, parse_index, ok := read_var_int(payload, parse_index)
|
|
165
|
+
if !ok {
|
|
166
|
+
return QUIC_LH_PARSED{valid: false}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
parse_index += int(token_len)
|
|
170
|
+
if parse_index >= len(payload) {
|
|
171
|
+
return QUIC_LH_PARSED{valid: false}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// length
|
|
175
|
+
payload_length, parse_index, ok := read_var_int(payload, parse_index)
|
|
176
|
+
if !ok {
|
|
177
|
+
return QUIC_LH_PARSED{valid: false}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if parse_index+int(payload_length) > len(payload) { // not >= but >
|
|
181
|
+
return QUIC_LH_PARSED{valid: false}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
# right now parse_index is pointing to the start of packet number
|
|
186
|
+
# I first need to unprotect the header to get the packet number length
|
|
187
|
+
# Start header protection removal
|
|
188
|
+
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
hp_key, client_key, client_iv := getSecretsAndKeys(dcid, QuicSalt)
|
|
192
|
+
|
|
193
|
+
packet_number, pn_len, hp_less_payload, ok := removeHeaderProtection(payload, parse_index, hp_key)
|
|
194
|
+
if !ok {
|
|
195
|
+
return QUIC_LH_PARSED{valid: false}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// moving past packet length
|
|
199
|
+
parse_index += pn_len
|
|
200
|
+
|
|
201
|
+
if int(payload_length) < pn_len {
|
|
202
|
+
return QUIC_LH_PARSED{valid: false}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
plain_text, ok := decryptQUICPayload(payload[parse_index:parse_index+int(payload_length)-pn_len], packet_number, client_key, client_iv, hp_less_payload[:parse_index])
|
|
206
|
+
if !ok {
|
|
207
|
+
return QUIC_LH_PARSED{valid: false}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
frames, ok := parse_quic_plain_text(plain_text)
|
|
211
|
+
if !ok {
|
|
212
|
+
return QUIC_LH_PARSED{valid: false}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return QUIC_LH_PARSED{valid: true, frames: frames}
|
|
216
|
+
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
func quicFrameStitcher(packets []PacketInfo) []byte {
|
|
220
|
+
data := make(map[int]byte)
|
|
221
|
+
|
|
222
|
+
for _, pkt := range packets {
|
|
223
|
+
parsed := quic_long_header_parser(pkt.Transport_payload)
|
|
224
|
+
if !parsed.valid {
|
|
225
|
+
continue
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
for _, f := range parsed.frames {
|
|
229
|
+
for i := 0; i < len(f.payload); i++ {
|
|
230
|
+
pos := f.offset + i
|
|
231
|
+
if _, exists := data[pos]; !exists { // ignore retransmits
|
|
232
|
+
data[pos] = f.payload[i]
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if len(data) == 0 {
|
|
239
|
+
return nil
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
keys := make([]int, 0, len(data))
|
|
243
|
+
for k := range data {
|
|
244
|
+
keys = append(keys, k)
|
|
245
|
+
}
|
|
246
|
+
slices.Sort(keys)
|
|
247
|
+
|
|
248
|
+
mn := keys[0]
|
|
249
|
+
mx := keys[len(keys)-1]
|
|
250
|
+
|
|
251
|
+
// Detect missing gaps
|
|
252
|
+
for i := mn; i <= mx; i++ {
|
|
253
|
+
if _, ok := data[i]; !ok {
|
|
254
|
+
return nil // incomplete CRYPTO stream
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
full := make([]byte, mx-mn+1)
|
|
259
|
+
for _, k := range keys {
|
|
260
|
+
full[k-mn] = data[k]
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return full
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
func QUIC_SNI_extractor(packets []PacketInfo) TLS_CH_Parsed {
|
|
267
|
+
// All packets in the same direction
|
|
268
|
+
|
|
269
|
+
full_data := quicFrameStitcher(packets)
|
|
270
|
+
|
|
271
|
+
if full_data == nil {
|
|
272
|
+
return TLS_CH_Parsed{Valid: false}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return tls_ch_parser(full_data)
|
|
276
|
+
|
|
277
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package commons
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strconv"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
type SaveStructCSV interface {
|
|
8
|
+
GetStringArr() []string
|
|
9
|
+
GetHeader() []string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
func (flow SaveFlow) GetStringArr() []string {
|
|
13
|
+
return []string{
|
|
14
|
+
strconv.Itoa(flow.FlowId),
|
|
15
|
+
flow.FiveTuple.String(),
|
|
16
|
+
flow.StartTime.String(),
|
|
17
|
+
flow.EndTime.String(),
|
|
18
|
+
flow.SNI,
|
|
19
|
+
flow.ALPN,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func (flow SaveFlow) GetHeader() []string {
|
|
24
|
+
return []string{"FlowId", "FiveTuple", "StartTime", "EndTime", "SNI", "ALPN"}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
func (packet SavePacket) GetHeader() []string {
|
|
28
|
+
return []string{
|
|
29
|
+
"FlowId", "PacketLength", "Timestamp", "Direction"}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func (packet SavePacket) GetStringArr() []string {
|
|
33
|
+
cols := []string{
|
|
34
|
+
strconv.Itoa(packet.FlowId),
|
|
35
|
+
strconv.Itoa(packet.PacketLength),
|
|
36
|
+
packet.Timestamp.String(),
|
|
37
|
+
strconv.Itoa(int(packet.Direction)),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return cols
|
|
41
|
+
}
|