Full Code of dreadl0ck/ja3 for AI

master ff953f11e259 cached
23 files
127.9 KB
54.6k tokens
45 symbols
1 requests
Download .txt
Repository: dreadl0ck/ja3
Branch: master
Commit: ff953f11e259
Files: 23
Total size: 127.9 KB

Directory structure:
gitextract_5b0w5x7_/

├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── TODO.md
├── cmd/
│   └── main.go
├── csv.go
├── go.mod
├── go.sum
├── gopacket.go
├── install.sh
├── ja3.go
├── ja3_output_testpcap.json
├── ja3_test.go
├── ja3s.go
├── ja3s_pcap.go
├── ja3s_test.go
├── ja3s_test.log
├── json.go
├── live.go
├── test.pcap
├── test2.pcap
└── utils.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea/

================================================
FILE: AUTHORS
================================================
Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>

================================================
FILE: LICENSE
================================================
Copyright (c) 2017, Salesforce.com, Inc.  
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

================================================
FILE: README.md
================================================
# JA3

[![Go Report Card](https://goreportcard.com/badge/github.com/dreadl0ck/ja3)](https://goreportcard.com/report/github.com/dreadl0ck/ja3)
[![License](https://img.shields.io/badge/License-BSDv3-blue.svg)](https://raw.githubusercontent.com/dreadl0ck/ja3/master/docs/LICENSE)
[![Golang](https://img.shields.io/badge/Go-1.10-blue.svg)](https://golang.org)
![Linux](https://img.shields.io/badge/Supports-Linux-green.svg)
![macOS](https://img.shields.io/badge/Supports-macOS-green.svg)
![Windows](https://img.shields.io/badge/Supports-Windows-green.svg)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/dreadl0ck/ja3)

JA3 is a technique developed by Salesforce, to fingerprint the TLS client and server hellos.

The official python implementation can be found [here](https://github.com/salesforce/ja3).
More details can be found in their blog post: https://engineering.salesforce.com/open-sourcing-ja3-92c9e53c3c41

This package provides a pure golang implementation of both the client and server hash functions,
with unit tests to ensure correct behavior and performance. 
This implementation uses [gopacket/gopacket](https://github.com/gopacket/gopacket) for packet decoding.

If you don't need gopacket in your project and just want to use the logic to generate the Ja3 based on the input data, you can disable compiling with gopacket:

```
go build -tags ja3_disable_gopacket
```

The **[bradleyfalzon/tlsx](https://github.com/bradleyfalzon/tlsx)** fork used for parsing the TLS handshakes (**[dreadl0ck/tlsx](https://github.com/dreadl0ck/tlsx)**),
has been extended to support extracting the server hello message, unit tests, 
benchmarks and a faster decoding interface for use with Ja3 fingerprinting.

Assembly of the ja3 bare was previously implemented with the golang 1.10 **strings.Builder** type,
however using byte slices for this turned out to be faster and causes less allocations. (Thanks for the tips @lukechampine)
Thanks to @guigzzz for his pull request on further reducing allocations!

## Package Usage

This package exports the following API:

Live Capture from interface, outputs CSV with configurable separator or JSON:

```go
func ReadInterface(iface string, out io.Writer, separator string, ja3s bool, asJSON bool, snaplen int, promisc bool, timeout time.Duration) {
```

Files:

```go
func ReadFileJSON(file string, out io.Writer, doJA3s bool)
```
```go
func ReadFileCSV(file string, out io.Writer, separator string, doJA3s bool)
```

Read file and only print Ja3s values, mimics the python implementation from salesforce:

```go
func ReadFileJa3s(file string, out io.Writer)
```

Using gopacket.Packet:

```go
func BarePacket(p gopacket.Packet) []byte
```
```go
func BarePacketJa3s(p gopacket.Packet) []byte
```
```go
func DigestPacket(p gopacket.Packet) [md5.Size]byte
```
```go
func DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte 
```

```go
func DigestHexPacket(p gopacket.Packet) string
```
```go
func DigestHexPacketJa3s(p gopacket.Packet) string 
```

Using tlsx.ClientHello:

```go
func Bare(hello *tlsx.ClientHello) []byte
```
```go
func BareJa3s(hello *tlsx.ServerHello) []byte
```
```go
func Digest(hello *tlsx.ClientHello) [md5.Size]byte
```
```go
func DigestJa3s(hello *tlsx.ServerHello) [md5.Size]byte
```
```go
func DigestHex(hello *tlsx.ClientHello) string
```
```go
func DigestHexJa3s(hello *tlsx.ServerHello) string
```

## Commandline Tool

The commandline program mimics the python reference implementation by default.
It reads the pcap file and prints the all extracted client hellos as JSON array to stdout.
Printing output as CSV, tab separated values or with a custom separator is also supported.

    $ goja3 -h
    Usage of goja3:
      -csv
        	print as CSV
      -debug
        	toggle debug mode
      -iface string
        	specify network interface to read packets from
      -ja3s
        	dump ja3s only
      -json
        	print as JSON array (default true)
      -read string
        	read PCAP file
      -separator string
        	set a custom separator (default ",")
      -tsv
        	print as TAB separated values

Benchmark of the python reference implementation VS this one,
on a 109 MB PCAP dumpfile (DEF CON 23 ICS Village.pcap).
This dump file is interesting for comparison because it contains handshakes without extensions set,
as well as TLS traffic over non standard ports.

Python implementation:

    # https://github.com/salesforce/ja3
    $ time ja3 -a DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
    
    real	0m29.031s
    user	0m28.803s
    sys	0m0.141s

Go implementation:

    # github.com/dreadl0ck/ja3
    # disable server fingerprint generation to mimic the behavior of the reference implementation
    $ time goja3 -ja3s=false -json -read DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
    
    real	0m0.996s
    user	0m2.245s
    sys	0m0.166s

Output:

    [
        ...
        {
            "destination_ip": "192.168.0.60",
            "destination_port": 443,
            "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
            "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
            "ja3s": "",
            "ja3s_digest": "",
            "source_ip": "192.168.0.112",
            "source_port": 59935,
            "timestamp": "1438978270.855224"
        },
        {
            "destination_ip": "192.168.0.30",
            "destination_port": 443,
            "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
            "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
            "ja3s": "",
            "ja3s_digest": "",
            "source_ip": "192.168.0.112",
            "source_port": 42455,
            "timestamp": "1438978270.855228"
        }
    ]

## JA3 Details

JA3 gathers the decimal values of the bytes for the following fields: **SSL Version, Accepted Ciphers, List of Extensions, Elliptic Curves, and Elliptic Curve Formats**.

It then concatenates those values together in order, using a “,” to delimit each field and a “-” to delimit each value in each field.

**The field order is as follows:**

    SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats

**Example:**

    769,47–53–5–10–49161–49162–49171–49172–50–56–19–4,0–10–11,23–24–25,0

If there are no SSL Extensions in the Client Hello, the fields are left empty.

**Example:**

    769,4–5–10–9–100–98–3–6–19–18–99,,,

These strings are then MD5 hashed to produce an easily consumable and shareable 32 character fingerprint. 

This is the JA3 SSL Client Fingerprint.

JA3 is a much more effective way to detect malicious activity over SSL than IP or domain based IOCs. Since JA3 detects the client application, it doesn’t matter if malware uses DGA (Domain Generation Algorithms), or different IPs for each C2 host, or even if the malware uses Twitter for C2, JA3 can detect the malware itself based on how it communicates rather than what it communicates to.

JA3 is also an excellent detection mechanism in locked-down environments where only a few specific applications are allowed to be installed. In these types of environments one could build a whitelist of expected applications and then alert on any other JA3 hits.

For more details on what you can see and do with JA3 and JA3S, please see this Shmoocon 2018 talk: https://youtu.be/oprPu7UIEuk?t=6m44s

## JA3S Details

JA3S is JA3 for the Server side of the SSL/TLS communication and fingerprints how servers respond to particular clients. 

JA3S uses the following field order:
```
SSLVersion,Cipher,SSLExtension
```
With JA3S it is possible to fingerprint the entire cryptographic negotiation between client and it's server by combining JA3 + JA3S. That is because servers will respond to different clients differently but will always respond to the same client the same.

For the Trickbot example:
```
JA3 = 6734f37431670b3ab4292b8f60f29984 ( Fingerprint of Trickbot )
JA3S = 623de93db17d313345d7ea481e7443cf ( Fingerprint of Command and Control Server Response )
```
For the Emotet example:
```
JA3 = 4d7a28d6f2263ed61de88ca66eb011e3 ( Fingerprint of Emotet )
JA3S = 80b3a14bccc8598a1f3bbe83e71f735f ( Fingerprint of Command and Control Server Response )
```

In these malware examples, the command and control server always responds to the malware client in exactly the same way, it does not deviate. So even though the traffic is encrypted and one may not know the command and control server's IPs or domains as they are constantly changing, we can still identify, with reasonable confidence, the malicious communication by fingerprinting the TLS negotiation between client and server. Again, please be aware that these are examples, not indicative of all versions ever, and are intended to illustrate what is possible.

## Benchmarks & Tests

Run the benchmarks and the correctness test with:

    $ go test -v -bench=.
    === RUN   TestDigestHexCorrect
    --- PASS: TestDigestHexCorrect (0.00s)
    === RUN   TestDigestHexComparePcap
    --- PASS: TestDigestHexComparePcap (0.10s)
    === RUN   TestDigestHexJa3sCorrect
    --- PASS: TestDigestHexJa3sCorrect (0.00s)
    === RUN   TestDigestHexJa3sComparePcap
    --- PASS: TestDigestHexJa3sComparePcap (0.10s)
    goos: darwin
    goarch: amd64
    pkg: github.com/dreadl0ck/ja3
    BenchmarkDigestHexPacket
    BenchmarkDigestHexPacket-12        	  781578	      1499 ns/op	     352 B/op	       9 allocs/op
    BenchmarkDigestPacket
    BenchmarkDigestPacket-12           	  827286	      1406 ns/op	     288 B/op	       7 allocs/op
    BenchmarkBarePacket
    BenchmarkBarePacket-12             	 1000000	      1212 ns/op	     288 B/op	       7 allocs/op
    BenchmarkDigestHex
    BenchmarkDigestHex-12              	  895094	      1278 ns/op	     256 B/op	       3 allocs/op
    BenchmarkDigest
    BenchmarkDigest-12                 	  996800	      1318 ns/op	     192 B/op	       1 allocs/op
    BenchmarkBare
    BenchmarkBare-12                   	 1101476	      1188 ns/op	     192 B/op	       1 allocs/op
    BenchmarkDigestHexPacketJa3s
    BenchmarkDigestHexPacketJa3s-12    	 1654360	       619 ns/op	     120 B/op	       4 allocs/op
    BenchmarkDigestPacketJa3s
    BenchmarkDigestPacketJa3s-12       	 2328241	       513 ns/op	      56 B/op	       2 allocs/op
    BenchmarkBarePacketJa3s
    BenchmarkBarePacketJa3s-12         	 3294260	       387 ns/op	      56 B/op	       2 allocs/op
    BenchmarkDigestHexJa3s
    BenchmarkDigestHexJa3s-12          	 2476090	       464 ns/op	     112 B/op	       3 allocs/op
    BenchmarkDigestJa3s
    BenchmarkDigestJa3s-12             	 3321294	       357 ns/op	      48 B/op	       1 allocs/op
    BenchmarkBareJa3s
    BenchmarkBareJa3s-12               	 4964508	       238 ns/op	      48 B/op	       1 allocs/op
    PASS
    ok  	github.com/dreadl0ck/ja3	18.414s
    
Performance comparison with other implementations:

    # https://github.com/open-ch/ja3
    $ time ja3exporter -pcap DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
    
    real	0m0.912s
    user	0m1.979s
    sys	0m0.142s
    
    # github.com/dreadl0ck/ja3
    # disable server fingerprint generation to mimic the behavior of the other implementations
    $ time goja3 -ja3s=false -json -read DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
    
    real	0m0.996s
    user	0m2.245s
    sys	0m0.166s
    
    # https://github.com/salesforce/ja3
    $ time ja3 -a DEF\ CON\ 23\ ICS\ Village.pcap &> /dev/null
    
    real	0m29.031s
    user	0m28.803s
    sys	0m0.141s
    
Number of records detected:

    # https://github.com/open-ch/ja3
    $ time ja3exporter -pcap DEF\ CON\ 23\ ICS\ Village.pcap | wc -l
    138
    
    # github.com/dreadl0ck/ja3
    $ goja3 -ja3s=false -json -read DEF\ CON\ 23\ ICS\ Village.pcap | jq '. | length'
    138
    
    # https://github.com/salesforce/ja3
    $ time ja3 -a DEF\ CON\ 23\ ICS\ Village.pcap | jq '. | length'
    138

    # https://github.com/salesforce/ja3
    # without -a flag, only tls traffic over port 443 will be extracted
    $ time ja3 DEF\ CON\ 23\ ICS\ Village.pcap | jq '. | length'
    96
    
## Notes

The ja3_output_testpcap.json file for output comparison in the unit tests
was generated with the python reference implementation using:

    ja3 -a test.pcap > ja3_output_testpcap.json


================================================
FILE: TODO.md
================================================
# TODO

- compare number of records to output of python implementation
- 0 allocations for Bare()

================================================
FILE: cmd/main.go
================================================
/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"flag"
	"fmt"
	"github.com/dreadl0ck/ja3"
	"github.com/gopacket/gopacket/pcap"
	"os"
)

var (
	flagJSON      = flag.Bool("json", true, "print as JSON array")
	flagCSV       = flag.Bool("csv", false, "print as CSV")
	flagTSV       = flag.Bool("tsv", false, "print as TAB separated values")
	flagSeparator = flag.String("separator", ",", "set a custom separator")
	flagInput     = flag.String("read", "", "read PCAP file")
	flagDebug     = flag.Bool("debug", false, "toggle debug mode")
	flagInterface = flag.String("iface", "", "specify network interface to read packets from")
	flagJa3S      = flag.Bool("ja3s", true, "include ja3 server hashes (ja3s)")
	flagOnlyJa3S  = flag.Bool("ja3s-only", false, "dump ja3s only")
	flagSnaplen   = flag.Int("snaplen", 1514, "default snap length for ethernet frames")
	flagPromisc   = flag.Bool("promisc", true, "capture in promiscuous mode (requires root)")
	// https://godoc.org/github.com/gopacket/gopacket/pcap#hdr-PCAP_Timeouts
	flagTimeout   = flag.Duration("timeout", pcap.BlockForever, "timeout for collecting packet batches")
	flagVersion   = flag.Bool("version", false, "display version and exit")
)

const version = "v1.0.2"

func main() {

	flag.Parse()

	if *flagVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	ja3.Debug = *flagDebug

	if *flagInterface != "" {
		ja3.ReadInterface(*flagInterface, os.Stdout, *flagSeparator, *flagJa3S, *flagJSON, *flagSnaplen, *flagPromisc, *flagTimeout)
		return
	}

	if *flagInput == "" {
		fmt.Println("use the -read flag to supply an input file.")
		os.Exit(1)
	}

	if *flagOnlyJa3S {
		ja3.ReadFileJa3s(*flagInput, os.Stdout)
		return
	}

	if *flagTSV {
		ja3.ReadFileCSV(*flagInput, os.Stdout, "\t", *flagJa3S)
		return
	}

	if *flagCSV {
		ja3.ReadFileCSV(*flagInput, os.Stdout, *flagSeparator, *flagJa3S)
		return
	}

	if *flagJSON {
		ja3.ReadFileJSON(*flagInput, os.Stdout, *flagJa3S)
	}
}


================================================
FILE: csv.go
================================================
//go:build !ja3_disable_gopacket

/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ja3

import (
	"fmt"
	"io"
	"strings"

	"github.com/gopacket/gopacket"
)

// ReadFileCSV reads the PCAP file at the given path
// and prints out all packets containing JA3 digests to the supplied io.Writer
func ReadFileCSV(file string, out io.Writer, separator string, doJA3s bool) {

	r, f, link, err := openPcap(file)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	columns := []string{"timestamp", "source_ip", "source_port", "destination_ip", "destination_port", "ja3_digest", "ja3s_digest"}
	_, err = out.Write([]byte(strings.Join(columns, separator) + "\n"))
	if err != nil {
		panic(err)
	}

	count := 0
	for {
		// read packet data
		data, ci, err := r.ReadPacketData()
		if err == io.EOF {
			if Debug {
				fmt.Println(count, "fingerprints.")
			}
			return
		} else if err != nil {
			panic(err)
		}

		var (
			// create gopacket
			p = gopacket.NewPacket(data, link, gopacket.Lazy)
			// get JA3 if possible
			digest   = DigestHexPacket(p)
			isServer bool
		)

		if doJA3s && digest == "" {
			digest = DigestHexPacketJa3s(p)
			isServer = true
		}

		// check if we got a result
		if digest != "" {

			count++

			var (
				b  strings.Builder
				nl = p.NetworkLayer()
				tl = p.TransportLayer()
			)

			// got an a digest but no transport or network layer
			if tl == nil || nl == nil {
				if Debug {
					fmt.Println("got a nil layer: ", nl, tl, p.Dump(), digest)
				}
				continue
			}

			b.WriteString(timeToString(ci.Timestamp))
			b.WriteString(separator)
			b.WriteString(nl.NetworkFlow().Src().String())
			b.WriteString(separator)
			b.WriteString(tl.TransportFlow().Src().String())
			b.WriteString(separator)
			b.WriteString(nl.NetworkFlow().Dst().String())
			b.WriteString(separator)
			b.WriteString(tl.TransportFlow().Dst().String())
			b.WriteString(separator)
			if isServer {
				b.WriteString("")
				b.WriteString(separator)
				b.WriteString(digest)
			} else { // client
				b.WriteString(digest)
				b.WriteString(separator)
				b.WriteString("")
			}
			b.WriteString("\n")

			_, err := out.Write([]byte(b.String()))
			if err != nil {
				panic(err)
			}
		}
	}
}


================================================
FILE: go.mod
================================================
module github.com/dreadl0ck/ja3

require (
	github.com/dreadl0ck/tlsx v1.1.1
	github.com/gopacket/gopacket v1.4.0
)

require (
	golang.org/x/crypto v0.43.0 // indirect
	golang.org/x/net v0.46.0 // indirect
	golang.org/x/sys v0.37.0 // indirect
)

go 1.24.0


================================================
FILE: go.sum
================================================
github.com/dreadl0ck/tlsx v1.1.1 h1:fclhQwhO2HmRm568JFBEX0G6S7hFGBsN/JBFh9RqgVU=
github.com/dreadl0ck/tlsx v1.1.1/go.mod h1:hqEz1A1Nt5lnFZh8nTE1iqkipCZGX7TWe+xXBWWPyo0=
github.com/gopacket/gopacket v1.4.0 h1:cr1OlFpzksCkZHNO0eLjaSSOrMQnpPXg0j6qHIY3y2U=
github.com/gopacket/gopacket v1.4.0/go.mod h1:EpvsxINeehp5qj4YMKMLf2/dekdhKn2IIAO/ZOifS7o=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=


================================================
FILE: gopacket.go
================================================
/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ja3

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"

	"github.com/gopacket/gopacket"
	"github.com/gopacket/gopacket/layers"
	"github.com/dreadl0ck/tlsx"
)

// DigestPacket returns the Ja3 digest
// for a packet carrying a TLS Client Hello
// or an empty byte slice
func DigestPacket(p gopacket.Packet) [md5.Size]byte {
	return md5.Sum(BarePacket(p))
}

// DigestPacket returns the Ja3s digest
// for a packet carrying a TLS Server Hello
// or an empty byte slice
func DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte {
	return md5.Sum(BarePacketJa3s(p))
}

// DigestHexPacket returns the hex string for the packet
// for a packet carrying a TLS Client Hello
func DigestHexPacket(p gopacket.Packet) string {

	bare := BarePacket(p)
	if len(bare) == 0 {
		return ""
	}

	sum := md5.Sum(bare)
	return hex.EncodeToString(sum[:])
}

// DigestHexPacket returns the hex string for the packet
// for a packet carrying a TLS Client Hello
func DigestHexPacketJa3s(p gopacket.Packet) string {

	bare := BarePacketJa3s(p)
	if len(bare) == 0 {
		return ""
	}

	sum := md5.Sum(bare)
	return hex.EncodeToString(sum[:])
}

// BarePacket returns the Ja3 digest if the supplied packet contains a TLS client hello
// otherwise returns an empty string
func BarePacket(p gopacket.Packet) []byte {
	if tl := p.TransportLayer(); tl != nil {
		if tcp, ok := tl.(*layers.TCP); ok {
			if tcp.SYN {
				// Connection setup
			} else if tcp.FIN {
				// Connection teardown
			} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
				// Acknowledgement packet
			} else if tcp.RST {
				// Unexpected packet
			} else {
				// data packet
				var (
					hello = tlsx.ClientHelloBasic{}
					err   = hello.Unmarshal(tcp.LayerPayload())
				)
				if err != nil {
					if Debug {
						fmt.Println(err)
						//fmt.Println(p.Dump())
					}
					return []byte{}
				}

				// return JA3 bare
				return Bare(&hello)
			}
		}
	}
	return []byte{}
}

// BarePacket returns the Ja3 digest if the supplied packet contains a TLS client hello
// otherwise returns an empty string
func BarePacketJa3s(p gopacket.Packet) []byte {
	if tl := p.TransportLayer(); tl != nil {
		if tcp, ok := tl.(*layers.TCP); ok {
			if tcp.SYN {
				// Connection setup
			} else if tcp.FIN {
				// Connection teardown
			} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
				// Acknowledgement packet
			} else if tcp.RST {
				// Unexpected packet
			} else {
				// data packet
				var (
					hello = tlsx.ServerHelloBasic{}
					err   = hello.Unmarshal(tcp.LayerPayload())
				)
				if err != nil {
					if Debug {
						fmt.Println(err, p.NetworkLayer().NetworkFlow(), p.TransportLayer().TransportFlow())
						//fmt.Println(p.Dump())
					}
					return []byte{}
				}

				// return JA3 bare
				return BareJa3s(&hello)
			}
		}
	}
	return []byte{}
}


================================================
FILE: install.sh
================================================
#!/bin/bash

go build -o $(go env GOPATH)/bin/goja3 -i github.com/dreadl0ck/ja3/cmd

================================================
FILE: ja3.go
================================================
/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ja3

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"strconv"

	"github.com/dreadl0ck/tlsx"
)

var (
	// Debug indicates whether we run in debug mode.
	Debug        = false
	sepValueByte = byte(45)
	sepFieldByte = byte(44)

	// Ciphers, extensions and elliptic curves, should be filtered so GREASE values are not added to the ja3 digest
	// GREASE RFC: https://tools.ietf.org/html/draft-davidben-tls-grease-01
	greaseValues = map[uint16]bool{
		0x0a0a: true, 0x1a1a: true,
		0x2a2a: true, 0x3a3a: true,
		0x4a4a: true, 0x5a5a: true,
		0x6a6a: true, 0x7a7a: true,
		0x8a8a: true, 0x9a9a: true,
		0xaaaa: true, 0xbaba: true,
		0xcaca: true, 0xdada: true,
		0xeaea: true, 0xfafa: true,
	}
)

// BareToDigestHex converts a bare []byte to a hex string.
func BareToDigestHex(bare []byte) string {
	sum := md5.Sum(bare)
	return hex.EncodeToString(sum[:])
}

// Digest returns only the digest md5.
func Digest(hello *tlsx.ClientHelloBasic) [md5.Size]byte {
	return md5.Sum(Bare(hello))
}

// DigestHex produce md5 hash from bare string.
func DigestHex(hello *tlsx.ClientHelloBasic) string {
	return BareToDigestHex(Bare(hello))
}

// Bare returns the JA3 bare string for a given tlsx.ClientHelloBasic instance
// JA3 is a technique developed by Salesforce, to fingerprint TLS Client Hellos.
// the official python implementation can be found here: https://github.com/salesforce/ja3
// JA3 gathers the decimal values of the bytes for the following fields; SSL Version, Accepted Ciphers, List of Extensions, Elliptic Curves, and Elliptic Curve Formats.
// It then concatenates those values together in order, using a “,” to delimit each field and a “-” to delimit each value in each field.
// The field order is as follows:
// SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats
// Example:
// 769,47–53–5–10–49161–49162–49171–49172–50–56–19–4,0–10–11,23–24–25,0
// If there are no SSL Extensions in the Client Hello, the fields are left empty.
// Example:
// 769,4–5–10–9–100–98–3–6–19–18–99,,,
// These strings are then MD5 hashed to produce an easily consumable and shareable 32 character fingerprint.
// This is the JA3 SSL Client Fingerprint returned by this function.
func Bare(hello *tlsx.ClientHelloBasic) []byte {

	// TODO: refactor into struct with inbuilt buffer to reduce allocations to ~ zero
	// i.e. only realloc if previously allocated buffer is too small for current packet

	var (
		maxPossibleBufferLength = 5 + 1 + // Version = uint16 => maximum = 65536 = 5chars + 1 field sep
			(5+1)*len(hello.CipherSuites) + // CipherSuite = uint16 => maximum = 65536 = 5chars
			(5+1)*len(hello.AllExtensions) + // uint16 = 2B => maximum = 65536 = 5chars
			(5+1)*len(hello.SupportedGroups) + // uint16 = 2B => maximum = 65536 = 5chars
			(3+1)*len(hello.SupportedPoints) // uint8 = 1B => maximum = 256 = 3chars

		buffer = make([]byte, 0, maxPossibleBufferLength)
	)

	buffer = strconv.AppendInt(buffer, int64(hello.HandshakeVersion), 10)
	buffer = append(buffer, sepFieldByte)

	/*
	 *	Cipher Suites
	 */

	// collect cipher suites
	lastElem := len(hello.CipherSuites) - 1
	if len(hello.CipherSuites) > 1 {
		for _, e := range hello.CipherSuites[:lastElem] {
			// filter GREASE values
			if !greaseValues[uint16(e)] {
				buffer = strconv.AppendInt(buffer, int64(e), 10)
				buffer = append(buffer, sepValueByte)
			}
		}
	}
	// append last element if cipher suites are not empty
	if lastElem != -1 {
		// filter GREASE values
		if !greaseValues[uint16(hello.CipherSuites[lastElem])] {
			buffer = strconv.AppendInt(buffer, int64(hello.CipherSuites[lastElem]), 10)
		}
	}
	buffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})
	buffer = append(buffer, sepFieldByte)

	/*
	 *	Extensions
	 */

	// collect extensions
	lastElem = len(hello.AllExtensions) - 1
	if len(hello.AllExtensions) > 1 {
		for _, e := range hello.AllExtensions[:lastElem] {
			// filter GREASE values
			if !greaseValues[uint16(e)] {
				buffer = strconv.AppendInt(buffer, int64(e), 10)
				buffer = append(buffer, sepValueByte)
			}
		}
	}
	// append last element if extensions are not empty
	if lastElem != -1 {
		// filter GREASE values
		if !greaseValues[uint16(hello.AllExtensions[lastElem])] {
			buffer = strconv.AppendInt(buffer, int64(hello.AllExtensions[lastElem]), 10)
		}
	}
	buffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})
	buffer = append(buffer, sepFieldByte)

	/*
	 *	Supported Groups
	 */

	// collect supported groups
	lastElem = len(hello.SupportedGroups) - 1
	if len(hello.SupportedGroups) > 1 {
		for _, e := range hello.SupportedGroups[:lastElem] {
			// filter GREASE values
			if !greaseValues[uint16(e)] {
				buffer = strconv.AppendInt(buffer, int64(e), 10)
				buffer = append(buffer, sepValueByte)
			}
		}
	}
	// append last element if supported groups are not empty
	if lastElem != -1 {
		// filter GREASE values
		if !greaseValues[uint16(hello.SupportedGroups[lastElem])] {
			buffer = strconv.AppendInt(buffer, int64(hello.SupportedGroups[lastElem]), 10)
		}
	}
	buffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})
	buffer = append(buffer, sepFieldByte)

	/*
	 *	Supported Points
	 */

	// collect supported points
	lastElem = len(hello.SupportedPoints) - 1
	if len(hello.SupportedPoints) > 1 {
		for _, e := range hello.SupportedPoints[:lastElem] {
			buffer = strconv.AppendInt(buffer, int64(e), 10)
			buffer = append(buffer, sepValueByte)
		}
	}
	// append last element if supported points are not empty
	if lastElem != -1 {
		buffer = strconv.AppendInt(buffer, int64(hello.SupportedPoints[lastElem]), 10)
	}

	return buffer
}


================================================
FILE: ja3_output_testpcap.json
================================================
[
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "768,22-19-10-102-5-4-101-100-99-98-97-96-21-18-9-20-17-8-6-3,,,",
        "ja3_digest": "ee0799c323d74129b75b633dcfd41593",
        "source_ip": "192.168.0.104",
        "source_port": 47851,
        "timestamp": 1438974952.485206
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.104",
        "source_port": 47852,
        "timestamp": 1438974952.499013
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 47436,
        "timestamp": 1438975304.430254
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 47437,
        "timestamp": 1438975310.453851
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 47438,
        "timestamp": 1438975310.80441
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 47439,
        "timestamp": 1438975311.016676
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 47440,
        "timestamp": 1438975312.045158
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 47441,
        "timestamp": 1438975312.182204
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 47442,
        "timestamp": 1438975312.918489
    },
    {
        "destination_ip": "192.168.0.30",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 42577,
        "timestamp": 1438975412.19272
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40052,
        "timestamp": 1438975415.681516
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40053,
        "timestamp": 1438975415.695585
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40054,
        "timestamp": 1438975415.784149
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40055,
        "timestamp": 1438975415.784384
    },
    {
        "destination_ip": "192.168.0.30",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 42938,
        "timestamp": 1438975417.285877
    },
    {
        "destination_ip": "192.168.0.30",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 42939,
        "timestamp": 1438975417.64084
    },
    {
        "destination_ip": "192.168.0.30",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 42940,
        "timestamp": 1438975417.846781
    },
    {
        "destination_ip": "192.168.0.30",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 42941,
        "timestamp": 1438975418.174816
    },
    {
        "destination_ip": "192.168.0.30",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 42942,
        "timestamp": 1438975418.252502
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40062,
        "timestamp": 1438975418.728398
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40063,
        "timestamp": 1438975418.735892
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40066,
        "timestamp": 1438975418.912281
    },
    {
        "destination_ip": "192.168.0.30",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 42949,
        "timestamp": 1438975418.953542
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40069,
        "timestamp": 1438975418.970933
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40071,
        "timestamp": 1438975419.160157
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40072,
        "timestamp": 1438975419.167724
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40074,
        "timestamp": 1438975419.363007
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40075,
        "timestamp": 1438975419.370242
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40077,
        "timestamp": 1438975419.563502
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40078,
        "timestamp": 1438975419.569185
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40080,
        "timestamp": 1438975419.763242
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40081,
        "timestamp": 1438975419.769096
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40083,
        "timestamp": 1438975419.965889
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40084,
        "timestamp": 1438975419.972691
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40086,
        "timestamp": 1438975420.167612
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40087,
        "timestamp": 1438975420.174799
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40089,
        "timestamp": 1438975420.370286
    },
    {
        "destination_ip": "192.168.0.21",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 40090,
        "timestamp": 1438975420.376324
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33325,
        "timestamp": 1438975627.692959
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33329,
        "timestamp": 1438975630.680471
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33330,
        "timestamp": 1438975630.730156
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33331,
        "timestamp": 1438975630.732841
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33333,
        "timestamp": 1438975630.929733
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33334,
        "timestamp": 1438975630.932921
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33336,
        "timestamp": 1438975631.130647
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33337,
        "timestamp": 1438975631.134219
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33339,
        "timestamp": 1438975631.332171
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33340,
        "timestamp": 1438975631.334887
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33342,
        "timestamp": 1438975631.533235
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33343,
        "timestamp": 1438975631.537026
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33345,
        "timestamp": 1438975631.733574
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33346,
        "timestamp": 1438975631.735561
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33348,
        "timestamp": 1438975631.935475
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33349,
        "timestamp": 1438975631.93968
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33351,
        "timestamp": 1438975632.137021
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33352,
        "timestamp": 1438975632.14068
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33354,
        "timestamp": 1438975632.338641
    },
    {
        "destination_ip": "192.168.0.32",
        "destination_port": 80,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 33355,
        "timestamp": 1438975632.342897
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "768,22-19-10-102-5-4-101-100-99-98-97-96-21-18-9-20-17-8-6-3,,,",
        "ja3_digest": "ee0799c323d74129b75b633dcfd41593",
        "source_ip": "192.168.0.109",
        "source_port": 60089,
        "timestamp": 1438975896.044741
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60090,
        "timestamp": 1438975896.065098
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60091,
        "timestamp": 1438975902.358398
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60096,
        "timestamp": 1438975902.358647
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60093,
        "timestamp": 1438975902.358653
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60097,
        "timestamp": 1438975902.359146
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60098,
        "timestamp": 1438975902.35915
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60099,
        "timestamp": 1438975902.360151
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "769,49169-57-4,,,",
        "ja3_digest": "a471a243929edb6640fff17325ed585f",
        "source_ip": "192.168.0.109",
        "source_port": 60092,
        "timestamp": 1438975902.407827
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "769,49169-57-4,13172,,",
        "ja3_digest": "231f19e6900c1003725de85e988c8136",
        "source_ip": "192.168.0.109",
        "source_port": 60094,
        "timestamp": 1438975902.408066
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60100,
        "timestamp": 1438975903.991505
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60101,
        "timestamp": 1438975903.994341
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60102,
        "timestamp": 1438975904.042925
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60103,
        "timestamp": 1438975904.920607
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60104,
        "timestamp": 1438975905.278113
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60105,
        "timestamp": 1438975905.568019
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60106,
        "timestamp": 1438975905.852386
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60107,
        "timestamp": 1438975906.11517
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60108,
        "timestamp": 1438975906.364325
    },
    {
        "destination_ip": "192.168.0.40",
        "destination_port": 443,
        "ja3": "771,5-4-2-8-20-3-1-21-6-22-23-51-57-25-58-26-24-53-9-10-27-47-52-49168-49158-49173-49163-49153-59-49200-49196-49192-49188-49172-49162-163-159-107-106-56-136-135-49177-167-109-137-49202-49198-49194-49190-49167-49157-157-61-132-49170-49160-19-49175-49165-49155-49199-49195-49191-49187-49171-49161-162-158-103-64-50-154-153-69-68-49176-166-108-155-70-49201-49197-49193-49189-49166-49156-156-60-150-65-49169-49159-49174-49164-49154-18-17-255,11-10-35-13-15,14-13-25-11-12-24-9-10-22-23-8-6-7-20-21-4-5-18-19-1-2-3-15-16-17,0-1-2",
        "ja3_digest": "e3bb8f1cd407701c585e7a84e96c24e1",
        "source_ip": "192.168.0.109",
        "source_port": 60109,
        "timestamp": 1438975906.659688
    },
    {
        "destination_ip": "192.168.0.60",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 50499,
        "timestamp": 1438975946.959158
    },
    {
        "destination_ip": "192.168.0.60",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 50539,
        "timestamp": 1438975968.774312
    },
    {
        "destination_ip": "192.168.0.60",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 50540,
        "timestamp": 1438975969.12589
    },
    {
        "destination_ip": "192.168.0.60",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 50541,
        "timestamp": 1438975969.382566
    },
    {
        "destination_ip": "192.168.0.60",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 50542,
        "timestamp": 1438975970.064632
    },
    {
        "destination_ip": "192.168.0.60",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 50543,
        "timestamp": 1438975970.068296
    },
    {
        "destination_ip": "192.168.0.60",
        "destination_port": 443,
        "ja3": "771,49195-49199-49162-49161-49171-49172-51-57-47-53-10,65281-10-11-35-13172-16-5-13,23-24-25,0",
        "ja3_digest": "07e4f12082fc28a84c5412bedc0aa0e2",
        "source_ip": "192.168.0.109",
        "source_port": 50544,
        "timestamp": 1438975970.069778
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7298,
        "timestamp": 1438976023.165545
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7301,
        "timestamp": 1438976028.310661
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7303,
        "timestamp": 1438976028.581532
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7304,
        "timestamp": 1438976028.601543
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7305,
        "timestamp": 1438976028.741557
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7306,
        "timestamp": 1438976028.777827
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7307,
        "timestamp": 1438976028.998445
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7308,
        "timestamp": 1438976028.998754
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7309,
        "timestamp": 1438976028.999243
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7310,
        "timestamp": 1438976029.028726
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7311,
        "timestamp": 1438976029.051919
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7312,
        "timestamp": 1438976029.360982
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7323,
        "timestamp": 1438976050.268904
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7324,
        "timestamp": 1438976050.290868
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7326,
        "timestamp": 1438976051.644443
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7327,
        "timestamp": 1438976051.66594
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7328,
        "timestamp": 1438976051.824233
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7329,
        "timestamp": 1438976051.855177
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7330,
        "timestamp": 1438976052.082936
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7331,
        "timestamp": 1438976052.083183
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7332,
        "timestamp": 1438976052.083439
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7333,
        "timestamp": 1438976052.111491
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7334,
        "timestamp": 1438976052.13963
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7335,
        "timestamp": 1438976052.162501
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7341,
        "timestamp": 1438976061.491056
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7342,
        "timestamp": 1438976061.492075
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7343,
        "timestamp": 1438976061.518477
    },
    {
        "destination_ip": "192.168.0.20",
        "destination_port": 443,
        "ja3": "771,49195-49199-158-52244-52243-52245-49162-49172-57-49161-49171-51-156-53-47-10-255,0-23-35-13-5-13172-18-16-30032-11-10,23-24,0",
        "ja3_digest": "624f05d5bd91b08b2d8c395c32358f19",
        "source_ip": "192.168.0.102",
        "source_port": 7344,
        "timestamp": 1438976061.540319
    }
]


