mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-16 08:42:22 +08:00
84 lines
1.9 KiB
Go
84 lines
1.9 KiB
Go
|
// Copyright (c) 2018, Open Systems AG. All rights reserved.
|
||
|
//
|
||
|
// Use of this source code is governed by a BSD-style license
|
||
|
// that can be found in the LICENSE file in the root of the source
|
||
|
// tree.
|
||
|
|
||
|
package ja3
|
||
|
|
||
|
import (
|
||
|
"crypto/md5"
|
||
|
"encoding/hex"
|
||
|
|
||
|
"golang.org/x/exp/slices"
|
||
|
)
|
||
|
|
||
|
type ClientHello struct {
|
||
|
Version uint16
|
||
|
CipherSuites []uint16
|
||
|
Extensions []uint16
|
||
|
EllipticCurves []uint16
|
||
|
EllipticCurvePF []uint8
|
||
|
Versions []uint16
|
||
|
SignatureAlgorithms []uint16
|
||
|
ServerName string
|
||
|
ja3ByteString []byte
|
||
|
ja3Hash string
|
||
|
}
|
||
|
|
||
|
func (j *ClientHello) Equals(another *ClientHello, ignoreExtensionsSequence bool) bool {
|
||
|
if j.Version != another.Version {
|
||
|
return false
|
||
|
}
|
||
|
if !slices.Equal(j.CipherSuites, another.CipherSuites) {
|
||
|
return false
|
||
|
}
|
||
|
if !ignoreExtensionsSequence && !slices.Equal(j.Extensions, another.Extensions) {
|
||
|
return false
|
||
|
}
|
||
|
if ignoreExtensionsSequence && !slices.Equal(j.Extensions, another.sortedExtensions()) {
|
||
|
return false
|
||
|
}
|
||
|
if !slices.Equal(j.EllipticCurves, another.EllipticCurves) {
|
||
|
return false
|
||
|
}
|
||
|
if !slices.Equal(j.EllipticCurvePF, another.EllipticCurvePF) {
|
||
|
return false
|
||
|
}
|
||
|
if !slices.Equal(j.SignatureAlgorithms, another.SignatureAlgorithms) {
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (j *ClientHello) sortedExtensions() []uint16 {
|
||
|
extensions := make([]uint16, len(j.Extensions))
|
||
|
copy(extensions, j.Extensions)
|
||
|
slices.Sort(extensions)
|
||
|
return extensions
|
||
|
}
|
||
|
|
||
|
func Compute(payload []byte) (*ClientHello, error) {
|
||
|
ja3 := ClientHello{}
|
||
|
err := ja3.parseSegment(payload)
|
||
|
return &ja3, err
|
||
|
}
|
||
|
|
||
|
func (j *ClientHello) String() string {
|
||
|
if j.ja3ByteString == nil {
|
||
|
j.marshalJA3()
|
||
|
}
|
||
|
return string(j.ja3ByteString)
|
||
|
}
|
||
|
|
||
|
func (j *ClientHello) Hash() string {
|
||
|
if j.ja3ByteString == nil {
|
||
|
j.marshalJA3()
|
||
|
}
|
||
|
if j.ja3Hash == "" {
|
||
|
h := md5.Sum(j.ja3ByteString)
|
||
|
j.ja3Hash = hex.EncodeToString(h[:])
|
||
|
}
|
||
|
return j.ja3Hash
|
||
|
}
|