@@ -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 | |||
@@ -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() | |||
} |
@@ -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 | |||
} |
@@ -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) | |||
} | |||
} |