================================================
FILE: ja3_test.go
================================================
/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ja3

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"testing"

	"github.com/gopacket/gopacket"
	"github.com/gopacket/gopacket/layers"
	"github.com/dreadl0ck/tlsx"
)

var tlsPacket = []byte{
	0x68, 0x7f, 0x74, 0xd6, 0x95, 0xc1, 0x00, 0x22, 0x15, 0x63, 0xc9, 0x5a, 0x08, 0x00, 0x45, 0x00,
	0x00, 0xe5, 0x0f, 0xf0, 0x40, 0x00, 0x80, 0x06, 0xaf, 0x9d, 0xc0, 0xa8, 0x01, 0x0e, 0x17, 0x17,
	0x61, 0xb8, 0xc0, 0xef, 0x01, 0xbb, 0x49, 0x71, 0x37, 0x25, 0x98, 0x36, 0xf3, 0x06, 0x50, 0x18,
	0x01, 0x00, 0xfe, 0xf4, 0x00, 0x00, 0x16, 0x03, 0x03, 0x00, 0xb8, 0x01, 0x00, 0x00, 0xb4, 0x03,
	0x03, 0x59, 0xc1, 0x47, 0x24, 0x67, 0x4d, 0x4f, 0x3b, 0x1f, 0xbd, 0x36, 0x75, 0x9b, 0x2a, 0x92,
	0x47, 0x45, 0xf6, 0x7b, 0x03, 0x19, 0x09, 0x13, 0x76, 0x7e, 0xfc, 0x7f, 0xc4, 0x22, 0x6b, 0xe8,
	0x1a, 0x20, 0xfb, 0xa8, 0x85, 0x7c, 0x99, 0xca, 0x36, 0x12, 0xe7, 0x72, 0xb0, 0x87, 0xef, 0xdd,
	0x08, 0x02, 0x7f, 0xc7, 0x55, 0x72, 0xdc, 0x5a, 0xeb, 0x7b, 0x1f, 0x04, 0x61, 0xf4, 0xa6, 0x82,
	0x18, 0x51, 0x00, 0x2a, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x05, 0x00, 0x0a,
	0xc0, 0x27, 0xc0, 0x13, 0xc0, 0x14, 0xc0, 0x2b, 0xc0, 0x23, 0xc0, 0x2c, 0xc0, 0x24, 0xc0, 0x09,
	0xc0, 0x0a, 0x00, 0x40, 0x00, 0x32, 0x00, 0x6a, 0x00, 0x38, 0x00, 0x13, 0x00, 0x04, 0x01, 0x00,
	0x00, 0x41, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x00, 0x0f,
	0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2e, 0x6b, 0x72, 0x78, 0x64, 0x2e, 0x6e, 0x65, 0x74, 0x00,
	0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
	0x0d, 0x00, 0x10, 0x00, 0x0e, 0x04, 0x01, 0x05, 0x01, 0x02, 0x01, 0x04, 0x03, 0x05, 0x03, 0x02,
	0x03, 0x02, 0x02,
}

func getHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ClientHelloBasic) {
	if tl := p.TransportLayer(); tl != nil {
		if tcp, ok := tl.(*layers.TCP); ok {
			if tcp.SYN {
				b.Fatal("Connection setup")
			} else if tcp.FIN {
				b.Fatal("Connection teardown")
			} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
				b.Fatal("Acknowledgement packet")
			} else if tcp.RST {
				b.Fatal("Unexpected packet")
			} else {
				hello = &tlsx.ClientHelloBasic{}
				err := hello.Unmarshal(tcp.LayerPayload())
				if err != nil {
					b.Fatal("invalid packet", err)
				}
			}
		}
	}
	return
}

/*
 *	Tests
 */

func TestDigestHexCorrect(t *testing.T) {

	p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
	if p.ErrorLayer() != nil {
		t.Fatal(p.ErrorLayer().Error())
	}

	hash := DigestHexPacket(p)
	if hash != "4d7a28d6f2263ed61de88ca66eb011e3" {
		t.Fatal(hash, "!=", "4d7a28d6f2263ed61de88ca66eb011e3")
	}
}

func TestDigestHexComparePcap(t *testing.T) {

	// Step 1: Read JSON output of reference implementation
	data, err := ioutil.ReadFile("ja3_output_testpcap.json")
	if err != nil {
		t.Fatal(err)
	}

	var referenceRecords = make([]*Record, 0)
	err = json.Unmarshal(data, &referenceRecords)
	if err != nil {
		t.Fatal(err)
	}

	// Step 2: generate JSON output wth goja3 and read into buffer
	var (
		b       bytes.Buffer
		records = make([]*Record, 0)
	)

	// read test.pcap and generate fingerprints
	ReadFileJSON("test.pcap", &b, false)

	err = json.Unmarshal(b.Bytes(), &records)
	if err != nil {
		t.Fatal(err)
	}

	// Step 3: compare the number of fingerprints and then the digest for each record
	if len(records) != len(referenceRecords) {
		t.Fatal("len(records) != len(referenceRecords): ", len(records), " != ", len(referenceRecords))
	}

	// range reference records
	for i, r := range referenceRecords {

		// compare reference digest to the one produced by goja3
		if r.JA3Digest != records[i].JA3Digest {
			t.Fatal("r.JA3Digest != records[i].JA3Digest: ", r.JA3Digest, " != ", records[i].JA3Digest)
		}
		// compare reference digest to the one produced by goja3
		if r.JA3SDigest != records[i].JA3SDigest {
			t.Fatal("r.JA3SDigest != records[i].JAS3Digest: ", r.JA3SDigest, " != ", records[i].JA3SDigest)
		}
	}
}

