refacto
							parent
							
								
									d6d9f8a111
								
							
						
					
					
						commit
						549437b355
					
				
							
								
								
									
										207
									
								
								d4-tlsf.go
								
								
								
								
							
							
						
						
									
										207
									
								
								d4-tlsf.go
								
								
								
								
							|  | @ -2,13 +2,10 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/md5" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/x509" | ||||
| 
 | ||||
| 	// TODO consider
 | ||||
| 	//"github.com/google/certificate-transparency-go/x509"
 | ||||
| 	"encoding/hex" | ||||
| 
 | ||||
| 	"encoding/json" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
|  | @ -17,12 +14,10 @@ import ( | |||
| 	"log" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/glaslos/tlsh" | ||||
| 	"github.com/google/gopacket" | ||||
| 	"github.com/google/gopacket/examples/util" | ||||
| 	"github.com/google/gopacket/ip4defrag" | ||||
|  | @ -30,6 +25,7 @@ import ( | |||
| 	"github.com/google/gopacket/pcap" | ||||
| 	"github.com/google/gopacket/reassembly" | ||||
| 
 | ||||
| 	"github.com/D4-project/sensor-d4-tls-fingerprinting/d4tls" | ||||
| 	"github.com/D4-project/sensor-d4-tls-fingerprinting/etls" | ||||
| ) | ||||
| 
 | ||||
|  | @ -52,38 +48,7 @@ var fname = flag.String("r", "", "Filename to read from, overrides -i") | |||
| // writing
 | ||||
| var outCerts = flag.String("w", "", "Folder to write certificates into") | ||||
| var outJSON = flag.String("j", "", "Folder to write certificates into, stdin if not set") | ||||
| var jobQ chan TLSSession | ||||
| 
 | ||||
| type CertMapElm struct { | ||||
| 	CertHash string | ||||
| 	*x509.Certificate | ||||
| } | ||||
| 
 | ||||
| type SessionRecord struct { | ||||
| 	ServerIP     string | ||||
| 	ServerPort   string | ||||
| 	ClientIP     string | ||||
| 	ClientPort   string | ||||
| 	TLSH         string | ||||
| 	Timestamp    time.Time | ||||
| 	JA3          string | ||||
| 	JA3Digest    string | ||||
| 	JA3S         string | ||||
| 	JA3SDigest   string | ||||
| 	Certificates []CertMapElm | ||||
| } | ||||
| 
 | ||||
| type TLSSession struct { | ||||
| 	record   SessionRecord | ||||
| 	tlsHdskR etls.ETLSHandshakeRecord | ||||
| } | ||||
| 
 | ||||
| var grease = map[uint16]bool{ | ||||
| 	0x0a0a: true, 0x1a1a: true, 0x2a2a: true, 0x3a3a: true, | ||||
| 	0x4a4a: true, 0x5a5a: true, 0x6a6a: true, 0x7a7a: true, | ||||
| 	0x8a8a: true, 0x9a9a: true, 0xaaaa: true, 0xbaba: true, | ||||
| 	0xcaca: true, 0xdada: true, 0xeaea: true, 0xfafa: true, | ||||
| } | ||||
| var jobQ chan d4tls.TLSSession | ||||
| 
 | ||||
| const closeTimeout time.Duration = time.Hour * 24 // Closing inactive: TODO: from CLI
 | ||||
| const timeout time.Duration = time.Minute * 5     // Pending bytes: TODO: from CLI
 | ||||
|  | @ -93,27 +58,6 @@ var errorsMap map[string]uint | |||
| var errorsMapMutex sync.Mutex | ||||
| var errors uint | ||||
| 
 | ||||
| func (t *TLSSession) String() string { | ||||
| 	var buf bytes.Buffer | ||||
| 	buf.WriteString(fmt.Sprintf("---------------SESSION START-------------------\n")) | ||||
| 	buf.WriteString(fmt.Sprintf("Time: %d\n", t.record.Timestamp)) | ||||
| 	buf.WriteString(fmt.Sprintf("Client: %v:%v\n", t.record.ClientIP, t.record.ClientPort)) | ||||
| 	buf.WriteString(fmt.Sprintf("Server: %v:%v\n", t.record.ServerIP, t.record.ServerPort)) | ||||
| 	buf.WriteString(fmt.Sprintf("TLSH: %q\n", t.record.TLSH)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3: %q\n", t.record.JA3)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3 Digest: %q\n", t.record.JA3Digest)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3s: %q\n", t.record.JA3S)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3s Digest: %q\n", t.record.JA3SDigest)) | ||||
| 	for _, certMe := range t.record.Certificates { | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate Issuer: %q\n", certMe.Certificate.Issuer)) | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate Subject: %q\n", certMe.Certificate.Subject)) | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate is CA: %t\n", certMe.Certificate.IsCA)) | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate SHA256: %t\n", certMe.CertHash)) | ||||
| 	} | ||||
| 	buf.WriteString(fmt.Sprintf("---------------SESSION  END--------------------\n")) | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // Too bad for perf that a... is evaluated
 | ||||
