ja3.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. // Copyright (c) 2018, Open Systems AG. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style license
  4. // that can be found in the LICENSE file in the root of the source
  5. // tree.
  6. package ja3
  7. import (
  8. "crypto/md5"
  9. "encoding/hex"
  10. "golang.org/x/exp/slices"
  11. )
  12. type ClientHello struct {
  13. Version uint16
  14. CipherSuites []uint16
  15. Extensions []uint16
  16. EllipticCurves []uint16
  17. EllipticCurvePF []uint8
  18. Versions []uint16
  19. SignatureAlgorithms []uint16
  20. ServerName string
  21. ja3ByteString []byte
  22. ja3Hash string
  23. }
  24. func (j *ClientHello) Equals(another *ClientHello, ignoreExtensionsSequence bool) bool {
  25. if j.Version != another.Version {
  26. return false
  27. }
  28. if !slices.Equal(j.CipherSuites, another.CipherSuites) {
  29. return false
  30. }
  31. if !ignoreExtensionsSequence && !slices.Equal(j.Extensions, another.Extensions) {
  32. return false
  33. }
  34. if ignoreExtensionsSequence && !slices.Equal(j.Extensions, another.sortedExtensions()) {
  35. return false
  36. }
  37. if !slices.Equal(j.EllipticCurves, another.EllipticCurves) {
  38. return false
  39. }
  40. if !slices.Equal(j.EllipticCurvePF, another.EllipticCurvePF) {
  41. return false
  42. }
  43. if !slices.Equal(j.SignatureAlgorithms, another.SignatureAlgorithms) {
  44. return false
  45. }
  46. return true
  47. }
  48. func (j *ClientHello) sortedExtensions() []uint16 {
  49. extensions := make([]uint16, len(j.Extensions))
  50. copy(extensions, j.Extensions)
  51. slices.Sort(extensions)
  52. return extensions
  53. }
  54. func Compute(payload []byte) (*ClientHello, error) {
  55. ja3 := ClientHello{}
  56. err := ja3.parseSegment(payload)
  57. return &ja3, err
  58. }
  59. func (j *ClientHello) String() string {
  60. if j.ja3ByteString == nil {
  61. j.marshalJA3()
  62. }
  63. return string(j.ja3ByteString)
  64. }
  65. func (j *ClientHello) Hash() string {
  66. if j.ja3ByteString == nil {
  67. j.marshalJA3()
  68. }
  69. if j.ja3Hash == "" {
  70. h := md5.Sum(j.ja3ByteString)
  71. j.ja3Hash = hex.EncodeToString(h[:])
  72. }
  73. return j.ja3Hash
  74. }