/*
 *	Benchmarks
 */

func BenchmarkDigestHexPacket(b *testing.B) {

	p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		DigestHexPacket(p)
	}
}

func BenchmarkDigestPacket(b *testing.B) {
	p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)
	if p.ErrorLayer() != nil {
		b.Fatal(p.ErrorLayer().Error())
	}

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		DigestPacket(p)
	}
}

func BenchmarkBarePacket(b *testing.B) {
	p := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)
	if p.ErrorLayer() != nil {
		b.Fatal(p.ErrorLayer().Error())
	}

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		BarePacket(p)
	}
}

func BenchmarkDigestHex(b *testing.B) {
	var (
		p     = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
		hello = getHello(p, b)
	)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		DigestHex(hello)
	}
}

func BenchmarkDigest(b *testing.B) {

	var (
		p     = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
		hello = getHello(p, b)
	)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		Digest(hello)
	}
}

func BenchmarkBare(b *testing.B) {

	var (
		p     = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)
		hello = getHello(p, b)
	)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		Bare(hello)
	}
}


================================================
FILE: ja3s.go
================================================
/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ja3

import (
	"crypto/md5"
	"encoding/hex"
	"github.com/dreadl0ck/tlsx"
	"strconv"
)

// BareToDigestHex converts a bare []byte to a hex string.
func BareToDigestHexJa3s(bare []byte) string {
	sum := md5.Sum(bare)
	return hex.EncodeToString(sum[:])
}

