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.
Files changed (53) hide show
  1. network_core-0.5.6/MANIFEST.in +1 -0
  2. {network_core-0.5.4 → network_core-0.5.6}/PKG-INFO +1 -1
  3. network_core-0.5.6/go_src/commons/constants.go +31 -0
  4. network_core-0.5.6/go_src/commons/data.go +84 -0
  5. network_core-0.5.6/go_src/commons/flowOps.go +45 -0
  6. network_core-0.5.6/go_src/commons/packetOps.go +67 -0
  7. network_core-0.5.6/go_src/commons/quicCrypto.go +124 -0
  8. network_core-0.5.6/go_src/commons/quicParser.go +277 -0
  9. network_core-0.5.6/go_src/commons/savingMethods.go +41 -0
  10. network_core-0.5.6/go_src/commons/tlsParser.go +238 -0
  11. network_core-0.5.6/go_src/commons/utils.go +155 -0
  12. network_core-0.5.6/go_src/core/driver.go +146 -0
  13. network_core-0.5.6/go_src/core/flowGatherer.go +201 -0
  14. network_core-0.5.6/go_src/core/saving.go +96 -0
  15. network_core-0.5.6/go_src/core/testing.go +22 -0
  16. network_core-0.5.6/go_src/core/utils.go +1 -0
  17. network_core-0.5.6/go_src/go.mod +9 -0
  18. network_core-0.5.6/go_src/go.sum +18 -0
  19. network_core-0.5.6/go_src/main.go +18 -0
  20. network_core-0.5.6/network_core/bin/process_pcap +0 -0
  21. {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/PKG-INFO +1 -1
  22. {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/SOURCES.txt +19 -0
  23. {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/top_level.txt +1 -0
  24. {network_core-0.5.4 → network_core-0.5.6}/pyproject.toml +1 -1
  25. network_core-0.5.6/setup.py +33 -0
  26. network_core-0.5.4/network_core/bin/process_pcap +0 -0
  27. {network_core-0.5.4 → network_core-0.5.6}/network_core/__init__.py +0 -0
  28. {network_core-0.5.4 → network_core-0.5.6}/network_core/analysis.py +0 -0
  29. {network_core-0.5.4 → network_core-0.5.6}/network_core/conn.py +0 -0
  30. {network_core-0.5.4 → network_core-0.5.6}/network_core/connOps.py +0 -0
  31. {network_core-0.5.4 → network_core-0.5.6}/network_core/dataModels.py +0 -0
  32. {network_core-0.5.4 → network_core-0.5.6}/network_core/http/__init__.py +0 -0
  33. {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/__init__.py +0 -0
  34. {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/helpers.py +0 -0
  35. {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/parser.py +0 -0
  36. {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/pdh.py +0 -0
  37. {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpExtract/pdml_parser.py +0 -0
  38. {network_core-0.5.4 → network_core-0.5.6}/network_core/http/httpIO.py +0 -0
  39. {network_core-0.5.4 → network_core-0.5.6}/network_core/pcap.py +0 -0
  40. {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/__init__.py +0 -0
  41. {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/clientHello.py +0 -0
  42. {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/constants.py +0 -0
  43. {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/crypto.py +0 -0
  44. {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/helpers.py +0 -0
  45. {network_core-0.5.4 → network_core-0.5.6}/network_core/sni/parsers.py +0 -0
  46. {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/__init__.py +0 -0
  47. {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/csvIO.py +0 -0
  48. {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/dt.py +0 -0
  49. {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/jsonIO.py +0 -0
  50. {network_core-0.5.4 → network_core-0.5.6}/network_core/utils/pcapIO.py +0 -0
  51. {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/dependency_links.txt +0 -0
  52. {network_core-0.5.4 → network_core-0.5.6}/network_core.egg-info/requires.txt +0 -0
  53. {network_core-0.5.4 → network_core-0.5.6}/setup.cfg +0 -0
@@ -0,0 +1 @@
1
+ recursive-include go_src *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: network_core
3
- Version: 0.5.4
3
+ Version: 0.5.6
4
4
  Summary: Core networking utilities and data models
5
5
  Author: Your Name
6
6
  Requires-Python: >=3.9
@@ -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
+ }