|
|
|
@ -13,10 +13,12 @@ import (
|
|
|
|
|
"crypto/md5"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"golang.org/x/crypto/cryptobyte"
|
|
|
|
|
"log"
|
|
|
|
|
"sort"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/crypto/cryptobyte"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Check if this is a GREASE value (RFC 8701).
|
|
|
|
@ -65,6 +67,8 @@ type highlights struct {
|
|
|
|
|
EarlyData bool `json:"-"` // don't include in JSON
|
|
|
|
|
JA3 string `json:"ja3"`
|
|
|
|
|
JA3MD5 byteSlice `json:"ja3_md5"`
|
|
|
|
|
NJA3 string `json:"nja3"` // normalized JA3 (extensions sorted)
|
|
|
|
|
NJA3MD5 byteSlice `json:"nja3_md5"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ClientHelloMsg struct {
|
|
|
|
@ -394,11 +398,11 @@ func (m *ClientHelloMsg) Unmarshal(data []byte) bool {
|
|
|
|
|
log.Println("Unknown extension:", extension.Code)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !extData.Empty() &&
|
|
|
|
|
extension.Name != "GREASE" &&
|
|
|
|
|
extension.Code != extensionPadding {
|
|
|
|
|
log.Printf("Extension %d data not read.\n", extension.Code)
|
|
|
|
|
}
|
|
|
|
|
//if !extData.Empty() &&
|
|
|
|
|
// extension.Name != "GREASE" &&
|
|
|
|
|
// extension.Code != extensionPadding {
|
|
|
|
|
// log.Printf("Extension %d data not read.\n", extension.Code)
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JA3
|
|
|
|
@ -415,12 +419,14 @@ func (m *ClientHelloMsg) Unmarshal(data []byte) bool {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ja3.WriteString(",")
|
|
|
|
|
var extCodes []uint16 // non-GREASE extension codes
|
|
|
|
|
for i, e := range m.Extensions {
|
|
|
|
|
if !isGREASE(e.Code) { // ignore GREASE values
|
|
|
|
|
ja3.WriteString(strconv.FormatUint(uint64(e.Code), 10))
|
|
|
|
|
if i+1 != len(m.Extensions) { // if not last element, add "-"
|
|
|
|
|
ja3.WriteString("-")
|
|
|
|
|
}
|
|
|
|
|
extCodes = append(extCodes, e.Code)
|
|
|
|
|
if e.Code == extensionSupportedGroups {
|
|
|
|
|
supportedGroups = e.Data.SupportedGroups
|
|
|
|
|
} else if e.Code == extensionSupportedPointFormats {
|
|
|
|
@ -448,6 +454,24 @@ func (m *ClientHelloMsg) Unmarshal(data []byte) bool {
|
|
|
|
|
hash := md5.Sum([]byte(m.Highlights.JA3))
|
|
|
|
|
m.Highlights.JA3MD5 = hash[:]
|
|
|
|
|
|
|
|
|
|
// Make normalized JA3:
|
|
|
|
|
// 1. sort extension codes;
|
|
|
|
|
sort.Slice(extCodes, func(i, j int) bool { return extCodes[i] < extCodes[j] })
|
|
|
|
|
// 2. build string of sorted codes;
|
|
|
|
|
var sortedExtString strings.Builder
|
|
|
|
|
for i, code := range extCodes {
|
|
|
|
|
sortedExtString.WriteString(strconv.FormatUint(uint64(code), 10))
|
|
|
|
|
if i+1 != len(extCodes) { // if not last element, add "-"
|
|
|
|
|
sortedExtString.WriteString("-")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 3. replace extensions part of the JA3 string.
|
|
|
|
|
splitJA3 := strings.Split(m.Highlights.JA3, ",")
|
|
|
|
|
splitJA3[2] = sortedExtString.String()
|
|
|
|
|
m.Highlights.NJA3 = strings.Join(splitJA3, ",")
|
|
|
|
|
nhash := md5.Sum([]byte(m.Highlights.NJA3))
|
|
|
|
|
m.Highlights.NJA3MD5 = nhash[:]
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|