// Digest returns only the digest md5.
func DigestJa3s(hello *tlsx.ServerHelloBasic) [md5.Size]byte {
	return md5.Sum(BareJa3s(hello))
}

// DigestHex produce md5 hash from bare string.
func DigestHexJa3s(hello *tlsx.ServerHelloBasic) string {
	return BareToDigestHex(BareJa3s(hello))
}

// BareJa3s returns the JA3S bare string for a given tlsx.ServerHelloBasic instance
// JA3S is JA3 for the Server side of the SSL/TLS communication and fingerprints how servers respond to particular clients.
// JA3S uses the following field order:
// SSLVersion,Cipher,SSLExtension
func BareJa3s(hello *tlsx.ServerHelloBasic) []byte {

	// TODO: refactor into struct with inbuilt buffer to reduce allocations to ~ zero
	// i.e. only realloc if previously allocated buffer is too small for current packet

	var (
		maxPossibleBufferLength = 5 + 1 + // Version = uint16 => maximum = 65536 = 5chars + 1 field sep
			(5+1)*1 + // CipherSuite = uint16 => maximum = 65536 = 5chars
			(5+1)*len(hello.Extensions) // uint16 = 2B => maximum = 65536 = 5chars

		buffer = make([]byte, 0, maxPossibleBufferLength)
	)

	buffer = strconv.AppendInt(buffer, int64(hello.Vers), 10)
	buffer = append(buffer, sepFieldByte)

	/*
	 *	Cipher Suite
	 */

	buffer = strconv.AppendInt(buffer, int64(hello.CipherSuite), 10)
	buffer = append(buffer, sepFieldByte)

	if len(hello.Extensions) > 0 {

		/*
		 *	Extensions
		 */

		// collect extensions
		lastElem := len(hello.Extensions) - 1
		if len(hello.Extensions) > 1 {
			for _, e := range hello.Extensions[:lastElem] {
				// filter GREASE values
				if !greaseValues[uint16(e)] {
					buffer = strconv.AppendInt(buffer, int64(e), 10)
					buffer = append(buffer, sepValueByte)
				}
			}
		}
		// append last element if extensions are not empty
		if lastElem != -1 {
			// filter GREASE values
			if !greaseValues[uint16(hello.Extensions[lastElem])] {
				buffer = strconv.AppendInt(buffer, int64(hello.Extensions[lastElem]), 10)
			}
		}
	}

	return buffer
}