| func Error(t string, s string, a ...interface{}) { | ||||
| 	errorsMapMutex.Lock() | ||||
|  | @ -155,6 +99,7 @@ func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.T | |||
| 		tcpstate:   reassembly.NewTCPSimpleFSM(fsmOptions), | ||||
| 		ident:      fmt.Sprintf("%s:%s", net, transport), | ||||
| 		optchecker: reassembly.NewTCPOptionCheck(), | ||||
| 		tlsSession: d4tls.TLSSession{}, | ||||
| 	} | ||||
| 	return stream | ||||
| } | ||||
|  | @ -188,7 +133,7 @@ type tcpStream struct { | |||
| 	reversed       bool | ||||
| 	urls           []string | ||||
| 	ident          string | ||||
| 	tlsSession     TLSSession | ||||
| 	tlsSession     d4tls.TLSSession | ||||
| 	sync.Mutex | ||||
| } | ||||
| 
 | ||||
|  | @ -258,32 +203,19 @@ func (t *tcpStream) ReassembledSG(sg reassembly.ScatterGather, ac reassembly.Ass | |||
| 						switch tlsrecord.ETLSHandshakeMsgType { | ||||
| 						// Client Hello
 | ||||
| 						case 1: | ||||
| 							t.tlsSession.tlsHdskR.ETLSHandshakeClientHello = tlsrecord.ETLSHandshakeClientHello | ||||
| 							t.tlsSession.record.ClientIP, t.tlsSession.record.ServerIP, t.tlsSession.record.ClientPort, t.tlsSession.record.ServerPort = getIPPorts(t) | ||||
| 							// Set up first seen
 | ||||
| 							info := sg.CaptureInfo(0) | ||||
| 							t.tlsSession.record.Timestamp = info.Timestamp | ||||
| 							t.tlsSession.gatherJa3() | ||||
| 							cip, sip, cp, sp := getIPPorts(t) | ||||
| 							t.tlsSession.PopulateClientHello(tlsrecord.ETLSHandshakeClientHello, cip, sip, cp, sp, info.Timestamp) | ||||
| 							t.tlsSession.D4Fingerprinting("ja3") | ||||
| 						// Server Hello
 | ||||
| 						case 2: | ||||
| 							t.tlsSession.tlsHdskR.ETLSHandshakeServerHello = tlsrecord.ETLSHandshakeServerHello | ||||
| 							t.tlsSession.gatherJa3s() | ||||
| 							t.tlsSession.PopulateServerHello(tlsrecord.ETLSHandshakeServerHello) | ||||
| 							t.tlsSession.D4Fingerprinting("ja3s") | ||||
| 						// Server Certificate
 | ||||
| 						case 11: | ||||
| 							for _, asn1Data := range tlsrecord.ETLSHandshakeCertificate.Certificates { | ||||
| 								cert, err := x509.ParseCertificate(asn1Data) | ||||
| 								if err != nil { | ||||
| 									Error("tls", "Failed to parse certificate from server: %x", err) | ||||
| 								} else { | ||||
| 									h := sha256.New() | ||||
| 									h.Write(cert.Raw) | ||||
| 									t.tlsSession.record.Certificates = append(t.tlsSession.record.Certificates, CertMapElm{Certificate: cert, CertHash: fmt.Sprintf("%x", h.Sum(nil))}) | ||||
| 								} | ||||
| 							} | ||||
| 							// We compute ja3jl
 | ||||
| 							out, _ := tlsh.HashBytes(t.tlsSession.record.ja3jl()) | ||||
| 							t.tlsSession.record.TLSH = out.String() | ||||
| 							t.tlsSession.PopulateCertificate(tlsrecord.ETLSHandshakeCertificate) | ||||
| 
 | ||||
| 							t.tlsSession.D4Fingerprinting("tlsh") | ||||
| 							// If we get a cert, we consider the handshake as finished and ready to ship to D4
 | ||||
| 							queueSession(t.tlsSession) | ||||
| 						default: | ||||
|  | @ -306,107 +238,6 @@ func getIPPorts(t *tcpStream) (string, string, string, string) { | |||
| 	return ipc, ips, cp, ps | ||||
| } | ||||
| 
 | ||||
| func (sr *SessionRecord) ja3jl() []byte { | ||||
| 	buf := sr.JA3 + sr.JA3S | ||||
| 	for _, cert := range sr.Certificates { | ||||
| 		buf += fmt.Sprintf("%q", cert.Issuer) + fmt.Sprintf("%q", cert.Subject) | ||||
| 	} | ||||
| 	buf = strings.Replace(buf, "-", "", -1) | ||||
| 	buf = strings.Replace(buf, ",", "", -1) | ||||
| 	buf = strings.Replace(buf, "\"", "", -1) | ||||
| 
 | ||||
| 	return []byte(buf) | ||||
| } | ||||
| 
 | ||||
| func (ts *TLSSession) gatherJa3s() bool { | ||||
| 	var buf []byte | ||||
| 	buf = strconv.AppendInt(buf, int64(ts.tlsHdskR.ETLSHandshakeServerHello.Vers), 10) | ||||
| 	// byte (44) is ","
 | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Cipher Suites
 | ||||
| 	buf = strconv.AppendInt(buf, int64(ts.tlsHdskR.ETLSHandshakeServerHello.CipherSuite), 10) | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are extensions
 | ||||
| 	if len(ts.tlsHdskR.ETLSHandshakeServerHello.AllExtensions) > 0 { | ||||
| 		for i, e := range ts.tlsHdskR.ETLSHandshakeServerHello.AllExtensions { | ||||
| 			// TODO check this grease thingy
 | ||||
| 			if grease[uint16(e)] == false { | ||||
| 				buf = strconv.AppendInt(buf, int64(e), 10) | ||||
| 				if (i + 1) < len(ts.tlsHdskR.ETLSHandshakeServerHello.AllExtensions) { | ||||
| 					// byte(45) is "-"
 | ||||
| 					buf = append(buf, byte(45)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ts.record.JA3S = string(buf) | ||||
| 	tmp := md5.Sum(buf) | ||||
| 	ts.record.JA3SDigest = hex.EncodeToString(tmp[:]) | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (ts *TLSSession) gatherJa3() bool { | ||||
| 	var buf []byte | ||||
| 	buf = strconv.AppendInt(buf, int64(ts.tlsHdskR.ETLSHandshakeClientHello.Vers), 10) | ||||
| 	// byte (44) is ","
 | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Cipher Suites
 | ||||
| 	if len(ts.tlsHdskR.ETLSHandshakeClientHello.CipherSuites) > 0 { | ||||
| 		for i, cs := range ts.tlsHdskR.ETLSHandshakeClientHello.CipherSuites { | ||||
| 			buf = strconv.AppendInt(buf, int64(cs), 10) | ||||
| 			// byte(45) is "-"
 | ||||
| 			if (i + 1) < len(ts.tlsHdskR.ETLSHandshakeClientHello.CipherSuites) { | ||||
| 				buf = append(buf, byte(45)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are extensions
 | ||||
| 	if len(ts.tlsHdskR.ETLSHandshakeClientHello.AllExtensions) > 0 { | ||||
| 		for i, e := range ts.tlsHdskR.ETLSHandshakeClientHello.AllExtensions { | ||||
| 			// TODO check this grease thingy
 | ||||
| 			if grease[uint16(e)] == false { | ||||
| 				buf = strconv.AppendInt(buf, int64(e), 10) | ||||
| 				if (i + 1) < len(ts.tlsHdskR.ETLSHandshakeClientHello.AllExtensions) { | ||||
| 					buf = append(buf, byte(45)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Supported Curves
 | ||||
| 	if len(ts.tlsHdskR.ETLSHandshakeClientHello.SupportedCurves) > 0 { | ||||
| 		for i, cs := range ts.tlsHdskR.ETLSHandshakeClientHello.SupportedCurves { | ||||
| 			buf = strconv.AppendInt(buf, int64(cs), 10) | ||||
| 			if (i + 1) < len(ts.tlsHdskR.ETLSHandshakeClientHello.SupportedCurves) { | ||||
| 				buf = append(buf, byte(45)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Supported Points
 | ||||
| 	if len(ts.tlsHdskR.ETLSHandshakeClientHello.SupportedPoints) > 0 { | ||||
| 		for i, cs := range ts.tlsHdskR.ETLSHandshakeClientHello.SupportedPoints { | ||||
| 			buf = strconv.AppendInt(buf, int64(cs), 10) | ||||
| 			if (i + 1) < len(ts.tlsHdskR.ETLSHandshakeClientHello.SupportedPoints) { | ||||
| 				buf = append(buf, byte(45)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	ts.record.JA3 = string(buf) | ||||
| 	tmp := md5.Sum(buf) | ||||
| 	ts.record.JA3Digest = hex.EncodeToString(tmp[:]) | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (t *tcpStream) ReassemblyComplete(ac reassembly.AssemblerContext) bool { | ||||
| 	Debug("%s: Connection closed\n", t.ident) | ||||
| 	// do not remove the connection to allow last ACK
 | ||||
|  | @ -465,7 +296,7 @@ func main() { | |||
| 	signal.Notify(signalChan, os.Interrupt) | ||||
| 
 | ||||
| 	// Job chan to hold Completed sessions to write
 | ||||
| 	jobQ = make(chan TLSSession, 100) | ||||
| 	jobQ = make(chan d4tls.TLSSession, 100) | ||||
| 	cancelC := make(chan string) | ||||
| 
 | ||||
| 	// We start a worker to send the processed TLS connection the outside world
 | ||||
|  | @ -543,7 +374,7 @@ func main() { | |||
| 	w.Wait() | ||||
| } | ||||
| 
 | ||||
| func processCompletedSession(jobQ <-chan TLSSession, w *sync.WaitGroup) { | ||||
| func processCompletedSession(jobQ <-chan d4tls.TLSSession, w *sync.WaitGroup) { | ||||
| 	for { | ||||
| 		tlss, more := <-jobQ | ||||
| 		if more { | ||||
|  | @ -556,7 +387,7 @@ func processCompletedSession(jobQ <-chan TLSSession, w *sync.WaitGroup) { | |||
| } | ||||
| 
 | ||||
| // Tries to enqueue or false
 | ||||
| func queueSession(t TLSSession) bool { | ||||
| func queueSession(t d4tls.TLSSession) bool { | ||||
| 	select { | ||||
| 	case jobQ <- t: | ||||
| 		return true | ||||
|  | @ -565,14 +396,14 @@ func queueSession(t TLSSession) bool { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func output(t TLSSession) { | ||||
| func output(t d4tls.TLSSession) { | ||||
| 
 | ||||
| 	jsonRecord, _ := json.MarshalIndent(t.record, "", "    ") | ||||
| 	jsonRecord, _ := json.MarshalIndent(t.Record, "", "    ") | ||||
| 
 | ||||
| 	// If an output folder was specified for certificates
 | ||||
| 	if *outCerts != "" { | ||||
| 		if _, err := os.Stat(fmt.Sprintf("./%s", *outCerts)); !os.IsNotExist(err) { | ||||
| 			for _, certMe := range t.record.Certificates { | ||||
| 			for _, certMe := range t.Record.Certificates { | ||||
| 				err := ioutil.WriteFile(fmt.Sprintf("./%s/%s.crt", *outCerts, certMe.CertHash), certMe.Certificate.Raw, 0644) | ||||
| 				if err != nil { | ||||
| 					panic("Could not write to file.") | ||||
|  | @ -586,7 +417,7 @@ func output(t TLSSession) { | |||
| 	// If an output folder was specified for json files
 | ||||
| 	if *outJSON != "" { | ||||
| 		if _, err := os.Stat(fmt.Sprintf("./%s", *outJSON)); !os.IsNotExist(err) { | ||||
| 			err := ioutil.WriteFile(fmt.Sprintf("./%s/%s.json", *outJSON, t.record.Timestamp.Format(time.RFC3339)), jsonRecord, 0644) | ||||
| 			err := ioutil.WriteFile(fmt.Sprintf("./%s/%s.json", *outJSON, t.Record.Timestamp.Format(time.RFC3339)), jsonRecord, 0644) | ||||
| 			if err != nil { | ||||
| 				panic("Could not write to file.") | ||||
| 			} | ||||
|  |  | |||
|  | @ -0,0 +1,220 @@ | |||
| package d4tls | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/md5" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/x509" | ||||
| 	"encoding/hex" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/D4-project/sensor-d4-tls-fingerprinting/etls" | ||||
| 	"github.com/glaslos/tlsh" | ||||
| ) | ||||
| 
 | ||||
| var grease = map[uint16]bool{ | ||||
| 	0x0a0a: true, 0x1a1a: true, 0x2a2a: true, 0x3a3a: true, | ||||
| 	0x4a4a: true, 0x5a5a: true, 0x6a6a: true, 0x7a7a: true, | ||||
| 	0x8a8a: true, 0x9a9a: true, 0xaaaa: true, 0xbaba: true, | ||||
| 	0xcaca: true, 0xdada: true, 0xeaea: true, 0xfafa: true, | ||||
| } | ||||
| 
 | ||||
| type certMapElm struct { | ||||
| 	CertHash string | ||||
| 	*x509.Certificate | ||||
| } | ||||
| 
 | ||||
| type sessionRecord struct { | ||||
| 	ServerIP     string | ||||
| 	ServerPort   string | ||||
| 	ClientIP     string | ||||
| 	ClientPort   string | ||||
| 	TLSH         string | ||||
| 	Timestamp    time.Time | ||||
| 	JA3          string | ||||
| 	JA3Digest    string | ||||
| 	JA3S         string | ||||
| 	JA3SDigest   string | ||||
| 	Certificates []certMapElm | ||||
| } | ||||
| 
 | ||||
| // TLSSession contains a handshakeRecord that had to be filled during the handshake,
 | ||||
| // and a Record that will be at last exported to Json
 | ||||
| type TLSSession struct { | ||||
| 	Record          sessionRecord | ||||
| 	handShakeRecord etls.ETLSHandshakeRecord | ||||
| } | ||||
| 
 | ||||
| // String returns a string that describes a TLSSession
 | ||||
| func (t *TLSSession) String() string { | ||||
| 	var buf bytes.Buffer | ||||
| 	buf.WriteString(fmt.Sprintf("---------------SESSION START-------------------\n")) | ||||
| 	buf.WriteString(fmt.Sprintf("Time: %v\n", t.Record.Timestamp)) | ||||
| 	buf.WriteString(fmt.Sprintf("Client: %v:%v\n", t.Record.ClientIP, t.Record.ClientPort)) | ||||
| 	buf.WriteString(fmt.Sprintf("Server: %v:%v\n", t.Record.ServerIP, t.Record.ServerPort)) | ||||
| 	buf.WriteString(fmt.Sprintf("TLSH: %q\n", t.Record.TLSH)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3: %q\n", t.Record.JA3)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3 Digest: %q\n", t.Record.JA3Digest)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3s: %q\n", t.Record.JA3S)) | ||||
| 	buf.WriteString(fmt.Sprintf("ja3s Digest: %q\n", t.Record.JA3SDigest)) | ||||
| 	for _, certMe := range t.Record.Certificates { | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate Issuer: %q\n", certMe.Certificate.Issuer)) | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate Subject: %q\n", certMe.Certificate.Subject)) | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate is CA: %t\n", certMe.Certificate.IsCA)) | ||||
| 		buf.WriteString(fmt.Sprintf("Certificate SHA256: %q\n", certMe.CertHash)) | ||||
| 	} | ||||
| 	buf.WriteString(fmt.Sprintf("---------------SESSION  END--------------------\n")) | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // PopulateClientHello takes a pointer to an etls ClientHelloMsg and writes it to the the TLSSession struct
 | ||||
| func (t *TLSSession) PopulateClientHello(h *etls.ClientHelloMsg, cip string, sip string, cp string, sp string, ti time.Time) { | ||||
| 	t.Record.ClientIP = cip | ||||
| 	t.Record.ServerIP = sip | ||||
| 	t.Record.ClientPort = cp | ||||
| 	t.Record.ServerPort = sp | ||||
| 	t.Record.Timestamp = ti | ||||
| 	t.handShakeRecord.ETLSHandshakeClientHello = h | ||||
| } | ||||
| 
 | ||||
| // PopulateServerHello takes a pointer to an etls ServerHelloMsg and writes it to the TLSSession struct
 | ||||
| func (t *TLSSession) PopulateServerHello(h *etls.ServerHelloMsg) { | ||||
| 	t.handShakeRecord.ETLSHandshakeServerHello = h | ||||
| } | ||||
| 
 | ||||
| // PopulateCertificate takes a pointer to an etls ServerHelloMsg and writes it to the TLSSession struct
 | ||||
| func (t *TLSSession) PopulateCertificate(c *etls.CertificateMsg) { | ||||
| 	t.handShakeRecord.ETLSHandshakeCertificate = c | ||||
| 	for _, asn1Data := range t.handShakeRecord.ETLSHandshakeCertificate.Certificates { | ||||
| 		cert, err := x509.ParseCertificate(asn1Data) | ||||
| 		if err != nil { | ||||
| 			//return err
 | ||||
| 		} else { | ||||
| 			h := sha256.New() | ||||
| 			h.Write(cert.Raw) | ||||
| 			t.Record.Certificates = append(t.Record.Certificates, certMapElm{Certificate: cert, CertHash: fmt.Sprintf("%x", h.Sum(nil))}) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // D4Fingerprinting computes fingerprints doh
 | ||||
| func (t *TLSSession) D4Fingerprinting(fd string) bool { | ||||
| 	switch fd { | ||||
| 	case "ja3": | ||||
| 		t.ja3() | ||||
| 	case "ja3s": | ||||
| 		t.ja3s() | ||||
| 	case "tlsh": | ||||
| 		t.d4fg() | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (t *TLSSession) d4fg() string { | ||||
| 	buf := t.Record.JA3 + t.Record.JA3S | ||||
| 	for _, cert := range t.Record.Certificates { | ||||
| 		buf += fmt.Sprintf("%q", cert.Issuer) + fmt.Sprintf("%q", cert.Subject) | ||||
| 	} | ||||
| 	buf = strings.Replace(buf, "-", "", -1) | ||||
| 	buf = strings.Replace(buf, ",", "", -1) | ||||
| 	buf = strings.Replace(buf, "\"", "", -1) | ||||
| 
 | ||||
| 	out, _ := tlsh.HashBytes([]byte(buf)) | ||||
| 	t.Record.TLSH = out.String() | ||||
| 	return buf | ||||
| } | ||||
| 
 | ||||
| func (t *TLSSession) ja3s() bool { | ||||
| 	var buf []byte | ||||
| 	buf = strconv.AppendInt(buf, int64(t.handShakeRecord.ETLSHandshakeServerHello.Vers), 10) | ||||
| 	// byte (44) is ","
 | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Cipher Suites
 | ||||
| 	buf = strconv.AppendInt(buf, int64(t.handShakeRecord.ETLSHandshakeServerHello.CipherSuite), 10) | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are extensions
 | ||||
| 	if len(t.handShakeRecord.ETLSHandshakeServerHello.AllExtensions) > 0 { | ||||
| 		for i, e := range t.handShakeRecord.ETLSHandshakeServerHello.AllExtensions { | ||||
| 			// TODO check this grease thingy
 | ||||
| 			if grease[uint16(e)] == false { | ||||
| 				buf = strconv.AppendInt(buf, int64(e), 10) | ||||
| 				if (i + 1) < len(t.handShakeRecord.ETLSHandshakeServerHello.AllExtensions) { | ||||
| 					// byte(45) is "-"
 | ||||
| 					buf = append(buf, byte(45)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	t.Record.JA3S = string(buf) | ||||
| 	tmp := md5.Sum(buf) | ||||
| 	t.Record.JA3SDigest = hex.EncodeToString(tmp[:]) | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (t *TLSSession) ja3() bool { | ||||
| 	var buf []byte | ||||
| 	buf = strconv.AppendInt(buf, int64(t.handShakeRecord.ETLSHandshakeClientHello.Vers), 10) | ||||
| 	// byte (44) is ","
 | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Cipher Suites
 | ||||
| 	if len(t.handShakeRecord.ETLSHandshakeClientHello.CipherSuites) > 0 { | ||||
| 		for i, cs := range t.handShakeRecord.ETLSHandshakeClientHello.CipherSuites { | ||||
| 			buf = strconv.AppendInt(buf, int64(cs), 10) | ||||
| 			// byte(45) is "-"
 | ||||
| 			if (i + 1) < len(t.handShakeRecord.ETLSHandshakeClientHello.CipherSuites) { | ||||
| 				buf = append(buf, byte(45)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are extensions
 | ||||
| 	if len(t.handShakeRecord.ETLSHandshakeClientHello.AllExtensions) > 0 { | ||||
| 		for i, e := range t.handShakeRecord.ETLSHandshakeClientHello.AllExtensions { | ||||
| 			// TODO check this grease thingy
 | ||||
| 			if grease[uint16(e)] == false { | ||||
| 				buf = strconv.AppendInt(buf, int64(e), 10) | ||||
| 				if (i + 1) < len(t.handShakeRecord.ETLSHandshakeClientHello.AllExtensions) { | ||||
| 					buf = append(buf, byte(45)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Supported Curves
 | ||||
| 	if len(t.handShakeRecord.ETLSHandshakeClientHello.SupportedCurves) > 0 { | ||||
| 		for i, cs := range t.handShakeRecord.ETLSHandshakeClientHello.SupportedCurves { | ||||
| 			buf = strconv.AppendInt(buf, int64(cs), 10) | ||||
| 			if (i + 1) < len(t.handShakeRecord.ETLSHandshakeClientHello.SupportedCurves) { | ||||
| 				buf = append(buf, byte(45)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	buf = append(buf, byte(44)) | ||||
| 
 | ||||
| 	// If there are Supported Points
 | ||||
| 	if len(t.handShakeRecord.ETLSHandshakeClientHello.SupportedPoints) > 0 { | ||||
| 		for i, cs := range t.handShakeRecord.ETLSHandshakeClientHello.SupportedPoints { | ||||
| 			buf = strconv.AppendInt(buf, int64(cs), 10) | ||||
| 			if (i + 1) < len(t.handShakeRecord.ETLSHandshakeClientHello.SupportedPoints) { | ||||
| 				buf = append(buf, byte(45)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	t.Record.JA3 = string(buf) | ||||
| 	tmp := md5.Sum(buf) | ||||
| 	t.Record.JA3Digest = hex.EncodeToString(tmp[:]) | ||||
| 	return true | ||||
| } | ||||
|  | @ -14,9 +14,9 @@ import ( | |||
| type ETLSHandshakeRecord struct { | ||||
| 	ETLSRecordHeader | ||||
| 	ETLSHandshakeMsgType     uint8 | ||||
| 	ETLSHandshakeServerHello *serverHelloMsg | ||||
| 	ETLSHandshakeClientHello *clientHelloMsg | ||||
| 	ETLSHandshakeCertificate *certificateMsg | ||||
| 	ETLSHandshakeServerHello *ServerHelloMsg | ||||
| 	ETLSHandshakeClientHello *ClientHelloMsg | ||||
| 	ETLSHandshakeCertificate *CertificateMsg | ||||
| } | ||||
| 
 | ||||
| // DecodeFromBytes decodes the slice into the ETLS struct.
 | ||||
|  | @ -30,15 +30,15 @@ func (t *ETLSHandshakeRecord) decodeFromBytes(h ETLSRecordHeader, data []byte, d | |||
| 	switch uint8(data[0]) { | ||||
| 	case typeClientHello: | ||||
| 		t.ETLSHandshakeMsgType = typeClientHello | ||||
| 		t.ETLSHandshakeClientHello = new(clientHelloMsg) | ||||
| 		t.ETLSHandshakeClientHello = new(ClientHelloMsg) | ||||
| 		t.ETLSHandshakeClientHello.unmarshal(data) | ||||
| 	case typeServerHello: | ||||
| 		t.ETLSHandshakeMsgType = typeServerHello | ||||
| 		t.ETLSHandshakeServerHello = new(serverHelloMsg) | ||||
| 		t.ETLSHandshakeServerHello = new(ServerHelloMsg) | ||||
| 		t.ETLSHandshakeServerHello.unmarshal(data) | ||||
| 	case typeCertificate: | ||||
| 		t.ETLSHandshakeMsgType = typeCertificate | ||||
| 		t.ETLSHandshakeCertificate = new(certificateMsg) | ||||
| 		t.ETLSHandshakeCertificate = new(CertificateMsg) | ||||
| 		t.ETLSHandshakeCertificate.unmarshal(data) | ||||
| 	} | ||||
| 	// Please see the following url if you are interested into implementing the rest:
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ type handshakeMessage interface { | |||
| 	unmarshal([]byte) bool | ||||
| } | ||||
| 
 | ||||
| type clientHelloMsg struct { | ||||
| type ClientHelloMsg struct { | ||||
| 	raw                          []byte | ||||
| 	extensions                   map[Extension]uint16 | ||||
| 	AllExtensions                []uint16 | ||||
|  | @ -35,7 +35,7 @@ type clientHelloMsg struct { | |||
| 	alpnProtocols                []string | ||||
| } | ||||
| 
 | ||||
| func (m *clientHelloMsg) unmarshal(data []byte) bool { | ||||
| func (m *ClientHelloMsg) unmarshal(data []byte) bool { | ||||
| 	if len(data) < 42 { | ||||
| 		return false | ||||
| 	} | ||||
|  | @ -158,7 +158,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { | |||
| 	return true | ||||
| } | ||||
| 
 | ||||
| type serverHelloMsg struct { | ||||
| type ServerHelloMsg struct { | ||||
| 	raw                          []byte | ||||
| 	extensions                   map[Extension]uint16 | ||||
| 	AllExtensions                []uint16 | ||||
|  | @ -177,7 +177,7 @@ type serverHelloMsg struct { | |||
| 	alpnProtocol                 string | ||||
| } | ||||
| 
 | ||||
| func (m *serverHelloMsg) unmarshal(data []byte) bool { | ||||
| func (m *ServerHelloMsg) unmarshal(data []byte) bool { | ||||
| 	if len(data) < 42 { | ||||
| 		return false | ||||
| 	} | ||||
|  | @ -327,12 +327,12 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { | |||
| 	return true | ||||
| } | ||||
| 
 | ||||
| type certificateMsg struct { | ||||
| type CertificateMsg struct { | ||||
| 	raw          []byte | ||||
| 	Certificates [][]byte | ||||
| } | ||||
| 
 | ||||
| func (m *certificateMsg) unmarshal(data []byte) bool { | ||||
| func (m *CertificateMsg) unmarshal(data []byte) bool { | ||||
| 	if len(data) < 7 { | ||||
| 		return false | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Jean-Louis Huynen
						Jean-Louis Huynen