diff --git a/README.md b/README.md index 2f0ff20..15ef5e6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,85 @@ Example: ``` -./tlshandshake www.fbi.com:443 -cipher X25519:P256:CECPQ2 +> go run cmd/tlshandshake/tlshandshake.go -tls_min 1.2 -tls_max 1.3 -groups X25519-SIDHp503 pqcrypto.uk +| TLS-Session: +----------------------------------------------------------------- + Protocol : 1.3 + Cipher : TLS_AES_128_GCM_SHA256 + Negotiated Group : X25519-SIDHp503 + Connection ID : d0129f4dea986b72 + SCTs : [] + Connection resumed : FALSE + EMS used : FALSE + Stapled OCSP response : 308201350a0100a082012e3082012a06092b0601... + +| Connection: +----------------------------------------------------------------- + Local address : 10.0.1.242:51536 + Remote address : 198.41.214.162:443 + +| Server Certificates: +----------------------------------------------------------------- + +Depth : 0 +Issuer : CN=DigiCert ECC Extended Validation Server CA,OU=www.digicert.com,O=DigiCert Inc,C=US +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 13836083707412516537413894398330316720 (0xa68bb984a507399f4716e809a44a7b0) + Signature Algorithm: ECDSA-SHA256 + Issuer: C=US,O=DigiCert Inc,OU=www.digicert.com,CN=DigiCert ECC Extended Validation Server CA + Validity + Not Before: Oct 30 00:00:00 2018 UTC + Not After : Nov 3 12:00:00 2020 UTC + Subject: UnknownOID=2.5.4.15,UnknownOID=1.3.6.1.4.1.311.60.2.1.3,UnknownOID=1.3.6.1.4.1.311.60.2.1.2,UnknownOID=2.5.4.5,C=US,ST=California,UnknownOID=2.5.4.7,O=Cloudflare, Inc.,CN=cloudflare.com + Subject Public Key Info: + Public Key Algorithm: ECDSA + Public-Key: (256 bit) + X: + ce:d7:61:49:49:fd:4b:35:8b:1b:86:bc:a3:c5:bc: + d8:20:6e:31:17:2d:92:8a:b7:34:f4:db:11:70:4e: + 49:16 + Y: + 61:fc:ae:fa:7f:ba:6f:0c:05:53:74:c6:79:7f:81: + 12:8a:f7:e2:5e:6c:f5:fa:10:69:6b:67:d9:d5:96: + 51:b0 + Curve: P-256 + X509v3 extensions: + X509v3 Authority Key Identifier: + keyid:F8:25:D9:A6:39:C7:C3:81:87:25:3E:30:54:91:18:21:40:9B:17:9D + X509v3 Subject Key Identifier: + DE:7F:7F:E6:7C:ED:ED:61:43:60:47:67:5D:86:2F:84:FD:A6:78:AD + X509v3 Subject Alternative Name: + DNS:cloudflare.com, DNS:www.cloudflare.com + X509v3 Key Usage: critical + Digital Signature + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://crl3.digicert.com/DigiCertECCExtendedValidationServerCA.crl, URI:http://crl4.digicert.com/DigiCertECCExtendedValidationServerCA.crl + + X509v3 Certificate Policies: + Policy: 2.16.840.1.114412.2.1 + Policy: 2.23.140.1.1 + Authority Information Access: + OCSP - URI:http://ocsp.digicert.com + CA Issuers - URI:http://cacerts.digicert.com/DigiCertECCExtendedValidationServerCA.crt + + X509v3 Basic Constraints: critical + CA:FALSE + Unknown extension 1.3.6.1.4.1.11129.2.4.2 + + Signature Algorithm: ECDSA-SHA256 + 30:65:02:30:1e:1b:3d:10:9a:50:23:2e:e6:86:11:13:46:a8: + 1d:e8:63:f8:2f:60:96:43:49:0a:49:30:73:55:f8:25:63:1d: + 46:59:da:a9:4b:98:68:99:3d:50:a8:c4:fc:52:0f:e3:02:31: + 00:d2:64:cc:ad:f8:92:b6:6b:fe:b7:a9:4e:8c:06:3b:fb:d3: + 08:9f:d9:04:10:80:b9:52:97:0a:14:24:a4:5a:8a:d7:27:3c: + 1e:86:cb:b7:a8:be:c3:c0:98:fa:4a:91:ae + ``` # Installation diff --git a/cmd/tlshandshake/tlshandshake.go b/cmd/tlshandshake/tlshandshake.go index ddf045a..562cb0a 100644 --- a/cmd/tlshandshake/tlshandshake.go +++ b/cmd/tlshandshake/tlshandshake.go @@ -3,13 +3,27 @@ package main import ( "errors" "flag" + "fmt" "os" + "path" "strings" hs "github.com/henrydcase/tlshandshake" - trs "github.com/henrydcase/trs" + "github.com/henrydcase/trs" ) +var tls_min, tls_max, named_groups, named_ciphers string +var dst_port string + +// Parse flags +func init() { + flag.StringVar(&tls_min, "tls_min", "1.0", "TLS version to use - minimal") + flag.StringVar(&tls_max, "tls_max", "1.3", "TLS version to use - maximal") + flag.StringVar(&named_groups, "groups", "", "ECDH group name to use for KEX") + flag.StringVar(&named_ciphers, "ciphers", "", "Named cipher IDs to use") + flag.StringVar(&dst_port, "port", "443", "Destination port number") +} + func getIDByName(m map[uint16]string, name string) (uint16, error) { for key, value := range m { if value == name { @@ -19,51 +33,68 @@ func getIDByName(m map[uint16]string, name string) (uint16, error) { return 0, errors.New("Unknown value") } +func usage() { + fmt.Fprintf(os.Stderr, "usage: %s [options] server:port\n", path.Base(os.Args[0])) + fmt.Fprintf(os.Stderr, "Flags:\n") + flag.PrintDefaults() + os.Exit(2) +} + // Usage client args host:port func main() { - var tls_version, named_groups, named_ciphers string - flag.StringVar(&tls_version, "tls_version", "1.3", "TLS version to use") - flag.StringVar(&named_groups, "groups", "X25519:P-256:P-384:P-521", "NamedGroups IDs to use") - flag.StringVar(&named_ciphers, "ciphers", "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384", "Named cipher IDs to use") + // parse flags flag.Parse() if flag.NArg() != 1 { - flag.Usage() - os.Exit(1) + usage() } client := hs.NewClient() client.Addr = flag.Arg(0) if !strings.Contains(client.Addr, ":") { - client.Addr += ":443" + client.Addr += ":" + dst_port } // Set requested DH groups client.TLS.CurvePreferences = []trs.CurveID{} - for _, ng := range strings.Split(named_groups, ":") { - id, err := getIDByName(hs.NamedGroupsToName, ng) - if err != nil { - panic("Wrong group name provided") + if len(named_groups) != 0 { + for _, ng := range strings.Split(named_groups, ":") { + id, err := getIDByName(hs.NamedGroupsToName, ng) + if err != nil { + panic("Wrong group name provided") + } + client.TLS.CurvePreferences = append(client.TLS.CurvePreferences, trs.CurveID(id)) } - client.TLS.CurvePreferences = append(client.TLS.CurvePreferences, trs.CurveID(id)) } - // Perform TLS handshake with each each requested CipherSuite - tlsID, err := getIDByName(hs.TlsVersionToName, tls_version) + // TLS min + tlsID, err := getIDByName(hs.TlsVersionToName, tls_min) if err != nil { panic("Unknown TLS version") } + client.TLS.MinVersion = tlsID + + // TLS max + tlsID, err = getIDByName(hs.TlsVersionToName, tls_max) + if err != nil { + panic("Unknown TLS version") + } + client.TLS.MaxVersion = tlsID cn := strings.Split(named_ciphers, ":") if len(cn) == 0 { panic("Cipher can't be null") } - id, err := getIDByName(hs.CipherSuiteIdToName, cn[0]) - if err != nil { - panic("Wrong cipher name provided") + if len(named_ciphers) != 0 { + id, err := getIDByName(hs.CipherSuiteIdToName, cn[0]) + if err != nil { + panic("Wrong cipher name provided") + } + client.TLS.CipherSuites = []uint16{id} } - client.SetMinMaxTLS(tlsID) - client.TLS.CipherSuites = []uint16{id} + + client.TLS.InsecureSkipVerify = true + // Let's go client.Run() } diff --git a/lib.go b/lib.go index 043f0b5..598bb04 100644 --- a/lib.go +++ b/lib.go @@ -2,10 +2,16 @@ package tlshandshake import ( "fmt" + "os" - trs "github.com/henrydcase/trs" + "github.com/henrydcase/trs" ) +type Client struct { + TLS trs.Config + Addr string +} + var TlsVersionToName = map[uint16]string{ trs.VersionTLS10: "1.0", trs.VersionTLS11: "1.1", @@ -14,11 +20,31 @@ var TlsVersionToName = map[uint16]string{ } var CipherSuiteIdToName = map[uint16]string{ - trs.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA", - trs.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", trs.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256", trs.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384", trs.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", + trs.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA", + trs.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + trs.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA", + trs.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA", + trs.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256", + trs.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256", + trs.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384", + trs.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + trs.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + trs.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + trs.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + trs.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + trs.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + trs.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + trs.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + trs.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + trs.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + trs.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + trs.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + trs.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + trs.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + trs.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", } var NamedGroupsToName = map[uint16]string{ @@ -30,13 +56,6 @@ var NamedGroupsToName = map[uint16]string{ uint16(trs.CurveP521): "P-521", } -var failed uint - -type Client struct { - TLS trs.Config - Addr string -} - func NewClient() *Client { var c Client c.TLS.InsecureSkipVerify = true @@ -44,18 +63,13 @@ func NewClient() *Client { } func (c *Client) Run() { - fmt.Printf("TLS %s with %s\n", TlsVersionToName[c.TLS.MinVersion], CipherSuiteIdToName[c.TLS.CipherSuites[0]]) - con, err := trs.Dial("tcp", c.Addr, &c.TLS) if err != nil { fmt.Printf("handshake failed: %v\n\n", err) - failed++ - return + os.Exit(1) } defer con.Close() - - fmt.Printf("[TLS: %s]\n", TlsVersionToName[con.ConnectionState().Version]) - fmt.Println("OK\n") + printTlsState(con) } func (c *Client) Clone() *Client { @@ -64,8 +78,3 @@ func (c *Client) Clone() *Client { clone.Addr = c.Addr return &clone } - -func (c *Client) SetMinMaxTLS(ver uint16) { - c.TLS.MinVersion = ver - c.TLS.MaxVersion = ver -} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..0445cee --- /dev/null +++ b/utils.go @@ -0,0 +1,53 @@ +package tlshandshake + +import ( + "encoding/hex" + "fmt" + "github.com/grantae/certinfo" + trs "github.com/henrydcase/trs" +) + +var tf = map[bool]string{ + true: "TRUE", + false: "FALSE", +} + +func toHex(b []byte) string { + str := make([]byte, hex.EncodedLen(len(b))) + hex.Encode(str, b[:]) + return string(str) +} + +func printTlsState(con *trs.Conn) { + state := con.ConnectionState() + fmt.Println("| TLS-Session:") + fmt.Println("-----------------------------------------------------------------") + fmt.Printf("\tProtocol\t\t: %s\n", TlsVersionToName[state.Version]) + fmt.Printf("\tCipher\t\t\t: %s\n", CipherSuiteIdToName[state.CipherSuite]) + fmt.Printf("\tNegotiated Group\t: %s\n", NamedGroupsToName[uint16(state.Group)]) + fmt.Printf("\tConnection ID\t\t: %s\n", toHex(state.ConnectionID)) + fmt.Printf("\tSCTs\t\t\t: %s\n", state.SignedCertificateTimestamps) + fmt.Printf("\tConnection resumed\t: %s\n", tf[state.DidResume]) + //fmt.Printf("\tNext protocol\t\t: %s\n", state.NegotiatedProtocol) + fmt.Printf("\tEMS used\t\t: %s\n", tf[con.UsedEMS()]) + fmt.Printf("\tStapled OCSP response\t: %s\n", toHex(state.OCSPResponse)) + + fmt.Println("\n| Connection:") + fmt.Println("-----------------------------------------------------------------") + fmt.Printf("\tLocal address\t\t: %s\n", con.LocalAddr()) + fmt.Printf("\tRemote address\t\t: %s\n", con.RemoteAddr()) + + fmt.Println("\n| Server Certificates:") + fmt.Println("-----------------------------------------------------------------") + for i, cert := range state.PeerCertificates { + fmt.Printf("Depth : %d\n", i) + fmt.Printf("Issuer : %s\n", cert.Issuer) + res, err := certinfo.CertificateText(cert) + if err != nil { + panic("Error parsing received server certificate") + } + fmt.Println(res) + + } + +}