================================================
FILE: ja3s_pcap.go
================================================
//go:build !ja3_disable_gopacket

package ja3

import (
	"fmt"
	"io"
	"strings"

	"github.com/gopacket/gopacket"
)

// ReadFileJa3s reads the PCAP file at the given path
// and prints out all packets containing JA3S digests to the supplied io.Writer
func ReadFileJa3s(file string, out io.Writer) {

	r, f, link, err := openPcap(file)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	count := 0
	for {
		// read packet data
		data, _, err := r.ReadPacketData()
		if err == io.EOF {
			if Debug {
				fmt.Println(count, "fingerprints.")
			}
			return
		} else if err != nil {
			panic(err)
		}

		var (
			// create gopacket
			p = gopacket.NewPacket(data, link, gopacket.Lazy)
			// get JA3 if possible
			digest = DigestHexPacketJa3s(p)
		)

		// check if we got a result
		if digest != "" {

			count++

			var (
				b  strings.Builder
				nl = p.NetworkLayer()
				tl = p.TransportLayer()
			)

			// got an a digest but no transport or network layer
			if tl == nil || nl == nil {
				if Debug {
					fmt.Println("got a nil layer: ", nl, tl, p.Dump(), digest)
				}
				continue
			}

			b.WriteString("[")
			b.WriteString(nl.NetworkFlow().Dst().String())
			b.WriteString(":")
			b.WriteString(tl.TransportFlow().Dst().String())
			b.WriteString("] JA3S: ")
			b.WriteString(string(BarePacketJa3s(p)))
			b.WriteString(" --> ")
			b.WriteString(digest)
			b.WriteString("\n")

			_, err := out.Write([]byte(b.String()))
			if err != nil {
				panic(err)
			}
		}
	}
}


================================================
FILE: ja3s_test.go
================================================
/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ja3_test

import (
	"bytes"
	"encoding/hex"
	"github.com/dreadl0ck/ja3"
	"io/ioutil"
	"log"
	"testing"

	"github.com/dreadl0ck/tlsx"
	"github.com/gopacket/gopacket"
	"github.com/gopacket/gopacket/layers"
)

var tlsServerHelloPacket, errDecodeServerHelloPacket = hex.DecodeString("f018982a38be48d343aac4b80800450005b4447b00007906ea20225f7893c0a8b20d01bbefca6011d3eb8013192d801000f087ae00000101080a79facb3a56d9798616030300640200006003035e5bea4420be035069306e15e36eca187d56a201e8e96f0a1afeeb529413fba020746572b9b8832a5b8f236ba51743281c20e923a880067e4c38a754f53f3a8743c02f00001800170000ff01000100000b0002010000100005000302683216030309980b0009940009910004fc308204f8308203e0a00302010202100a86b904765831e240cc6211101f5736300d06092a864886f70d01010b0500305e310b300906035504061302555331153013060355040a130c446967694365727420496e6331193017060355040b13107777772e64696769636572742e636f6d311d301b0603550403131447656f5472757374205253412043412032303138301e170d3138303130343030303030305a170d3230303730393132303030305a3068310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f060355040713085061736164656e61311b3019060355040a13124f70656e5820546563686e6f6c6f676965733114301206035504030c0b2a2e6f70656e782e6e657430820122300d06092a864886f70d01010105000382010f003082010a0282010100bf0efabfe55538e687d4db8f8d615ab1d5dce14702307ca21c0547cd2cc43e455627b4d8decd8015296c19127d29025fbac71f2fc1af1bfe525ed0c2778028a3e7dd643b60842801d9d196651924057827c4d3ce6ae62253b869f384d56a72060055e3537a67a9904e61a4628067f07c680a8bf30c14444d090910c182ea3c5e473b1ab1233838645b0bdfb24265ee32e3521b4bdbae6d79acdbdd7a00c0f00479537079fd352aade5e8394be2f2b397366cfd64bb94b1b96515b4e78d3a2fac04e9d5787792fcf411b6b66cb5dfbc4cb023d93fa99cee630d8ed3558b33e0f2a019ecc5eac16919c48a844f693b5e7ccb4eb84724ab963979c570eb7a3b58ad0203010001a38201a6308201a2301f0603551d230418301680149058ffb09c75a8515477b1edf2a34316389e6cc5301d0603551d0e04160414e03952aaaeb23c67e939ec6022188686bcb314c430210603551d11041a3018820b2a2e6f70656e782e6e657482096f70656e782e6e6574300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302303e0603551d1f043730353033a031a02f862d687474703a2f2f6364702e67656f74727573742e636f6d2f47656f54727573745253414341323031382e63726c304c0603551d2004453043303706096086480186fd6c0101302a302806082b06010505070201161c68747470733a2f2f7777772e64696769636572742e636f6d2f4350533008060667810c010202307506082b0601050507010104693067302606082b06010505073001861a687474703a2f2f7374617475732e67656f74727573742e636f6d303d06082b060105050730028631687474703a2f2f636163657274732e67656f74727573742e636f6d2f47656f54727573745253414341323031382e63727430090603551d1304023000300d06092a864886f70d01010b0500038201010045a0d33fa95d251c861518096f2ffe89bae2e062cfc4505859b42adeaa775fcf34c2eebde155cfb6449a806abffbaad2e88d77b1397e76ea08265dbc2aa98cb04e3d141c27ed629961d9cd90bb7f6db672408bb15126dd4df4cdf831465c1ee9f321505ace6e7c09d864683fbbaa8ee9fc73e74feefaa5a027067856000f56c9ae39bcb3cc586928429bd8abe5d2a3d8df5c63b4db45fa2cc3ffa6f75b946188019e162fb8dfacf2be3064bf52788378e61c8397ec13e72649fbe15e5c5b6d8bae43ca7b188eda5a41e512f508206fefe96a832ce4676a425ddd06e3cfe93046140818a7359723f2477315f38ab51f83c9dced627efaf6541ed3e9c0767376b200048f3082048b30820373a0")

func getServerHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ServerHelloBasic) {

	if errDecodeServerHelloPacket != nil {
		log.Fatal("failed to decode test packet", errDecodeServerHelloPacket)
	}

	if tl := p.TransportLayer(); tl != nil {
		if tcp, ok := tl.(*layers.TCP); ok {
			if tcp.SYN {
				b.Fatal("Connection setup")
			} else if tcp.FIN {
				b.Fatal("Connection teardown")
			} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {
				b.Fatal("Acknowledgement packet")
			} else if tcp.RST {
				b.Fatal("Unexpected packet")
			} else {
				hello = &tlsx.ServerHelloBasic{}
				err := hello.Unmarshal(tcp.LayerPayload())
				if err != nil {
					b.Fatal("invalid packet", err)
				}
			}
		}
	}
	return
}

/*
 *	Tests
 */

func TestDigestHexJa3sCorrect(t *testing.T) {

	p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
	if p.ErrorLayer() != nil {
		t.Fatal(p.ErrorLayer().Error())
	}

	hash := ja3.DigestHexPacketJa3s(p)
	if hash != "5b94af9bf6efc9dea416841602004fbb" {
		t.Fatal(hash, "!=", "5b94af9bf6efc9dea416841602004fbb")
	}
}

func TestPcapWithNonEthernetEncapsulation(t *testing.T) {
	var (
		b bytes.Buffer
	)

	// read test2.pcap and generate fingerprints
	ja3.ReadFileJSON("test2.pcap", &b, true)

	if b.String() == "" {
		t.Fatal("expected output")
	}
}

func TestDigestHexJa3sComparePcap(t *testing.T) {

	// Step 1: Read output of reference implementation
	data, err := ioutil.ReadFile("ja3s_test.log")
	if err != nil {
		t.Fatal(err)
	}

	// Step 2: generate output wth goja3 and read into buffer
	var (
		b bytes.Buffer
	)

	// read test.pcap and generate fingerprints
	ja3.ReadFileJa3s("test.pcap", &b)

	// Step 3: compare the number of fingerprints and then the digest for each record
	if len(data) != len(b.Bytes()) {
		t.Fatal("len(records) != len(referenceRecords): ", len(data), " != ", len(b.Bytes()))
	}

	reference := bytes.Split(data, []byte{'\n'})

	// range reference records
	for i, r := range bytes.Split(b.Bytes(), []byte{'\n'}) {

		// compare reference digest to the one produced by goja3
		if string(r) != string(reference[i]) {
			t.Fatal("message does not match reference implementation: ", string(r), " != ", string(reference[i]))
		}
	}
}

/*
 *	Benchmarks
 */

func BenchmarkDigestHexPacketJa3s(b *testing.B) {

	p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ja3.DigestHexPacketJa3s(p)
	}
}

func BenchmarkDigestPacketJa3s(b *testing.B) {
	p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)
	if p.ErrorLayer() != nil {
		b.Fatal(p.ErrorLayer().Error())
	}

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ja3.DigestPacketJa3s(p)
	}
}

func BenchmarkBarePacketJa3s(b *testing.B) {
	p := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)
	if p.ErrorLayer() != nil {
		b.Fatal(p.ErrorLayer().Error())
	}

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ja3.BarePacketJa3s(p)
	}
}

