[
  {
    "path": ".gitignore",
    "content": ".idea/"
  },
  {
    "path": "AUTHORS",
    "content": "Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2017, Salesforce.com, Inc.  \nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n* 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.\n\n* 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.\n\nTHIS 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."
  },
  {
    "path": "README.md",
    "content": "# JA3\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/dreadl0ck/ja3)](https://goreportcard.com/report/github.com/dreadl0ck/ja3)\n[![License](https://img.shields.io/badge/License-BSDv3-blue.svg)](https://raw.githubusercontent.com/dreadl0ck/ja3/master/docs/LICENSE)\n[![Golang](https://img.shields.io/badge/Go-1.10-blue.svg)](https://golang.org)\n![Linux](https://img.shields.io/badge/Supports-Linux-green.svg)\n![macOS](https://img.shields.io/badge/Supports-macOS-green.svg)\n![Windows](https://img.shields.io/badge/Supports-Windows-green.svg)\n[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/dreadl0ck/ja3)\n\nJA3 is a technique developed by Salesforce, to fingerprint the TLS client and server hellos.\n\nThe official python implementation can be found [here](https://github.com/salesforce/ja3).\nMore details can be found in their blog post: https://engineering.salesforce.com/open-sourcing-ja3-92c9e53c3c41\n\nThis package provides a pure golang implementation of both the client and server hash functions,\nwith unit tests to ensure correct behavior and performance. \nThis implementation uses [gopacket/gopacket](https://github.com/gopacket/gopacket) for packet decoding.\n\nIf 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:\n\n```\ngo build -tags ja3_disable_gopacket\n```\n\nThe **[bradleyfalzon/tlsx](https://github.com/bradleyfalzon/tlsx)** fork used for parsing the TLS handshakes (**[dreadl0ck/tlsx](https://github.com/dreadl0ck/tlsx)**),\nhas been extended to support extracting the server hello message, unit tests, \nbenchmarks and a faster decoding interface for use with Ja3 fingerprinting.\n\nAssembly of the ja3 bare was previously implemented with the golang 1.10 **strings.Builder** type,\nhowever using byte slices for this turned out to be faster and causes less allocations. (Thanks for the tips @lukechampine)\nThanks to @guigzzz for his pull request on further reducing allocations!\n\n## Package Usage\n\nThis package exports the following API:\n\nLive Capture from interface, outputs CSV with configurable separator or JSON:\n\n```go\nfunc ReadInterface(iface string, out io.Writer, separator string, ja3s bool, asJSON bool, snaplen int, promisc bool, timeout time.Duration) {\n```\n\nFiles:\n\n```go\nfunc ReadFileJSON(file string, out io.Writer, doJA3s bool)\n```\n```go\nfunc ReadFileCSV(file string, out io.Writer, separator string, doJA3s bool)\n```\n\nRead file and only print Ja3s values, mimics the python implementation from salesforce:\n\n```go\nfunc ReadFileJa3s(file string, out io.Writer)\n```\n\nUsing gopacket.Packet:\n\n```go\nfunc BarePacket(p gopacket.Packet) []byte\n```\n```go\nfunc BarePacketJa3s(p gopacket.Packet) []byte\n```\n```go\nfunc DigestPacket(p gopacket.Packet) [md5.Size]byte\n```\n```go\nfunc DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte \n```\n\n```go\nfunc DigestHexPacket(p gopacket.Packet) string\n```\n```go\nfunc DigestHexPacketJa3s(p gopacket.Packet) string \n```\n\nUsing tlsx.ClientHello:\n\n```go\nfunc Bare(hello *tlsx.ClientHello) []byte\n```\n```go\nfunc BareJa3s(hello *tlsx.ServerHello) []byte\n```\n```go\nfunc Digest(hello *tlsx.ClientHello) [md5.Size]byte\n```\n```go\nfunc DigestJa3s(hello *tlsx.ServerHello) [md5.Size]byte\n```\n```go\nfunc DigestHex(hello *tlsx.ClientHello) string\n```\n```go\nfunc DigestHexJa3s(hello *tlsx.ServerHello) string\n```\n\n## Commandline Tool\n\nThe commandline program mimics the python reference implementation by default.\nIt reads the pcap file and prints the all extracted client hellos as JSON array to stdout.\nPrinting output as CSV, tab separated values or with a custom separator is also supported.\n\n    $ goja3 -h\n    Usage of goja3:\n      -csv\n        \tprint as CSV\n      -debug\n        \ttoggle debug mode\n      -iface string\n        \tspecify network interface to read packets from\n      -ja3s\n        \tdump ja3s only\n      -json\n        \tprint as JSON array (default true)\n      -read string\n        \tread PCAP file\n      -separator string\n        \tset a custom separator (default \",\")\n      -tsv\n        \tprint as TAB separated values\n\nBenchmark of the python reference implementation VS this one,\non a 109 MB PCAP dumpfile (DEF CON 23 ICS Village.pcap).\nThis dump file is interesting for comparison because it contains handshakes without extensions set,\nas well as TLS traffic over non standard ports.\n\nPython implementation:\n\n    # https://github.com/salesforce/ja3\n    $ time ja3 -a DEF\\ CON\\ 23\\ ICS\\ Village.pcap &> /dev/null\n    \n    real\t0m29.031s\n    user\t0m28.803s\n    sys\t0m0.141s\n\nGo implementation:\n\n    # github.com/dreadl0ck/ja3\n    # disable server fingerprint generation to mimic the behavior of the reference implementation\n    $ time goja3 -ja3s=false -json -read DEF\\ CON\\ 23\\ ICS\\ Village.pcap &> /dev/null\n    \n    real\t0m0.996s\n    user\t0m2.245s\n    sys\t0m0.166s\n\nOutput:\n\n    [\n        ...\n        {\n            \"destination_ip\": \"192.168.0.60\",\n            \"destination_port\": 443,\n            \"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\",\n            \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n            \"ja3s\": \"\",\n            \"ja3s_digest\": \"\",\n            \"source_ip\": \"192.168.0.112\",\n            \"source_port\": 59935,\n            \"timestamp\": \"1438978270.855224\"\n        },\n        {\n            \"destination_ip\": \"192.168.0.30\",\n            \"destination_port\": 443,\n            \"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\",\n            \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n            \"ja3s\": \"\",\n            \"ja3s_digest\": \"\",\n            \"source_ip\": \"192.168.0.112\",\n            \"source_port\": 42455,\n            \"timestamp\": \"1438978270.855228\"\n        }\n    ]\n\n## JA3 Details\n\nJA3 gathers the decimal values of the bytes for the following fields: **SSL Version, Accepted Ciphers, List of Extensions, Elliptic Curves, and Elliptic Curve Formats**.\n\nIt then concatenates those values together in order, using a “,” to delimit each field and a “-” to delimit each value in each field.\n\n**The field order is as follows:**\n\n    SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats\n\n**Example:**\n\n    769,47–53–5–10–49161–49162–49171–49172–50–56–19–4,0–10–11,23–24–25,0\n\nIf there are no SSL Extensions in the Client Hello, the fields are left empty.\n\n**Example:**\n\n    769,4–5–10–9–100–98–3–6–19–18–99,,,\n\nThese strings are then MD5 hashed to produce an easily consumable and shareable 32 character fingerprint. \n\nThis is the JA3 SSL Client Fingerprint.\n\nJA3 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.\n\nJA3 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.\n\nFor 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\n\n## JA3S Details\n\nJA3S is JA3 for the Server side of the SSL/TLS communication and fingerprints how servers respond to particular clients. \n\nJA3S uses the following field order:\n```\nSSLVersion,Cipher,SSLExtension\n```\nWith 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.\n\nFor the Trickbot example:\n```\nJA3 = 6734f37431670b3ab4292b8f60f29984 ( Fingerprint of Trickbot )\nJA3S = 623de93db17d313345d7ea481e7443cf ( Fingerprint of Command and Control Server Response )\n```\nFor the Emotet example:\n```\nJA3 = 4d7a28d6f2263ed61de88ca66eb011e3 ( Fingerprint of Emotet )\nJA3S = 80b3a14bccc8598a1f3bbe83e71f735f ( Fingerprint of Command and Control Server Response )\n```\n\nIn 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.\n\n## Benchmarks & Tests\n\nRun the benchmarks and the correctness test with:\n\n    $ go test -v -bench=.\n    === RUN   TestDigestHexCorrect\n    --- PASS: TestDigestHexCorrect (0.00s)\n    === RUN   TestDigestHexComparePcap\n    --- PASS: TestDigestHexComparePcap (0.10s)\n    === RUN   TestDigestHexJa3sCorrect\n    --- PASS: TestDigestHexJa3sCorrect (0.00s)\n    === RUN   TestDigestHexJa3sComparePcap\n    --- PASS: TestDigestHexJa3sComparePcap (0.10s)\n    goos: darwin\n    goarch: amd64\n    pkg: github.com/dreadl0ck/ja3\n    BenchmarkDigestHexPacket\n    BenchmarkDigestHexPacket-12        \t  781578\t      1499 ns/op\t     352 B/op\t       9 allocs/op\n    BenchmarkDigestPacket\n    BenchmarkDigestPacket-12           \t  827286\t      1406 ns/op\t     288 B/op\t       7 allocs/op\n    BenchmarkBarePacket\n    BenchmarkBarePacket-12             \t 1000000\t      1212 ns/op\t     288 B/op\t       7 allocs/op\n    BenchmarkDigestHex\n    BenchmarkDigestHex-12              \t  895094\t      1278 ns/op\t     256 B/op\t       3 allocs/op\n    BenchmarkDigest\n    BenchmarkDigest-12                 \t  996800\t      1318 ns/op\t     192 B/op\t       1 allocs/op\n    BenchmarkBare\n    BenchmarkBare-12                   \t 1101476\t      1188 ns/op\t     192 B/op\t       1 allocs/op\n    BenchmarkDigestHexPacketJa3s\n    BenchmarkDigestHexPacketJa3s-12    \t 1654360\t       619 ns/op\t     120 B/op\t       4 allocs/op\n    BenchmarkDigestPacketJa3s\n    BenchmarkDigestPacketJa3s-12       \t 2328241\t       513 ns/op\t      56 B/op\t       2 allocs/op\n    BenchmarkBarePacketJa3s\n    BenchmarkBarePacketJa3s-12         \t 3294260\t       387 ns/op\t      56 B/op\t       2 allocs/op\n    BenchmarkDigestHexJa3s\n    BenchmarkDigestHexJa3s-12          \t 2476090\t       464 ns/op\t     112 B/op\t       3 allocs/op\n    BenchmarkDigestJa3s\n    BenchmarkDigestJa3s-12             \t 3321294\t       357 ns/op\t      48 B/op\t       1 allocs/op\n    BenchmarkBareJa3s\n    BenchmarkBareJa3s-12               \t 4964508\t       238 ns/op\t      48 B/op\t       1 allocs/op\n    PASS\n    ok  \tgithub.com/dreadl0ck/ja3\t18.414s\n    \nPerformance comparison with other implementations:\n\n    # https://github.com/open-ch/ja3\n    $ time ja3exporter -pcap DEF\\ CON\\ 23\\ ICS\\ Village.pcap &> /dev/null\n    \n    real\t0m0.912s\n    user\t0m1.979s\n    sys\t0m0.142s\n    \n    # github.com/dreadl0ck/ja3\n    # disable server fingerprint generation to mimic the behavior of the other implementations\n    $ time goja3 -ja3s=false -json -read DEF\\ CON\\ 23\\ ICS\\ Village.pcap &> /dev/null\n    \n    real\t0m0.996s\n    user\t0m2.245s\n    sys\t0m0.166s\n    \n    # https://github.com/salesforce/ja3\n    $ time ja3 -a DEF\\ CON\\ 23\\ ICS\\ Village.pcap &> /dev/null\n    \n    real\t0m29.031s\n    user\t0m28.803s\n    sys\t0m0.141s\n    \nNumber of records detected:\n\n    # https://github.com/open-ch/ja3\n    $ time ja3exporter -pcap DEF\\ CON\\ 23\\ ICS\\ Village.pcap | wc -l\n    138\n    \n    # github.com/dreadl0ck/ja3\n    $ goja3 -ja3s=false -json -read DEF\\ CON\\ 23\\ ICS\\ Village.pcap | jq '. | length'\n    138\n    \n    # https://github.com/salesforce/ja3\n    $ time ja3 -a DEF\\ CON\\ 23\\ ICS\\ Village.pcap | jq '. | length'\n    138\n\n    # https://github.com/salesforce/ja3\n    # without -a flag, only tls traffic over port 443 will be extracted\n    $ time ja3 DEF\\ CON\\ 23\\ ICS\\ Village.pcap | jq '. | length'\n    96\n    \n## Notes\n\nThe ja3_output_testpcap.json file for output comparison in the unit tests\nwas generated with the python reference implementation using:\n\n    ja3 -a test.pcap > ja3_output_testpcap.json\n"
  },
  {
    "path": "TODO.md",
    "content": "# TODO\n\n- compare number of records to output of python implementation\n- 0 allocations for Bare()"
  },
  {
    "path": "cmd/main.go",
    "content": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/dreadl0ck/ja3\"\n\t\"github.com/gopacket/gopacket/pcap\"\n\t\"os\"\n)\n\nvar (\n\tflagJSON      = flag.Bool(\"json\", true, \"print as JSON array\")\n\tflagCSV       = flag.Bool(\"csv\", false, \"print as CSV\")\n\tflagTSV       = flag.Bool(\"tsv\", false, \"print as TAB separated values\")\n\tflagSeparator = flag.String(\"separator\", \",\", \"set a custom separator\")\n\tflagInput     = flag.String(\"read\", \"\", \"read PCAP file\")\n\tflagDebug     = flag.Bool(\"debug\", false, \"toggle debug mode\")\n\tflagInterface = flag.String(\"iface\", \"\", \"specify network interface to read packets from\")\n\tflagJa3S      = flag.Bool(\"ja3s\", true, \"include ja3 server hashes (ja3s)\")\n\tflagOnlyJa3S  = flag.Bool(\"ja3s-only\", false, \"dump ja3s only\")\n\tflagSnaplen   = flag.Int(\"snaplen\", 1514, \"default snap length for ethernet frames\")\n\tflagPromisc   = flag.Bool(\"promisc\", true, \"capture in promiscuous mode (requires root)\")\n\t// https://godoc.org/github.com/gopacket/gopacket/pcap#hdr-PCAP_Timeouts\n\tflagTimeout   = flag.Duration(\"timeout\", pcap.BlockForever, \"timeout for collecting packet batches\")\n\tflagVersion   = flag.Bool(\"version\", false, \"display version and exit\")\n)\n\nconst version = \"v1.0.2\"\n\nfunc main() {\n\n\tflag.Parse()\n\n\tif *flagVersion {\n\t\tfmt.Println(version)\n\t\tos.Exit(0)\n\t}\n\n\tja3.Debug = *flagDebug\n\n\tif *flagInterface != \"\" {\n\t\tja3.ReadInterface(*flagInterface, os.Stdout, *flagSeparator, *flagJa3S, *flagJSON, *flagSnaplen, *flagPromisc, *flagTimeout)\n\t\treturn\n\t}\n\n\tif *flagInput == \"\" {\n\t\tfmt.Println(\"use the -read flag to supply an input file.\")\n\t\tos.Exit(1)\n\t}\n\n\tif *flagOnlyJa3S {\n\t\tja3.ReadFileJa3s(*flagInput, os.Stdout)\n\t\treturn\n\t}\n\n\tif *flagTSV {\n\t\tja3.ReadFileCSV(*flagInput, os.Stdout, \"\\t\", *flagJa3S)\n\t\treturn\n\t}\n\n\tif *flagCSV {\n\t\tja3.ReadFileCSV(*flagInput, os.Stdout, *flagSeparator, *flagJa3S)\n\t\treturn\n\t}\n\n\tif *flagJSON {\n\t\tja3.ReadFileJSON(*flagInput, os.Stdout, *flagJa3S)\n\t}\n}\n"
  },
  {
    "path": "csv.go",
    "content": "//go:build !ja3_disable_gopacket\n\n/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage ja3\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/gopacket/gopacket\"\n)\n\n// ReadFileCSV reads the PCAP file at the given path\n// and prints out all packets containing JA3 digests to the supplied io.Writer\nfunc ReadFileCSV(file string, out io.Writer, separator string, doJA3s bool) {\n\n\tr, f, link, err := openPcap(file)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer f.Close()\n\n\tcolumns := []string{\"timestamp\", \"source_ip\", \"source_port\", \"destination_ip\", \"destination_port\", \"ja3_digest\", \"ja3s_digest\"}\n\t_, err = out.Write([]byte(strings.Join(columns, separator) + \"\\n\"))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcount := 0\n\tfor {\n\t\t// read packet data\n\t\tdata, ci, err := r.ReadPacketData()\n\t\tif err == io.EOF {\n\t\t\tif Debug {\n\t\t\t\tfmt.Println(count, \"fingerprints.\")\n\t\t\t}\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tvar (\n\t\t\t// create gopacket\n\t\t\tp = gopacket.NewPacket(data, link, gopacket.Lazy)\n\t\t\t// get JA3 if possible\n\t\t\tdigest   = DigestHexPacket(p)\n\t\t\tisServer bool\n\t\t)\n\n\t\tif doJA3s && digest == \"\" {\n\t\t\tdigest = DigestHexPacketJa3s(p)\n\t\t\tisServer = true\n\t\t}\n\n\t\t// check if we got a result\n\t\tif digest != \"\" {\n\n\t\t\tcount++\n\n\t\t\tvar (\n\t\t\t\tb  strings.Builder\n\t\t\t\tnl = p.NetworkLayer()\n\t\t\t\ttl = p.TransportLayer()\n\t\t\t)\n\n\t\t\t// got an a digest but no transport or network layer\n\t\t\tif tl == nil || nl == nil {\n\t\t\t\tif Debug {\n\t\t\t\t\tfmt.Println(\"got a nil layer: \", nl, tl, p.Dump(), digest)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tb.WriteString(timeToString(ci.Timestamp))\n\t\t\tb.WriteString(separator)\n\t\t\tb.WriteString(nl.NetworkFlow().Src().String())\n\t\t\tb.WriteString(separator)\n\t\t\tb.WriteString(tl.TransportFlow().Src().String())\n\t\t\tb.WriteString(separator)\n\t\t\tb.WriteString(nl.NetworkFlow().Dst().String())\n\t\t\tb.WriteString(separator)\n\t\t\tb.WriteString(tl.TransportFlow().Dst().String())\n\t\t\tb.WriteString(separator)\n\t\t\tif isServer {\n\t\t\t\tb.WriteString(\"\")\n\t\t\t\tb.WriteString(separator)\n\t\t\t\tb.WriteString(digest)\n\t\t\t} else { // client\n\t\t\t\tb.WriteString(digest)\n\t\t\t\tb.WriteString(separator)\n\t\t\t\tb.WriteString(\"\")\n\t\t\t}\n\t\t\tb.WriteString(\"\\n\")\n\n\t\t\t_, err := out.Write([]byte(b.String()))\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "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\nrequire (\n\tgolang.org/x/crypto v0.43.0 // indirect\n\tgolang.org/x/net v0.46.0 // indirect\n\tgolang.org/x/sys v0.37.0 // indirect\n)\n\ngo 1.24.0\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/dreadl0ck/tlsx v1.1.1 h1:fclhQwhO2HmRm568JFBEX0G6S7hFGBsN/JBFh9RqgVU=\ngithub.com/dreadl0ck/tlsx v1.1.1/go.mod h1:hqEz1A1Nt5lnFZh8nTE1iqkipCZGX7TWe+xXBWWPyo0=\ngithub.com/gopacket/gopacket v1.4.0 h1:cr1OlFpzksCkZHNO0eLjaSSOrMQnpPXg0j6qHIY3y2U=\ngithub.com/gopacket/gopacket v1.4.0/go.mod h1:EpvsxINeehp5qj4YMKMLf2/dekdhKn2IIAO/ZOifS7o=\ngithub.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=\ngithub.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=\ngithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=\ngithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngolang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=\ngolang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=\ngolang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=\ngolang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=\ngolang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=\ngolang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\n"
  },
  {
    "path": "gopacket.go",
    "content": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage ja3\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\n\t\"github.com/gopacket/gopacket\"\n\t\"github.com/gopacket/gopacket/layers\"\n\t\"github.com/dreadl0ck/tlsx\"\n)\n\n// DigestPacket returns the Ja3 digest\n// for a packet carrying a TLS Client Hello\n// or an empty byte slice\nfunc DigestPacket(p gopacket.Packet) [md5.Size]byte {\n\treturn md5.Sum(BarePacket(p))\n}\n\n// DigestPacket returns the Ja3s digest\n// for a packet carrying a TLS Server Hello\n// or an empty byte slice\nfunc DigestPacketJa3s(p gopacket.Packet) [md5.Size]byte {\n\treturn md5.Sum(BarePacketJa3s(p))\n}\n\n// DigestHexPacket returns the hex string for the packet\n// for a packet carrying a TLS Client Hello\nfunc DigestHexPacket(p gopacket.Packet) string {\n\n\tbare := BarePacket(p)\n\tif len(bare) == 0 {\n\t\treturn \"\"\n\t}\n\n\tsum := md5.Sum(bare)\n\treturn hex.EncodeToString(sum[:])\n}\n\n// DigestHexPacket returns the hex string for the packet\n// for a packet carrying a TLS Client Hello\nfunc DigestHexPacketJa3s(p gopacket.Packet) string {\n\n\tbare := BarePacketJa3s(p)\n\tif len(bare) == 0 {\n\t\treturn \"\"\n\t}\n\n\tsum := md5.Sum(bare)\n\treturn hex.EncodeToString(sum[:])\n}\n\n// BarePacket returns the Ja3 digest if the supplied packet contains a TLS client hello\n// otherwise returns an empty string\nfunc BarePacket(p gopacket.Packet) []byte {\n\tif tl := p.TransportLayer(); tl != nil {\n\t\tif tcp, ok := tl.(*layers.TCP); ok {\n\t\t\tif tcp.SYN {\n\t\t\t\t// Connection setup\n\t\t\t} else if tcp.FIN {\n\t\t\t\t// Connection teardown\n\t\t\t} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {\n\t\t\t\t// Acknowledgement packet\n\t\t\t} else if tcp.RST {\n\t\t\t\t// Unexpected packet\n\t\t\t} else {\n\t\t\t\t// data packet\n\t\t\t\tvar (\n\t\t\t\t\thello = tlsx.ClientHelloBasic{}\n\t\t\t\t\terr   = hello.Unmarshal(tcp.LayerPayload())\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tfmt.Println(err)\n\t\t\t\t\t\t//fmt.Println(p.Dump())\n\t\t\t\t\t}\n\t\t\t\t\treturn []byte{}\n\t\t\t\t}\n\n\t\t\t\t// return JA3 bare\n\t\t\t\treturn Bare(&hello)\n\t\t\t}\n\t\t}\n\t}\n\treturn []byte{}\n}\n\n// BarePacket returns the Ja3 digest if the supplied packet contains a TLS client hello\n// otherwise returns an empty string\nfunc BarePacketJa3s(p gopacket.Packet) []byte {\n\tif tl := p.TransportLayer(); tl != nil {\n\t\tif tcp, ok := tl.(*layers.TCP); ok {\n\t\t\tif tcp.SYN {\n\t\t\t\t// Connection setup\n\t\t\t} else if tcp.FIN {\n\t\t\t\t// Connection teardown\n\t\t\t} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {\n\t\t\t\t// Acknowledgement packet\n\t\t\t} else if tcp.RST {\n\t\t\t\t// Unexpected packet\n\t\t\t} else {\n\t\t\t\t// data packet\n\t\t\t\tvar (\n\t\t\t\t\thello = tlsx.ServerHelloBasic{}\n\t\t\t\t\terr   = hello.Unmarshal(tcp.LayerPayload())\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tfmt.Println(err, p.NetworkLayer().NetworkFlow(), p.TransportLayer().TransportFlow())\n\t\t\t\t\t\t//fmt.Println(p.Dump())\n\t\t\t\t\t}\n\t\t\t\t\treturn []byte{}\n\t\t\t\t}\n\n\t\t\t\t// return JA3 bare\n\t\t\t\treturn BareJa3s(&hello)\n\t\t\t}\n\t\t}\n\t}\n\treturn []byte{}\n}\n"
  },
  {
    "path": "install.sh",
    "content": "#!/bin/bash\n\ngo build -o $(go env GOPATH)/bin/goja3 -i github.com/dreadl0ck/ja3/cmd"
  },
  {
    "path": "ja3.go",
    "content": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage ja3\n\nimport (\n\t\"bytes\"\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"strconv\"\n\n\t\"github.com/dreadl0ck/tlsx\"\n)\n\nvar (\n\t// Debug indicates whether we run in debug mode.\n\tDebug        = false\n\tsepValueByte = byte(45)\n\tsepFieldByte = byte(44)\n\n\t// Ciphers, extensions and elliptic curves, should be filtered so GREASE values are not added to the ja3 digest\n\t// GREASE RFC: https://tools.ietf.org/html/draft-davidben-tls-grease-01\n\tgreaseValues = map[uint16]bool{\n\t\t0x0a0a: true, 0x1a1a: true,\n\t\t0x2a2a: true, 0x3a3a: true,\n\t\t0x4a4a: true, 0x5a5a: true,\n\t\t0x6a6a: true, 0x7a7a: true,\n\t\t0x8a8a: true, 0x9a9a: true,\n\t\t0xaaaa: true, 0xbaba: true,\n\t\t0xcaca: true, 0xdada: true,\n\t\t0xeaea: true, 0xfafa: true,\n\t}\n)\n\n// BareToDigestHex converts a bare []byte to a hex string.\nfunc BareToDigestHex(bare []byte) string {\n\tsum := md5.Sum(bare)\n\treturn hex.EncodeToString(sum[:])\n}\n\n// Digest returns only the digest md5.\nfunc Digest(hello *tlsx.ClientHelloBasic) [md5.Size]byte {\n\treturn md5.Sum(Bare(hello))\n}\n\n// DigestHex produce md5 hash from bare string.\nfunc DigestHex(hello *tlsx.ClientHelloBasic) string {\n\treturn BareToDigestHex(Bare(hello))\n}\n\n// Bare returns the JA3 bare string for a given tlsx.ClientHelloBasic instance\n// JA3 is a technique developed by Salesforce, to fingerprint TLS Client Hellos.\n// the official python implementation can be found here: https://github.com/salesforce/ja3\n// 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.\n// It then concatenates those values together in order, using a “,” to delimit each field and a “-” to delimit each value in each field.\n// The field order is as follows:\n// SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats\n// Example:\n// 769,47–53–5–10–49161–49162–49171–49172–50–56–19–4,0–10–11,23–24–25,0\n// If there are no SSL Extensions in the Client Hello, the fields are left empty.\n// Example:\n// 769,4–5–10–9–100–98–3–6–19–18–99,,,\n// These strings are then MD5 hashed to produce an easily consumable and shareable 32 character fingerprint.\n// This is the JA3 SSL Client Fingerprint returned by this function.\nfunc Bare(hello *tlsx.ClientHelloBasic) []byte {\n\n\t// TODO: refactor into struct with inbuilt buffer to reduce allocations to ~ zero\n\t// i.e. only realloc if previously allocated buffer is too small for current packet\n\n\tvar (\n\t\tmaxPossibleBufferLength = 5 + 1 + // Version = uint16 => maximum = 65536 = 5chars + 1 field sep\n\t\t\t(5+1)*len(hello.CipherSuites) + // CipherSuite = uint16 => maximum = 65536 = 5chars\n\t\t\t(5+1)*len(hello.AllExtensions) + // uint16 = 2B => maximum = 65536 = 5chars\n\t\t\t(5+1)*len(hello.SupportedGroups) + // uint16 = 2B => maximum = 65536 = 5chars\n\t\t\t(3+1)*len(hello.SupportedPoints) // uint8 = 1B => maximum = 256 = 3chars\n\n\t\tbuffer = make([]byte, 0, maxPossibleBufferLength)\n\t)\n\n\tbuffer = strconv.AppendInt(buffer, int64(hello.HandshakeVersion), 10)\n\tbuffer = append(buffer, sepFieldByte)\n\n\t/*\n\t *\tCipher Suites\n\t */\n\n\t// collect cipher suites\n\tlastElem := len(hello.CipherSuites) - 1\n\tif len(hello.CipherSuites) > 1 {\n\t\tfor _, e := range hello.CipherSuites[:lastElem] {\n\t\t\t// filter GREASE values\n\t\t\tif !greaseValues[uint16(e)] {\n\t\t\t\tbuffer = strconv.AppendInt(buffer, int64(e), 10)\n\t\t\t\tbuffer = append(buffer, sepValueByte)\n\t\t\t}\n\t\t}\n\t}\n\t// append last element if cipher suites are not empty\n\tif lastElem != -1 {\n\t\t// filter GREASE values\n\t\tif !greaseValues[uint16(hello.CipherSuites[lastElem])] {\n\t\t\tbuffer = strconv.AppendInt(buffer, int64(hello.CipherSuites[lastElem]), 10)\n\t\t}\n\t}\n\tbuffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})\n\tbuffer = append(buffer, sepFieldByte)\n\n\t/*\n\t *\tExtensions\n\t */\n\n\t// collect extensions\n\tlastElem = len(hello.AllExtensions) - 1\n\tif len(hello.AllExtensions) > 1 {\n\t\tfor _, e := range hello.AllExtensions[:lastElem] {\n\t\t\t// filter GREASE values\n\t\t\tif !greaseValues[uint16(e)] {\n\t\t\t\tbuffer = strconv.AppendInt(buffer, int64(e), 10)\n\t\t\t\tbuffer = append(buffer, sepValueByte)\n\t\t\t}\n\t\t}\n\t}\n\t// append last element if extensions are not empty\n\tif lastElem != -1 {\n\t\t// filter GREASE values\n\t\tif !greaseValues[uint16(hello.AllExtensions[lastElem])] {\n\t\t\tbuffer = strconv.AppendInt(buffer, int64(hello.AllExtensions[lastElem]), 10)\n\t\t}\n\t}\n\tbuffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})\n\tbuffer = append(buffer, sepFieldByte)\n\n\t/*\n\t *\tSupported Groups\n\t */\n\n\t// collect supported groups\n\tlastElem = len(hello.SupportedGroups) - 1\n\tif len(hello.SupportedGroups) > 1 {\n\t\tfor _, e := range hello.SupportedGroups[:lastElem] {\n\t\t\t// filter GREASE values\n\t\t\tif !greaseValues[uint16(e)] {\n\t\t\t\tbuffer = strconv.AppendInt(buffer, int64(e), 10)\n\t\t\t\tbuffer = append(buffer, sepValueByte)\n\t\t\t}\n\t\t}\n\t}\n\t// append last element if supported groups are not empty\n\tif lastElem != -1 {\n\t\t// filter GREASE values\n\t\tif !greaseValues[uint16(hello.SupportedGroups[lastElem])] {\n\t\t\tbuffer = strconv.AppendInt(buffer, int64(hello.SupportedGroups[lastElem]), 10)\n\t\t}\n\t}\n\tbuffer = bytes.TrimSuffix(buffer, []byte{sepValueByte})\n\tbuffer = append(buffer, sepFieldByte)\n\n\t/*\n\t *\tSupported Points\n\t */\n\n\t// collect supported points\n\tlastElem = len(hello.SupportedPoints) - 1\n\tif len(hello.SupportedPoints) > 1 {\n\t\tfor _, e := range hello.SupportedPoints[:lastElem] {\n\t\t\tbuffer = strconv.AppendInt(buffer, int64(e), 10)\n\t\t\tbuffer = append(buffer, sepValueByte)\n\t\t}\n\t}\n\t// append last element if supported points are not empty\n\tif lastElem != -1 {\n\t\tbuffer = strconv.AppendInt(buffer, int64(hello.SupportedPoints[lastElem]), 10)\n\t}\n\n\treturn buffer\n}\n"
  },
  {
    "path": "ja3_output_testpcap.json",
    "content": "[\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"ja3\": \"768,22-19-10-102-5-4-101-100-99-98-97-96-21-18-9-20-17-8-6-3,,,\",\n        \"ja3_digest\": \"ee0799c323d74129b75b633dcfd41593\",\n        \"source_ip\": \"192.168.0.104\",\n        \"source_port\": 47851,\n        \"timestamp\": 1438974952.485206\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.104\",\n        \"source_port\": 47852,\n        \"timestamp\": 1438974952.499013\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 47436,\n        \"timestamp\": 1438975304.430254\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 47437,\n        \"timestamp\": 1438975310.453851\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 47438,\n        \"timestamp\": 1438975310.80441\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 47439,\n        \"timestamp\": 1438975311.016676\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 47440,\n        \"timestamp\": 1438975312.045158\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 47441,\n        \"timestamp\": 1438975312.182204\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 47442,\n        \"timestamp\": 1438975312.918489\n    },\n    {\n        \"destination_ip\": \"192.168.0.30\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 42577,\n        \"timestamp\": 1438975412.19272\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40052,\n        \"timestamp\": 1438975415.681516\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40053,\n        \"timestamp\": 1438975415.695585\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40054,\n        \"timestamp\": 1438975415.784149\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40055,\n        \"timestamp\": 1438975415.784384\n    },\n    {\n        \"destination_ip\": \"192.168.0.30\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 42938,\n        \"timestamp\": 1438975417.285877\n    },\n    {\n        \"destination_ip\": \"192.168.0.30\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 42939,\n        \"timestamp\": 1438975417.64084\n    },\n    {\n        \"destination_ip\": \"192.168.0.30\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 42940,\n        \"timestamp\": 1438975417.846781\n    },\n    {\n        \"destination_ip\": \"192.168.0.30\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 42941,\n        \"timestamp\": 1438975418.174816\n    },\n    {\n        \"destination_ip\": \"192.168.0.30\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 42942,\n        \"timestamp\": 1438975418.252502\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40062,\n        \"timestamp\": 1438975418.728398\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40063,\n        \"timestamp\": 1438975418.735892\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40066,\n        \"timestamp\": 1438975418.912281\n    },\n    {\n        \"destination_ip\": \"192.168.0.30\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 42949,\n        \"timestamp\": 1438975418.953542\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40069,\n        \"timestamp\": 1438975418.970933\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40071,\n        \"timestamp\": 1438975419.160157\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40072,\n        \"timestamp\": 1438975419.167724\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40074,\n        \"timestamp\": 1438975419.363007\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40075,\n        \"timestamp\": 1438975419.370242\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40077,\n        \"timestamp\": 1438975419.563502\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40078,\n        \"timestamp\": 1438975419.569185\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40080,\n        \"timestamp\": 1438975419.763242\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40081,\n        \"timestamp\": 1438975419.769096\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40083,\n        \"timestamp\": 1438975419.965889\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40084,\n        \"timestamp\": 1438975419.972691\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40086,\n        \"timestamp\": 1438975420.167612\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40087,\n        \"timestamp\": 1438975420.174799\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40089,\n        \"timestamp\": 1438975420.370286\n    },\n    {\n        \"destination_ip\": \"192.168.0.21\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 40090,\n        \"timestamp\": 1438975420.376324\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33325,\n        \"timestamp\": 1438975627.692959\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33329,\n        \"timestamp\": 1438975630.680471\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33330,\n        \"timestamp\": 1438975630.730156\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33331,\n        \"timestamp\": 1438975630.732841\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33333,\n        \"timestamp\": 1438975630.929733\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33334,\n        \"timestamp\": 1438975630.932921\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33336,\n        \"timestamp\": 1438975631.130647\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33337,\n        \"timestamp\": 1438975631.134219\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33339,\n        \"timestamp\": 1438975631.332171\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33340,\n        \"timestamp\": 1438975631.334887\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33342,\n        \"timestamp\": 1438975631.533235\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33343,\n        \"timestamp\": 1438975631.537026\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33345,\n        \"timestamp\": 1438975631.733574\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33346,\n        \"timestamp\": 1438975631.735561\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33348,\n        \"timestamp\": 1438975631.935475\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33349,\n        \"timestamp\": 1438975631.93968\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33351,\n        \"timestamp\": 1438975632.137021\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33352,\n        \"timestamp\": 1438975632.14068\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33354,\n        \"timestamp\": 1438975632.338641\n    },\n    {\n        \"destination_ip\": \"192.168.0.32\",\n        \"destination_port\": 80,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 33355,\n        \"timestamp\": 1438975632.342897\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"ja3\": \"768,22-19-10-102-5-4-101-100-99-98-97-96-21-18-9-20-17-8-6-3,,,\",\n        \"ja3_digest\": \"ee0799c323d74129b75b633dcfd41593\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60089,\n        \"timestamp\": 1438975896.044741\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60090,\n        \"timestamp\": 1438975896.065098\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60091,\n        \"timestamp\": 1438975902.358398\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60096,\n        \"timestamp\": 1438975902.358647\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60093,\n        \"timestamp\": 1438975902.358653\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60097,\n        \"timestamp\": 1438975902.359146\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60098,\n        \"timestamp\": 1438975902.35915\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60099,\n        \"timestamp\": 1438975902.360151\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"ja3\": \"769,49169-57-4,,,\",\n        \"ja3_digest\": \"a471a243929edb6640fff17325ed585f\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60092,\n        \"timestamp\": 1438975902.407827\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"ja3\": \"769,49169-57-4,13172,,\",\n        \"ja3_digest\": \"231f19e6900c1003725de85e988c8136\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60094,\n        \"timestamp\": 1438975902.408066\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60100,\n        \"timestamp\": 1438975903.991505\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60101,\n        \"timestamp\": 1438975903.994341\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60102,\n        \"timestamp\": 1438975904.042925\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60103,\n        \"timestamp\": 1438975904.920607\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60104,\n        \"timestamp\": 1438975905.278113\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60105,\n        \"timestamp\": 1438975905.568019\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60106,\n        \"timestamp\": 1438975905.852386\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60107,\n        \"timestamp\": 1438975906.11517\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60108,\n        \"timestamp\": 1438975906.364325\n    },\n    {\n        \"destination_ip\": \"192.168.0.40\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"e3bb8f1cd407701c585e7a84e96c24e1\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 60109,\n        \"timestamp\": 1438975906.659688\n    },\n    {\n        \"destination_ip\": \"192.168.0.60\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 50499,\n        \"timestamp\": 1438975946.959158\n    },\n    {\n        \"destination_ip\": \"192.168.0.60\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 50539,\n        \"timestamp\": 1438975968.774312\n    },\n    {\n        \"destination_ip\": \"192.168.0.60\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 50540,\n        \"timestamp\": 1438975969.12589\n    },\n    {\n        \"destination_ip\": \"192.168.0.60\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 50541,\n        \"timestamp\": 1438975969.382566\n    },\n    {\n        \"destination_ip\": \"192.168.0.60\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 50542,\n        \"timestamp\": 1438975970.064632\n    },\n    {\n        \"destination_ip\": \"192.168.0.60\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 50543,\n        \"timestamp\": 1438975970.068296\n    },\n    {\n        \"destination_ip\": \"192.168.0.60\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"07e4f12082fc28a84c5412bedc0aa0e2\",\n        \"source_ip\": \"192.168.0.109\",\n        \"source_port\": 50544,\n        \"timestamp\": 1438975970.069778\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7298,\n        \"timestamp\": 1438976023.165545\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7301,\n        \"timestamp\": 1438976028.310661\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7303,\n        \"timestamp\": 1438976028.581532\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7304,\n        \"timestamp\": 1438976028.601543\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7305,\n        \"timestamp\": 1438976028.741557\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7306,\n        \"timestamp\": 1438976028.777827\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7307,\n        \"timestamp\": 1438976028.998445\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7308,\n        \"timestamp\": 1438976028.998754\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7309,\n        \"timestamp\": 1438976028.999243\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7310,\n        \"timestamp\": 1438976029.028726\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7311,\n        \"timestamp\": 1438976029.051919\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7312,\n        \"timestamp\": 1438976029.360982\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7323,\n        \"timestamp\": 1438976050.268904\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7324,\n        \"timestamp\": 1438976050.290868\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7326,\n        \"timestamp\": 1438976051.644443\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7327,\n        \"timestamp\": 1438976051.66594\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7328,\n        \"timestamp\": 1438976051.824233\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7329,\n        \"timestamp\": 1438976051.855177\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7330,\n        \"timestamp\": 1438976052.082936\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7331,\n        \"timestamp\": 1438976052.083183\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7332,\n        \"timestamp\": 1438976052.083439\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7333,\n        \"timestamp\": 1438976052.111491\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7334,\n        \"timestamp\": 1438976052.13963\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7335,\n        \"timestamp\": 1438976052.162501\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7341,\n        \"timestamp\": 1438976061.491056\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7342,\n        \"timestamp\": 1438976061.492075\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7343,\n        \"timestamp\": 1438976061.518477\n    },\n    {\n        \"destination_ip\": \"192.168.0.20\",\n        \"destination_port\": 443,\n        \"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\",\n        \"ja3_digest\": \"624f05d5bd91b08b2d8c395c32358f19\",\n        \"source_ip\": \"192.168.0.102\",\n        \"source_port\": 7344,\n        \"timestamp\": 1438976061.540319\n    }\n]\n"
  },
  {
    "path": "ja3_test.go",
    "content": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage ja3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"testing\"\n\n\t\"github.com/gopacket/gopacket\"\n\t\"github.com/gopacket/gopacket/layers\"\n\t\"github.com/dreadl0ck/tlsx\"\n)\n\nvar tlsPacket = []byte{\n\t0x68, 0x7f, 0x74, 0xd6, 0x95, 0xc1, 0x00, 0x22, 0x15, 0x63, 0xc9, 0x5a, 0x08, 0x00, 0x45, 0x00,\n\t0x00, 0xe5, 0x0f, 0xf0, 0x40, 0x00, 0x80, 0x06, 0xaf, 0x9d, 0xc0, 0xa8, 0x01, 0x0e, 0x17, 0x17,\n\t0x61, 0xb8, 0xc0, 0xef, 0x01, 0xbb, 0x49, 0x71, 0x37, 0x25, 0x98, 0x36, 0xf3, 0x06, 0x50, 0x18,\n\t0x01, 0x00, 0xfe, 0xf4, 0x00, 0x00, 0x16, 0x03, 0x03, 0x00, 0xb8, 0x01, 0x00, 0x00, 0xb4, 0x03,\n\t0x03, 0x59, 0xc1, 0x47, 0x24, 0x67, 0x4d, 0x4f, 0x3b, 0x1f, 0xbd, 0x36, 0x75, 0x9b, 0x2a, 0x92,\n\t0x47, 0x45, 0xf6, 0x7b, 0x03, 0x19, 0x09, 0x13, 0x76, 0x7e, 0xfc, 0x7f, 0xc4, 0x22, 0x6b, 0xe8,\n\t0x1a, 0x20, 0xfb, 0xa8, 0x85, 0x7c, 0x99, 0xca, 0x36, 0x12, 0xe7, 0x72, 0xb0, 0x87, 0xef, 0xdd,\n\t0x08, 0x02, 0x7f, 0xc7, 0x55, 0x72, 0xdc, 0x5a, 0xeb, 0x7b, 0x1f, 0x04, 0x61, 0xf4, 0xa6, 0x82,\n\t0x18, 0x51, 0x00, 0x2a, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x05, 0x00, 0x0a,\n\t0xc0, 0x27, 0xc0, 0x13, 0xc0, 0x14, 0xc0, 0x2b, 0xc0, 0x23, 0xc0, 0x2c, 0xc0, 0x24, 0xc0, 0x09,\n\t0xc0, 0x0a, 0x00, 0x40, 0x00, 0x32, 0x00, 0x6a, 0x00, 0x38, 0x00, 0x13, 0x00, 0x04, 0x01, 0x00,\n\t0x00, 0x41, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x00, 0x0f,\n\t0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2e, 0x6b, 0x72, 0x78, 0x64, 0x2e, 0x6e, 0x65, 0x74, 0x00,\n\t0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,\n\t0x0d, 0x00, 0x10, 0x00, 0x0e, 0x04, 0x01, 0x05, 0x01, 0x02, 0x01, 0x04, 0x03, 0x05, 0x03, 0x02,\n\t0x03, 0x02, 0x02,\n}\n\nfunc getHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ClientHelloBasic) {\n\tif tl := p.TransportLayer(); tl != nil {\n\t\tif tcp, ok := tl.(*layers.TCP); ok {\n\t\t\tif tcp.SYN {\n\t\t\t\tb.Fatal(\"Connection setup\")\n\t\t\t} else if tcp.FIN {\n\t\t\t\tb.Fatal(\"Connection teardown\")\n\t\t\t} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {\n\t\t\t\tb.Fatal(\"Acknowledgement packet\")\n\t\t\t} else if tcp.RST {\n\t\t\t\tb.Fatal(\"Unexpected packet\")\n\t\t\t} else {\n\t\t\t\thello = &tlsx.ClientHelloBasic{}\n\t\t\t\terr := hello.Unmarshal(tcp.LayerPayload())\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(\"invalid packet\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n/*\n *\tTests\n */\n\nfunc TestDigestHexCorrect(t *testing.T) {\n\n\tp := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\tif p.ErrorLayer() != nil {\n\t\tt.Fatal(p.ErrorLayer().Error())\n\t}\n\n\thash := DigestHexPacket(p)\n\tif hash != \"4d7a28d6f2263ed61de88ca66eb011e3\" {\n\t\tt.Fatal(hash, \"!=\", \"4d7a28d6f2263ed61de88ca66eb011e3\")\n\t}\n}\n\nfunc TestDigestHexComparePcap(t *testing.T) {\n\n\t// Step 1: Read JSON output of reference implementation\n\tdata, err := ioutil.ReadFile(\"ja3_output_testpcap.json\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar referenceRecords = make([]*Record, 0)\n\terr = json.Unmarshal(data, &referenceRecords)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Step 2: generate JSON output wth goja3 and read into buffer\n\tvar (\n\t\tb       bytes.Buffer\n\t\trecords = make([]*Record, 0)\n\t)\n\n\t// read test.pcap and generate fingerprints\n\tReadFileJSON(\"test.pcap\", &b, false)\n\n\terr = json.Unmarshal(b.Bytes(), &records)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Step 3: compare the number of fingerprints and then the digest for each record\n\tif len(records) != len(referenceRecords) {\n\t\tt.Fatal(\"len(records) != len(referenceRecords): \", len(records), \" != \", len(referenceRecords))\n\t}\n\n\t// range reference records\n\tfor i, r := range referenceRecords {\n\n\t\t// compare reference digest to the one produced by goja3\n\t\tif r.JA3Digest != records[i].JA3Digest {\n\t\t\tt.Fatal(\"r.JA3Digest != records[i].JA3Digest: \", r.JA3Digest, \" != \", records[i].JA3Digest)\n\t\t}\n\t\t// compare reference digest to the one produced by goja3\n\t\tif r.JA3SDigest != records[i].JA3SDigest {\n\t\t\tt.Fatal(\"r.JA3SDigest != records[i].JAS3Digest: \", r.JA3SDigest, \" != \", records[i].JA3SDigest)\n\t\t}\n\t}\n}\n\n/*\n *\tBenchmarks\n */\n\nfunc BenchmarkDigestHexPacket(b *testing.B) {\n\n\tp := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tDigestHexPacket(p)\n\t}\n}\n\nfunc BenchmarkDigestPacket(b *testing.B) {\n\tp := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)\n\tif p.ErrorLayer() != nil {\n\t\tb.Fatal(p.ErrorLayer().Error())\n\t}\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tDigestPacket(p)\n\t}\n}\n\nfunc BenchmarkBarePacket(b *testing.B) {\n\tp := gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Default)\n\tif p.ErrorLayer() != nil {\n\t\tb.Fatal(p.ErrorLayer().Error())\n\t}\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tBarePacket(p)\n\t}\n}\n\nfunc BenchmarkDigestHex(b *testing.B) {\n\tvar (\n\t\tp     = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\t\thello = getHello(p, b)\n\t)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tDigestHex(hello)\n\t}\n}\n\nfunc BenchmarkDigest(b *testing.B) {\n\n\tvar (\n\t\tp     = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\t\thello = getHello(p, b)\n\t)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tDigest(hello)\n\t}\n}\n\nfunc BenchmarkBare(b *testing.B) {\n\n\tvar (\n\t\tp     = gopacket.NewPacket(tlsPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\t\thello = getHello(p, b)\n\t)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tBare(hello)\n\t}\n}\n"
  },
  {
    "path": "ja3s.go",
    "content": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage ja3\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"github.com/dreadl0ck/tlsx\"\n\t\"strconv\"\n)\n\n// BareToDigestHex converts a bare []byte to a hex string.\nfunc BareToDigestHexJa3s(bare []byte) string {\n\tsum := md5.Sum(bare)\n\treturn hex.EncodeToString(sum[:])\n}\n\n// Digest returns only the digest md5.\nfunc DigestJa3s(hello *tlsx.ServerHelloBasic) [md5.Size]byte {\n\treturn md5.Sum(BareJa3s(hello))\n}\n\n// DigestHex produce md5 hash from bare string.\nfunc DigestHexJa3s(hello *tlsx.ServerHelloBasic) string {\n\treturn BareToDigestHex(BareJa3s(hello))\n}\n\n// BareJa3s returns the JA3S bare string for a given tlsx.ServerHelloBasic instance\n// JA3S is JA3 for the Server side of the SSL/TLS communication and fingerprints how servers respond to particular clients.\n// JA3S uses the following field order:\n// SSLVersion,Cipher,SSLExtension\nfunc BareJa3s(hello *tlsx.ServerHelloBasic) []byte {\n\n\t// TODO: refactor into struct with inbuilt buffer to reduce allocations to ~ zero\n\t// i.e. only realloc if previously allocated buffer is too small for current packet\n\n\tvar (\n\t\tmaxPossibleBufferLength = 5 + 1 + // Version = uint16 => maximum = 65536 = 5chars + 1 field sep\n\t\t\t(5+1)*1 + // CipherSuite = uint16 => maximum = 65536 = 5chars\n\t\t\t(5+1)*len(hello.Extensions) // uint16 = 2B => maximum = 65536 = 5chars\n\n\t\tbuffer = make([]byte, 0, maxPossibleBufferLength)\n\t)\n\n\tbuffer = strconv.AppendInt(buffer, int64(hello.Vers), 10)\n\tbuffer = append(buffer, sepFieldByte)\n\n\t/*\n\t *\tCipher Suite\n\t */\n\n\tbuffer = strconv.AppendInt(buffer, int64(hello.CipherSuite), 10)\n\tbuffer = append(buffer, sepFieldByte)\n\n\tif len(hello.Extensions) > 0 {\n\n\t\t/*\n\t\t *\tExtensions\n\t\t */\n\n\t\t// collect extensions\n\t\tlastElem := len(hello.Extensions) - 1\n\t\tif len(hello.Extensions) > 1 {\n\t\t\tfor _, e := range hello.Extensions[:lastElem] {\n\t\t\t\t// filter GREASE values\n\t\t\t\tif !greaseValues[uint16(e)] {\n\t\t\t\t\tbuffer = strconv.AppendInt(buffer, int64(e), 10)\n\t\t\t\t\tbuffer = append(buffer, sepValueByte)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// append last element if extensions are not empty\n\t\tif lastElem != -1 {\n\t\t\t// filter GREASE values\n\t\t\tif !greaseValues[uint16(hello.Extensions[lastElem])] {\n\t\t\t\tbuffer = strconv.AppendInt(buffer, int64(hello.Extensions[lastElem]), 10)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn buffer\n}\n"
  },
  {
    "path": "ja3s_pcap.go",
    "content": "//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// ReadFileJa3s reads the PCAP file at the given path\n// and prints out all packets containing JA3S digests to the supplied io.Writer\nfunc ReadFileJa3s(file string, out io.Writer) {\n\n\tr, f, link, err := openPcap(file)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer f.Close()\n\n\tcount := 0\n\tfor {\n\t\t// read packet data\n\t\tdata, _, err := r.ReadPacketData()\n\t\tif err == io.EOF {\n\t\t\tif Debug {\n\t\t\t\tfmt.Println(count, \"fingerprints.\")\n\t\t\t}\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tvar (\n\t\t\t// create gopacket\n\t\t\tp = gopacket.NewPacket(data, link, gopacket.Lazy)\n\t\t\t// get JA3 if possible\n\t\t\tdigest = DigestHexPacketJa3s(p)\n\t\t)\n\n\t\t// check if we got a result\n\t\tif digest != \"\" {\n\n\t\t\tcount++\n\n\t\t\tvar (\n\t\t\t\tb  strings.Builder\n\t\t\t\tnl = p.NetworkLayer()\n\t\t\t\ttl = p.TransportLayer()\n\t\t\t)\n\n\t\t\t// got an a digest but no transport or network layer\n\t\t\tif tl == nil || nl == nil {\n\t\t\t\tif Debug {\n\t\t\t\t\tfmt.Println(\"got a nil layer: \", nl, tl, p.Dump(), digest)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tb.WriteString(\"[\")\n\t\t\tb.WriteString(nl.NetworkFlow().Dst().String())\n\t\t\tb.WriteString(\":\")\n\t\t\tb.WriteString(tl.TransportFlow().Dst().String())\n\t\t\tb.WriteString(\"] JA3S: \")\n\t\t\tb.WriteString(string(BarePacketJa3s(p)))\n\t\t\tb.WriteString(\" --> \")\n\t\t\tb.WriteString(digest)\n\t\t\tb.WriteString(\"\\n\")\n\n\t\t\t_, err := out.Write([]byte(b.String()))\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "ja3s_test.go",
    "content": "/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage ja3_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"github.com/dreadl0ck/ja3\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"testing\"\n\n\t\"github.com/dreadl0ck/tlsx\"\n\t\"github.com/gopacket/gopacket\"\n\t\"github.com/gopacket/gopacket/layers\"\n)\n\nvar tlsServerHelloPacket, errDecodeServerHelloPacket = hex.DecodeString(\"f018982a38be48d343aac4b80800450005b4447b00007906ea20225f7893c0a8b20d01bbefca6011d3eb8013192d801000f087ae00000101080a79facb3a56d9798616030300640200006003035e5bea4420be035069306e15e36eca187d56a201e8e96f0a1afeeb529413fba020746572b9b8832a5b8f236ba51743281c20e923a880067e4c38a754f53f3a8743c02f00001800170000ff01000100000b0002010000100005000302683216030309980b0009940009910004fc308204f8308203e0a00302010202100a86b904765831e240cc6211101f5736300d06092a864886f70d01010b0500305e310b300906035504061302555331153013060355040a130c446967694365727420496e6331193017060355040b13107777772e64696769636572742e636f6d311d301b0603550403131447656f5472757374205253412043412032303138301e170d3138303130343030303030305a170d3230303730393132303030305a3068310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f060355040713085061736164656e61311b3019060355040a13124f70656e5820546563686e6f6c6f676965733114301206035504030c0b2a2e6f70656e782e6e657430820122300d06092a864886f70d01010105000382010f003082010a0282010100bf0efabfe55538e687d4db8f8d615ab1d5dce14702307ca21c0547cd2cc43e455627b4d8decd8015296c19127d29025fbac71f2fc1af1bfe525ed0c2778028a3e7dd643b60842801d9d196651924057827c4d3ce6ae62253b869f384d56a72060055e3537a67a9904e61a4628067f07c680a8bf30c14444d090910c182ea3c5e473b1ab1233838645b0bdfb24265ee32e3521b4bdbae6d79acdbdd7a00c0f00479537079fd352aade5e8394be2f2b397366cfd64bb94b1b96515b4e78d3a2fac04e9d5787792fcf411b6b66cb5dfbc4cb023d93fa99cee630d8ed3558b33e0f2a019ecc5eac16919c48a844f693b5e7ccb4eb84724ab963979c570eb7a3b58ad0203010001a38201a6308201a2301f0603551d230418301680149058ffb09c75a8515477b1edf2a34316389e6cc5301d0603551d0e04160414e03952aaaeb23c67e939ec6022188686bcb314c430210603551d11041a3018820b2a2e6f70656e782e6e657482096f70656e782e6e6574300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302303e0603551d1f043730353033a031a02f862d687474703a2f2f6364702e67656f74727573742e636f6d2f47656f54727573745253414341323031382e63726c304c0603551d2004453043303706096086480186fd6c0101302a302806082b06010505070201161c68747470733a2f2f7777772e64696769636572742e636f6d2f4350533008060667810c010202307506082b0601050507010104693067302606082b06010505073001861a687474703a2f2f7374617475732e67656f74727573742e636f6d303d06082b060105050730028631687474703a2f2f636163657274732e67656f74727573742e636f6d2f47656f54727573745253414341323031382e63727430090603551d1304023000300d06092a864886f70d01010b0500038201010045a0d33fa95d251c861518096f2ffe89bae2e062cfc4505859b42adeaa775fcf34c2eebde155cfb6449a806abffbaad2e88d77b1397e76ea08265dbc2aa98cb04e3d141c27ed629961d9cd90bb7f6db672408bb15126dd4df4cdf831465c1ee9f321505ace6e7c09d864683fbbaa8ee9fc73e74feefaa5a027067856000f56c9ae39bcb3cc586928429bd8abe5d2a3d8df5c63b4db45fa2cc3ffa6f75b946188019e162fb8dfacf2be3064bf52788378e61c8397ec13e72649fbe15e5c5b6d8bae43ca7b188eda5a41e512f508206fefe96a832ce4676a425ddd06e3cfe93046140818a7359723f2477315f38ab51f83c9dced627efaf6541ed3e9c0767376b200048f3082048b30820373a0\")\n\nfunc getServerHello(p gopacket.Packet, b *testing.B) (hello *tlsx.ServerHelloBasic) {\n\n\tif errDecodeServerHelloPacket != nil {\n\t\tlog.Fatal(\"failed to decode test packet\", errDecodeServerHelloPacket)\n\t}\n\n\tif tl := p.TransportLayer(); tl != nil {\n\t\tif tcp, ok := tl.(*layers.TCP); ok {\n\t\t\tif tcp.SYN {\n\t\t\t\tb.Fatal(\"Connection setup\")\n\t\t\t} else if tcp.FIN {\n\t\t\t\tb.Fatal(\"Connection teardown\")\n\t\t\t} else if tcp.ACK && len(tcp.LayerPayload()) == 0 {\n\t\t\t\tb.Fatal(\"Acknowledgement packet\")\n\t\t\t} else if tcp.RST {\n\t\t\t\tb.Fatal(\"Unexpected packet\")\n\t\t\t} else {\n\t\t\t\thello = &tlsx.ServerHelloBasic{}\n\t\t\t\terr := hello.Unmarshal(tcp.LayerPayload())\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(\"invalid packet\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n\n/*\n *\tTests\n */\n\nfunc TestDigestHexJa3sCorrect(t *testing.T) {\n\n\tp := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\tif p.ErrorLayer() != nil {\n\t\tt.Fatal(p.ErrorLayer().Error())\n\t}\n\n\thash := ja3.DigestHexPacketJa3s(p)\n\tif hash != \"5b94af9bf6efc9dea416841602004fbb\" {\n\t\tt.Fatal(hash, \"!=\", \"5b94af9bf6efc9dea416841602004fbb\")\n\t}\n}\n\nfunc TestPcapWithNonEthernetEncapsulation(t *testing.T) {\n\tvar (\n\t\tb bytes.Buffer\n\t)\n\n\t// read test2.pcap and generate fingerprints\n\tja3.ReadFileJSON(\"test2.pcap\", &b, true)\n\n\tif b.String() == \"\" {\n\t\tt.Fatal(\"expected output\")\n\t}\n}\n\nfunc TestDigestHexJa3sComparePcap(t *testing.T) {\n\n\t// Step 1: Read output of reference implementation\n\tdata, err := ioutil.ReadFile(\"ja3s_test.log\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Step 2: generate output wth goja3 and read into buffer\n\tvar (\n\t\tb bytes.Buffer\n\t)\n\n\t// read test.pcap and generate fingerprints\n\tja3.ReadFileJa3s(\"test.pcap\", &b)\n\n\t// Step 3: compare the number of fingerprints and then the digest for each record\n\tif len(data) != len(b.Bytes()) {\n\t\tt.Fatal(\"len(records) != len(referenceRecords): \", len(data), \" != \", len(b.Bytes()))\n\t}\n\n\treference := bytes.Split(data, []byte{'\\n'})\n\n\t// range reference records\n\tfor i, r := range bytes.Split(b.Bytes(), []byte{'\\n'}) {\n\n\t\t// compare reference digest to the one produced by goja3\n\t\tif string(r) != string(reference[i]) {\n\t\t\tt.Fatal(\"message does not match reference implementation: \", string(r), \" != \", string(reference[i]))\n\t\t}\n\t}\n}\n\n/*\n *\tBenchmarks\n */\n\nfunc BenchmarkDigestHexPacketJa3s(b *testing.B) {\n\n\tp := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tja3.DigestHexPacketJa3s(p)\n\t}\n}\n\nfunc BenchmarkDigestPacketJa3s(b *testing.B) {\n\tp := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)\n\tif p.ErrorLayer() != nil {\n\t\tb.Fatal(p.ErrorLayer().Error())\n\t}\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tja3.DigestPacketJa3s(p)\n\t}\n}\n\nfunc BenchmarkBarePacketJa3s(b *testing.B) {\n\tp := gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Default)\n\tif p.ErrorLayer() != nil {\n\t\tb.Fatal(p.ErrorLayer().Error())\n\t}\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tja3.BarePacketJa3s(p)\n\t}\n}\n\nfunc BenchmarkDigestHexJa3s(b *testing.B) {\n\tvar (\n\t\tp     = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\t\thello = getServerHello(p, b)\n\t)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tja3.DigestHexJa3s(hello)\n\t}\n}\n\nfunc BenchmarkDigestJa3s(b *testing.B) {\n\n\tvar (\n\t\tp     = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\t\thello = getServerHello(p, b)\n\t)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tja3.DigestJa3s(hello)\n\t}\n}\n\nfunc BenchmarkBareJa3s(b *testing.B) {\n\n\tvar (\n\t\tp     = gopacket.NewPacket(tlsServerHelloPacket, layers.LinkTypeEthernet, gopacket.Lazy)\n\t\thello = getServerHello(p, b)\n\t)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\tfor n := 0; n < b.N; n++ {\n\t\tja3.BareJa3s(hello)\n\t}\n}\n"
  },
  {
    "path": "ja3s_test.log",
    "content": "[192.168.0.104:47851] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d\n[192.168.0.104:47852] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:47436] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:47437] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:47438] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:47439] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:47440] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:47441] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:47442] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:42577] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:42938] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:42939] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:42940] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:42941] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:42942] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:42949] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:60089] JA3S: 768,10, --> beee243d749b8f43c283eeaf7517600d\n[192.168.0.109:60090] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60093] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60096] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60097] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60098] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60099] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60091] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60094] JA3S: 769,4, --> 53611273a714cb4789c8222932efd5a7\n[192.168.0.109:60092] JA3S: 769,4, --> 53611273a714cb4789c8222932efd5a7\n[192.168.0.109:60100] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60101] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60102] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60103] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60104] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60105] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60106] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60107] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60108] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:60109] JA3S: 769,5, --> 9aeeb84942a46257594025306635f0ff\n[192.168.0.109:50499] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:50539] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:50540] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:50541] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:50542] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:50543] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.109:50544] JA3S: 769,47, --> 18e962e106761869a61045bed0e81c2c\n[192.168.0.102:7298] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7301] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7303] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7304] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7305] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7306] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7307] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7308] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7309] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7310] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7311] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7312] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7323] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7324] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7326] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7327] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7328] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7329] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7330] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7331] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7332] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7333] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7334] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7335] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7341] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7342] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7343] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n[192.168.0.102:7344] JA3S: 769,53, --> ab1e32eeaf70ee94c0af00f08d126891\n"
  },
  {
    "path": "json.go",
    "content": "//go:build !ja3_disable_gopacket\n\n/*\n * JA3 - TLS Client Hello Hash\n * Copyright (c) 2017, Salesforce.com, Inc.\n * this code was created by Philipp Mieden <dreadl0ck [at] protonmail [dot] ch>\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\npackage ja3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/gopacket/gopacket\"\n)\n\n// Record contains all information for a calculated JA3\ntype Record struct {\n\tDestinationIP   string  `json:\"destination_ip\"`\n\tDestinationPort int     `json:\"destination_port\"`\n\tJA3             string  `json:\"ja3\"`\n\tJA3Digest       string  `json:\"ja3_digest\"`\n\tJA3S            string  `json:\"ja3s\"`\n\tJA3SDigest      string  `json:\"ja3s_digest\"`\n\tSourceIP        string  `json:\"source_ip\"`\n\tSourcePort      int     `json:\"source_port\"`\n\tTimestamp       float64 `json:\"timestamp\"`\n}\n\n// ReadFileJSON reads the PCAP file at the given path\n// and prints out all packets containing JA3 digests formatted as JSON to the supplied io.Writer\nfunc ReadFileJSON(file string, out io.Writer, doJA3s bool) {\n\n\tr, f, link, err := openPcap(file)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer f.Close()\n\n\tvar records []*Record\n\n\tfor {\n\t\t// read packet data\n\t\tdata, ci, err := r.ReadPacketData()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tvar (\n\t\t\t// create gopacket\n\t\t\tp = gopacket.NewPacket(data, link, gopacket.Lazy)\n\n\t\t\t// get JA3 if possible\n\t\t\tbare     = BarePacket(p)\n\t\t\tisServer bool\n\t\t)\n\n\t\tif doJA3s && len(bare) == 0 {\n\t\t\tbare = BarePacketJa3s(p)\n\t\t\tisServer = true\n\t\t}\n\n\t\t// check if we got a result\n\t\tif len(bare) > 0 {\n\n\t\t\tvar (\n\t\t\t\tnl = p.NetworkLayer()\n\t\t\t\ttl = p.TransportLayer()\n\t\t\t)\n\n\t\t\t// stop if either network or transport layer is nil\n\t\t\tif tl == nil || nl == nil {\n\t\t\t\tfmt.Println(\"error: \", nl, tl, p.Dump())\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tr := &Record{\n\t\t\t\tDestinationIP:   nl.NetworkFlow().Dst().String(),\n\t\t\t\tDestinationPort: int(binary.BigEndian.Uint16(tl.TransportFlow().Dst().Raw())),\n\t\t\t\tSourceIP:        nl.NetworkFlow().Src().String(),\n\t\t\t\tSourcePort:      int(binary.BigEndian.Uint16(tl.TransportFlow().Src().Raw())),\n\t\t\t\tTimestamp:       timeToFloat(ci.Timestamp),\n\t\t\t}\n\n\t\t\tif isServer {\n\t\t\t\tr.JA3S = string(bare)\n\t\t\t\tr.JA3SDigest = BareToDigestHex(bare)\n\t\t\t} else {\n\t\t\t\tr.JA3 = string(bare)\n\t\t\t\tr.JA3Digest = BareToDigestHex(bare)\n\t\t\t}\n\n\t\t\t// append record and populate all fields\n\t\t\trecords = append(records, r)\n\t\t}\n\t}\n\n\t// make it pretty please\n\tb, err := json.MarshalIndent(records, \"\", \"    \")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tif string(b) != \"null\" { // no matches will result in \"null\" json\n\t\t// write to output io.Writer\n\t\t_, err = out.Write(b)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tif Debug {\n\t\tfmt.Println(len(records), \"fingerprints.\")\n\t}\n}\n\n// convert a time.Time to a string timestamp in the format seconds.microseconds\nfunc timeToString(t time.Time) string {\n\tmicro := fmt.Sprintf(\"%06d\", t.Nanosecond()/1000)\n\treturn strconv.FormatInt(t.Unix(), 10) + \".\" + micro\n}\n\nfunc timeStringToFloat64(t string) float64 {\n\tf, err := strconv.ParseFloat(t, 64)\n\tif err != nil {\n\t\tfmt.Println(\"[ERROR] failed to convert\", t, \"to float64. error:\", err)\n\t}\n\treturn f\n}\n\nfunc timeToFloat(t time.Time) float64 {\n\treturn timeStringToFloat64(timeToString(t))\n}\n"
  },
  {
    "path": "live.go",
    "content": "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/gopacket\"\n\t\"github.com/gopacket/gopacket/layers\"\n\t\"github.com/gopacket/gopacket/pcap\"\n)\n\n// ReadInterface reads packets from the named interface\n// if asJSON is true the results will be dumped as newline separated JSON objects\n// otherwise CSV will be printed to the supplied io.Writer.\nfunc ReadInterface(iface string, out io.Writer, separator string, ja3s bool, asJSON bool, snaplen int, promisc bool, timeout time.Duration) {\n\n\th, err := pcap.OpenLive(iface, int32(snaplen), promisc, timeout)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer h.Close()\n\n\tif !asJSON {\n\t\tcolumns := []string{\"timestamp\", \"source_ip\", \"source_port\", \"destination_ip\", \"destination_port\", \"ja3_digest\"}\n\n\t\t_, err = out.Write([]byte(strings.Join(columns, separator) + \"\\n\"))\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tcount := 0\n\tfor {\n\t\t// read packet data\n\t\tdata, ci, err := h.ReadPacketData()\n\t\tif err == io.EOF {\n\t\t\tif Debug {\n\t\t\t\tfmt.Println(count, \"fingerprints.\")\n\t\t\t}\n\t\t\treturn\n\t\t} else if err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tvar (\n\t\t\t// create gopacket\n\t\t\tp        = gopacket.NewPacket(data, layers.LinkTypeEthernet, gopacket.Lazy)\n\t\t\tbare     = BarePacket(p)\n\t\t\tisServer bool\n\t\t)\n\n\t\tif ja3s && len(bare) == 0 {\n\t\t\tbare = BarePacketJa3s(p)\n\t\t\tisServer = true\n\t\t}\n\n\t\t// check if we got a result\n\t\tif len(bare) > 0 {\n\n\t\t\tcount++\n\n\t\t\tvar (\n\t\t\t\tb  strings.Builder\n\t\t\t\tnl = p.NetworkLayer()\n\t\t\t\ttl = p.TransportLayer()\n\t\t\t)\n\n\t\t\t// got a bare but no transport or network layer\n\t\t\tif tl == nil || nl == nil {\n\t\t\t\tif Debug {\n\t\t\t\t\tfmt.Println(\"got a nil layer: \", nl, tl, p.Dump(), string(bare))\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tr := &Record{\n\t\t\t\tDestinationIP:   nl.NetworkFlow().Dst().String(),\n\t\t\t\tDestinationPort: int(binary.BigEndian.Uint16(tl.TransportFlow().Dst().Raw())),\n\t\t\t\tSourceIP:        nl.NetworkFlow().Src().String(),\n\t\t\t\tSourcePort:      int(binary.BigEndian.Uint16(tl.TransportFlow().Src().Raw())),\n\t\t\t\tTimestamp:       timeToFloat(ci.Timestamp),\n\t\t\t}\n\n\t\t\tdigest := BareToDigestHex(bare)\n\t\t\tif isServer {\n\t\t\t\tr.JA3S = string(bare)\n\t\t\t\tr.JA3SDigest = digest\n\t\t\t} else {\n\t\t\t\tr.JA3 = string(bare)\n\t\t\t\tr.JA3Digest = digest\n\t\t\t}\n\n\t\t\tif asJSON {\n\n\t\t\t\t// make it pretty please\n\t\t\t\tb, err := json.MarshalIndent(r, \"\", \"    \")\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\n\t\t\t\tif string(b) != \"null\" { // no matches will result in \"null\" json\n\t\t\t\t\t// write to output io.Writer\n\t\t\t\t\t_, err = out.Write(b)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t}\n\n\t\t\t\t\t_, err = out.Write([]byte(\"\\n\"))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else { // CSV\n\t\t\t\tb.WriteString(timeToString(ci.Timestamp))\n\t\t\t\tb.WriteString(separator)\n\t\t\t\tb.WriteString(nl.NetworkFlow().Src().String())\n\t\t\t\tb.WriteString(separator)\n\t\t\t\tb.WriteString(tl.TransportFlow().Src().String())\n\t\t\t\tb.WriteString(separator)\n\t\t\t\tb.WriteString(nl.NetworkFlow().Dst().String())\n\t\t\t\tb.WriteString(separator)\n\t\t\t\tb.WriteString(tl.TransportFlow().Dst().String())\n\t\t\t\tb.WriteString(separator)\n\t\t\t\tb.WriteString(digest)\n\t\t\t\tb.WriteString(\"\\n\")\n\n\t\t\t\t_, err := out.Write([]byte(b.String()))\n\t\t\t\tif err != nil {\n\t\t\t\t\tpanic(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "utils.go",
    "content": "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/gopacket/gopacket/pcapgo\"\n)\n\n// PacketSource means we can read Packets.\ntype PacketSource interface {\n\tReadPacketData() ([]byte, gopacket.CaptureInfo, error)\n}\n\nfunc openPcap(file string) (PacketSource, *os.File, layers.LinkType, error) {\n\n\t// get file handle\n\tf, err := os.Open(file)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar (\n\t\treader       PacketSource\n\t\tlinkType     layers.LinkType\n\t)\n\n\t// try to create pcap reader\n\tpcapReader, errPcap := pcapgo.NewReader(f)\n\tif errPcap != nil {\n\n\t\t// maybe its a PCAPNG\n\t\t// reopen file, otherwise offsets will be wrong\n\t\terr = f.Close()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tf, err = os.Open(file)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\t// try to create pcapng reader\n\t\tngReader, errPcapNg := pcapgo.NewNgReader(f, pcapgo.DefaultNgReaderOptions)\n\t\tif errPcapNg != nil {\n\t\t\t// nope\n\t\t\tfmt.Println(\"pcap error:\", errPcap)\n\t\t\tfmt.Println(\"pcap-ng error:\", errPcapNg)\n\t\t\tpanic(\"cannot open PCAP file\")\n\t\t}\n\n\t\tlinkType = ngReader.LinkType()\n\t\treader = ngReader\n\t} else {\n\t\tlinkType = pcapReader.LinkType()\n\t\treader = pcapReader\n\t}\n\n\treturn reader, f, linkType, err\n}\n"
  }
]