func BenchmarkDigestHexJa3s(b *testing.B) {
	var (
		p     = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
		hello = getServerHello(p, b)
	)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ja3.DigestHexJa3s(hello)
	}
}

func BenchmarkDigestJa3s(b *testing.B) {

	var (
		p     = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
		hello = getServerHello(p, b)
	)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ja3.DigestJa3s(hello)
	}
}

func BenchmarkBareJa3s(b *testing.B) {

	var (
		p     = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)
		hello = getServerHello(p, b)
	)

	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ja3.BareJa3s(hello)
	}
}


================================================
FILE: ja3s_test.log
================================================
[192.168.0.104:47851] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d
[192.168.0.104:47852] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:47436] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47437] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47438] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47439] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47440] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47441] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:47442] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42577] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42938] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42939] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42940] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42941] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42942] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:42949] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:60089] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d
[192.168.0.109:60090] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60093] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60096] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60097] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60098] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60099] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60091] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60094] JA3S: 769,4, --> 53611273a714cb4789c8222932efd5a7
[192.168.0.109:60092] JA3S: 769,4, --> 53611273a714cb4789c8222932efd5a7
[192.168.0.109:60100] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60101] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60102] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60103] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60104] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60105] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60106] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60107] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60108] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:60109] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff
[192.168.0.109:50499] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50539] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50540] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50541] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50542] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50543] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.109:50544] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c
[192.168.0.102:7298] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7301] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7303] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7304] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7305] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7306] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7307] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7308] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7309] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7310] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7311] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7312] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7323] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7324] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7326] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7327] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7328] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7329] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7330] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7331] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7332] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7333] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7334] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7335] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7341] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7342] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7343] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891
[192.168.0.102:7344] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891


================================================
FILE: json.go
================================================
//go:build !ja3_disable_gopacket

/*
 * JA3 - TLS Client Hello Hash
 * Copyright (c) 2017, Salesforce.com, Inc.
 * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ja3

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"strconv"
	"time"

	"github.com/gopacket/gopacket"
)

// Record contains all information for a calculated JA3
type Record struct {
	DestinationIP   string  `json:"destination_ip"`
	DestinationPort int     `json:"destination_port"`
	JA3             string  `json:"ja3"`
	JA3Digest       string  `json:"ja3_digest"`
	JA3S            string  `json:"ja3s"`
	JA3SDigest      string  `json:"ja3s_digest"`
	SourceIP        string  `json:"source_ip"`
	SourcePort      int     `json:"source_port"`
	Timestamp       float64 `json:"timestamp"`
}

// ReadFileJSON reads the PCAP file at the given path
// and prints out all packets containing JA3 digests formatted as JSON to the supplied io.Writer
func ReadFileJSON(file string, out io.Writer, doJA3s bool) {

	r, f, link, err := openPcap(file)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	var records []*Record

	for {
		// read packet data
		data, ci, err := r.ReadPacketData()
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}

		var (
			// create gopacket
			p = gopacket.NewPacket(data, link, gopacket.Lazy)

			// get JA3 if possible
			bare     = BarePacket(p)
			isServer bool
		)

		if doJA3s && len(bare) == 0 {
			bare = BarePacketJa3s(p)
			isServer = true
		}

		// check if we got a result
		if len(bare) > 0 {

			var (
				nl = p.NetworkLayer()
				tl = p.TransportLayer()
			)

			// stop if either network or transport layer is nil
			if tl == nil || nl == nil {
				fmt.Println("error: ", nl, tl, p.Dump())
				continue
			}

			r := &Record{
				DestinationIP:   nl.NetworkFlow().Dst().String(),
				DestinationPort: int(binary.BigEndian.Uint16(tl.TransportFlow().Dst().Raw())),
				SourceIP:        nl.NetworkFlow().Src().String(),
				SourcePort:      int(binary.BigEndian.Uint16(tl.TransportFlow().Src().Raw())),
				Timestamp:       timeToFloat(ci.Timestamp),
			}

			if isServer {
				r.JA3S = string(bare)
				r.JA3SDigest = BareToDigestHex(bare)
			} else {
				r.JA3 = string(bare)
				r.JA3Digest = BareToDigestHex(bare)
			}

			// append record and populate all fields
			records = append(records, r)
		}
	}

	// make it pretty please
	b, err := json.MarshalIndent(records, "", "    ")
	if err != nil {
		panic(err)
	}

	if string(b) != "null" { // no matches will result in "null" json
		// write to output io.Writer
		_, err = out.Write(b)
		if err != nil {
			panic(err)
		}
	}

	if Debug {
		fmt.Println(len(records), "fingerprints.")
	}
}

// convert a time.Time to a string timestamp in the format seconds.microseconds
func timeToString(t time.Time) string {
	micro := fmt.Sprintf("%06d", t.Nanosecond()/1000)
	return strconv.FormatInt(t.Unix(), 10) + "." + micro
}

func timeStringToFloat64(t string) float64 {
	f, err := strconv.ParseFloat(t, 64)
	if err != nil {
		fmt.Println("[ERROR] failed to convert", t, "to float64. error:", err)
	}
	return f
}

func timeToFloat(t time.Time) float64 {
	return timeStringToFloat64(timeToString(t))
}


================================================
FILE: live.go
================================================
package ja3

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/gopacket/gopacket"
	"github.com/gopacket/gopacket/layers"
	"github.com/gopacket/gopacket/pcap"
)

// ReadInterface reads packets from the named interface
// if asJSON is true the results will be dumped as newline separated JSON objects
// otherwise CSV will be printed to the supplied io.Writer.
func ReadInterface(iface string, out io.Writer, separator string, ja3s bool, asJSON bool, snaplen int, promisc bool, timeout time.Duration) {

	h, err := pcap.OpenLive(iface, int32(snaplen), promisc, timeout)
	if err != nil {
		panic(err)
	}
	defer h.Close()

	if !asJSON {
		columns := []string{"timestamp", "source_ip", "source_port", "destination_ip", "destination_port", "ja3_digest"}

		_, err = out.Write([]byte(strings.Join(columns, separator) + "\n"))
		if err != nil {
			panic(err)
		}
	}

	count := 0
	for {
		// read packet data
		data, ci, err := h.ReadPacketData()
		if err == io.EOF {
			if Debug {
				fmt.Println(count, "fingerprints.")
			}
			return
		} else if err != nil {
			panic(err)
		}

		var (
			// create gopacket
			p        = gopacket.NewPacket(data, layers.LinkTypeEthernet, gopacket.Lazy)
			bare     = BarePacket(p)
			isServer bool
		)

		if ja3s && len(bare) == 0 {
			bare = BarePacketJa3s(p)
			isServer = true
		}

		// check if we got a result
		if len(bare) > 0 {

			count++

			var (
				b  strings.Builder
				nl = p.NetworkLayer()
				tl = p.TransportLayer()
			)

			// got a bare but no transport or network layer
			if tl == nil || nl == nil {
				if Debug {
					fmt.Println("got a nil layer: ", nl, tl, p.Dump(), string(bare))
				}
				continue
			}

			r := &Record{
				DestinationIP:   nl.NetworkFlow().Dst().String(),
				DestinationPort: int(binary.BigEndian.Uint16(tl.TransportFlow().Dst().Raw())),
				SourceIP:        nl.NetworkFlow().Src().String(),
				SourcePort:      int(binary.BigEndian.Uint16(tl.TransportFlow().Src().Raw())),
				Timestamp:       timeToFloat(ci.Timestamp),
			}

			digest := BareToDigestHex(bare)
			if isServer {
				r.JA3S = string(bare)
				r.JA3SDigest = digest
			} else {
				r.JA3 = string(bare)
				r.JA3Digest = digest
			}

			if asJSON {

				// make it pretty please
				b, err := json.MarshalIndent(r, "", "    ")
				if err != nil {
					panic(err)
				}

				if string(b) != "null" { // no matches will result in "null" json
					// write to output io.Writer
					_, err = out.Write(b)
					if err != nil {
						panic(err)
					}

					_, err = out.Write([]byte("\n"))
					if err != nil {
						panic(err)
					}
				}
			} else { // CSV
				b.WriteString(timeToString(ci.Timestamp))
				b.WriteString(separator)
				b.WriteString(nl.NetworkFlow().Src().String())
				b.WriteString(separator)
				b.WriteString(tl.TransportFlow().Src().String())
				b.WriteString(separator)
				b.WriteString(nl.NetworkFlow().Dst().String())
				b.WriteString(separator)
				b.WriteString(tl.TransportFlow().Dst().String())
				b.WriteString(separator)
				b.WriteString(digest)
				b.WriteString("\n")

				_, err := out.Write([]byte(b.String()))
				if err != nil {
					panic(err)
				}
			}
		}
	}
}


================================================
FILE: utils.go
================================================
package ja3

import (
	"fmt"
	"github.com/gopacket/gopacket/layers"
	"os"

	"github.com/gopacket/gopacket"
	"github.com/gopacket/gopacket/pcapgo"
)

// PacketSource means we can read Packets.
type PacketSource interface {
	ReadPacketData() ([]byte, gopacket.CaptureInfo, error)
}

func openPcap(file string) (PacketSource, *os.File, layers.LinkType, error) {

	// get file handle
	f, err := os.Open(file)
	if err != nil {
		panic(err)
	}

	var (
		reader       PacketSource
		linkType     layers.LinkType
	)

	// try to create pcap reader
	pcapReader, errPcap := pcapgo.NewReader(f)
	if errPcap != nil {

		// maybe its a PCAPNG
		// reopen file, otherwise offsets will be wrong
		err = f.Close()
		if err != nil {
			panic(err)
		}
		f, err = os.Open(file)
		if err != nil {
			panic(err)
		}

		// try to create pcapng reader
		ngReader, errPcapNg := pcapgo.NewNgReader(f, pcapgo.DefaultNgReaderOptions)
		if errPcapNg != nil {
			// nope
			fmt.Println("pcap error:", errPcap)
			fmt.Println("pcap-ng error:", errPcapNg)
			panic("cannot open PCAP file")
		}

		linkType = ngReader.LinkType()
		reader = ngReader
	} else {
		linkType = pcapReader.LinkType()
		reader = pcapReader
	}

	return reader, f, linkType, err
}
Download .txt
gitextract_5b0w5x7_/

├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── TODO.md
├── cmd/
│   └── main.go
├── csv.go
├── go.mod
├── go.sum
├── gopacket.go
├── install.sh
├── ja3.go
├── ja3_output_testpcap.json
├── ja3_test.go
├── ja3s.go
├── ja3s_pcap.go
├── ja3s_test.go
├── ja3s_test.log
├── json.go
├── live.go
├── test.pcap
├── test2.pcap
└── utils.go
Download .txt
SYMBOL INDEX (45 symbols across 11 files)

FILE: cmd/main.go
  constant version (line 42) | version = "v1.0.2"
  function main (line 44) | func main() {

FILE: csv.go
  function ReadFileCSV (line 29) | func ReadFileCSV(file string, out io.Writer, separator string, doJA3s bo...

FILE: gopacket.go
  function DigestPacket (line 30) | func DigestPacket(p gopacket.Packet) [md5.Size]byte {
  function DigestPacketJa3s (line 37) | func DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte {
  function DigestHexPacket (line 43) | func DigestHexPacket(p gopacket.Packet) string {
  function DigestHexPacketJa3s (line 56) | func DigestHexPacketJa3s(p gopacket.Packet) string {
  function BarePacket (line 69) | func BarePacket(p gopacket.Packet) []byte {
  function BarePacketJa3s (line 104) | func BarePacketJa3s(p gopacket.Packet) []byte {

FILE: ja3.go
  function BareToDigestHex (line 47) | func BareToDigestHex(bare []byte) string {
  function Digest (line 53) | func Digest(hello *tlsx.ClientHelloBasic) [md5.Size]byte {
  function DigestHex (line 58) | func DigestHex(hello *tlsx.ClientHelloBasic) string {
  function Bare (line 76) | func Bare(hello *tlsx.ClientHelloBasic) []byte {

FILE: ja3_test.go
  function getHello (line 47) | func getHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ClientHelloB...
  function TestDigestHexCorrect (line 74) | func TestDigestHexCorrect(t *testing.T) {
  function TestDigestHexComparePcap (line 87) | func TestDigestHexComparePcap(t *testing.T) {
  function BenchmarkDigestHexPacket (line 138) | func BenchmarkDigestHexPacket(b *testing.B) {
  function BenchmarkDigestPacket (line 149) | func BenchmarkDigestPacket(b *testing.B) {
  function BenchmarkBarePacket (line 162) | func BenchmarkBarePacket(b *testing.B) {
  function BenchmarkDigestHex (line 175) | func BenchmarkDigestHex(b *testing.B) {
  function BenchmarkDigest (line 188) | func BenchmarkDigest(b *testing.B) {
  function BenchmarkBare (line 202) | func BenchmarkBare(b *testing.B) {

FILE: ja3s.go
  function BareToDigestHexJa3s (line 25) | func BareToDigestHexJa3s(bare []byte) string {
  function DigestJa3s (line 31) | func DigestJa3s(hello *tlsx.ServerHelloBasic) [md5.Size]byte {
  function DigestHexJa3s (line 36) | func DigestHexJa3s(hello *tlsx.ServerHelloBasic) string {
  function BareJa3s (line 44) | func BareJa3s(hello *tlsx.ServerHelloBasic) []byte {

FILE: ja3s_pcap.go
  function ReadFileJa3s (line 15) | func ReadFileJa3s(file string, out io.Writer) {

FILE: ja3s_test.go
  function getServerHello (line 32) | func getServerHello(p gopacket.Packet, b *testing.B) (hello *tlsx.Server...
  function TestDigestHexJa3sCorrect (line 64) | func TestDigestHexJa3sCorrect(t *testing.T) {
  function TestPcapWithNonEthernetEncapsulation (line 77) | func TestPcapWithNonEthernetEncapsulation(t *testing.T) {
  function TestDigestHexJa3sComparePcap (line 90) | func TestDigestHexJa3sComparePcap(t *testing.T) {
  function BenchmarkDigestHexPacketJa3s (line 127) | func BenchmarkDigestHexPacketJa3s(b *testing.B) {
  function BenchmarkDigestPacketJa3s (line 138) | func BenchmarkDigestPacketJa3s(b *testing.B) {
  function BenchmarkBarePacketJa3s (line 151) | func BenchmarkBarePacketJa3s(b *testing.B) {
  function BenchmarkDigestHexJa3s (line 164) | func BenchmarkDigestHexJa3s(b *testing.B) {
  function BenchmarkDigestJa3s (line 177) | func BenchmarkDigestJa3s(b *testing.B) {
  function BenchmarkBareJa3s (line 191) | func BenchmarkBareJa3s(b *testing.B) {

FILE: json.go
  type Record (line 31) | type Record struct
  function ReadFileJSON (line 45) | func ReadFileJSON(file string, out io.Writer, doJA3s bool) {
  function timeToString (line 133) | func timeToString(t time.Time) string {
  function timeStringToFloat64 (line 138) | func timeStringToFloat64(t string) float64 {
  function timeToFloat (line 146) | func timeToFloat(t time.Time) float64 {

FILE: live.go
  function ReadInterface (line 19) | func ReadInterface(iface string, out io.Writer, separator string, ja3s b...

FILE: utils.go
  type PacketSource (line 13) | type PacketSource interface
  function openPcap (line 17) | func openPcap(file string) (PacketSource, *os.File, layers.LinkType, err...
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (140K chars).
[
  {
    "path": ".gitignore",
    "chars": 6,
    "preview": ".idea/"
  },
  {
    "path": "AUTHORS",
    "chars": 51,
    "preview": "Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>"
  },
  {
    "path": "LICENSE",
    "chars": 1483,
    "preview": "Copyright (c) 2017, Salesforce.com, Inc.  \nAll rights reserved.\n\nRedistribution and use in source and binary forms, with"
  },
  {
    "path": "README.md",
    "chars": 13232,
    "preview": "# JA3\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/dreadl0ck/ja3)](https://goreportcard.com/report/gith"
  },
  {
    "path": "TODO.md",
    "chars": 97,
    "preview": "# TODO\n\n- compare number of records to output of python implementation\n- 0 allocations for Bare()"
  },
  {
    "path": "cmd/main.go",
    "chars": 2584,
    "preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
  },
  {
    "path": "csv.go",
    "chars": 2832,
    "preview": "//go:build !ja3_disable_gopacket\n\n/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this "
  },
  {
    "path": "go.mod",
    "chars": 257,
    "preview": "module github.com/dreadl0ck/ja3\n\nrequire (\n\tgithub.com/dreadl0ck/tlsx v1.1.1\n\tgithub.com/gopacket/gopacket v1.4.0\n)\n\nreq"
  },
  {
    "path": "go.sum",
    "chars": 1219,
    "preview": "github.com/dreadl0ck/tlsx v1.1.1 h1:fclhQwhO2HmRm568JFBEX0G6S7hFGBsN/JBFh9RqgVU=\ngithub.com/dreadl0ck/tlsx v1.1.1/go.mod"
  },
  {
    "path": "gopacket.go",
    "chars": 3482,
    "preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
  },
  {
    "path": "install.sh",
    "chars": 83,
    "preview": "#!/bin/bash\n\ngo build -o $(go env GOPATH)/bin/goja3 -i github.com/dreadl0ck/ja3/cmd"
  },
  {
    "path": "ja3.go",
    "chars": 6251,
    "preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
  },
  {
    "path": "ja3_output_testpcap.json",
    "chars": 67850,
    "preview": "[\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"ja3\": \"768,22-19-10-102-5-4-"
  },
  {
    "path": "ja3_test.go",
    "chars": 6112,
    "preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
  },
  {
    "path": "ja3s.go",
    "chars": 2911,
    "preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
  },
  {
    "path": "ja3s_pcap.go",
    "chars": 1477,
    "preview": "//go:build !ja3_disable_gopacket\n\npackage ja3\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/gopacket/gopacket\"\n)\n\n// R"
  },
  {
    "path": "ja3s_test.go",
    "chars": 7796,
    "preview": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden"
  },
  {
    "path": "ja3s_test.log",
    "chars": 5135,
    "preview": "[192.168.0.104:47851] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d\n[192.168.0.104:47852] JA3S: 769,5, --> 9aeeb849"
  },
  {
    "path": "json.go",
    "chars": 3729,
    "preview": "//go:build !ja3_disable_gopacket\n\n/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this "
  },
  {
    "path": "live.go",
    "chars": 3190,
    "preview": "package ja3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gopacket/gopacke"
  },
  {
    "path": "utils.go",
    "chars": 1222,
    "preview": "package ja3\n\nimport (\n\t\"fmt\"\n\t\"github.com/gopacket/gopacket/layers\"\n\t\"os\"\n\n\t\"github.com/gopacket/gopacket\"\n\t\"github.com/"
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the dreadl0ck/ja3 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (127.9 KB), approximately 54.6k tokens, and a symbol index with 45 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!