package inspect import ( "crypto/tls" "fmt" "strconv" ) // ConnectionState returns a set of information about a TLS ConnectionState. func ConnectionState(c tls.ConnectionState) []Info { var info = []Info{ &TLSConnectionState{ Version: c.Version, CipherSuite: c.CipherSuite, }, } for i, cert := range c.PeerCertificates { loc := "leaf certificate" if i > 0 { loc = fmt.Sprintf("intermediate certificate #%d", i) } info = append(info, CertificateInfo(loc, cert)) } return info } // TLSSecurity provides an indicative security assessment of a negotiated TLS // connection. type TLSSecurity int const ( TLSSecurityStrong = iota TLSSecurityWeak TLSSecurityInsecure ) func (ts TLSSecurity) String() string { switch ts { case TLSSecurityStrong: return "strong" case TLSSecurityWeak: return "weak" case TLSSecurityInsecure: return "insecure" } return "???" } // MarshalJSON returns a quoted string. func (ts TLSSecurity) MarshalJSON() ([]byte, error) { return strconv.AppendQuote(nil, ts.String()), nil } // MarshalYAML returns a string. func (ts TLSSecurity) MarshalYAML() (any, error) { return ts.String(), nil } // TLSConnectionState holds structured information about a negotiated TLS // connection. It does not include the peer's certificate information. type TLSConnectionState struct { // Version of TLS protocol used. Version uint16 // CipherSuite negotiated. CipherSuite uint16 } // Type indicates this is a TLS connection type. func (tcs *TLSConnectionState) Type() Type { return TypeTLSConnectionState } // Location returns the location data stored by TLSConnectionState. func (tcs *TLSConnectionState) Location() string { return "TLS handshake" } // Info returns structured information about the TLS connection. func (tcs *TLSConnectionState) Info() []Section { var security TLSSecurity = TLSSecurityStrong ciphersuite := tls.CipherSuiteName(tcs.CipherSuite) switch tcs.CipherSuite { case tls.TLS_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_AES_256_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA256, tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_RSA_WITH_AES_256_GCM_SHA384: // using plain RSA for key exchange isn't good security = TLSSecurityInsecure case tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: // broken/bad symmetric ciphers security = TLSSecurityInsecure case tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: // CBC now considered weaker security = TLSSecurityWeak default: // allow Go's opinion to augment our own for _, bad := range tls.InsecureCipherSuites() { if tcs.CipherSuite == bad.ID { security = TLSSecurityInsecure break } } } var version string switch tcs.Version { case tls.VersionTLS10: version = "TLSv1.0" security = TLSSecurityInsecure case tls.VersionTLS11: version = "TLSv1.1" security = TLSSecurityInsecure case tls.VersionTLS12: version = "TLSv1.2" case tls.VersionTLS13: version = "TLSv1.3" } return []Section{ Section{ Title: "TLS protocol negotatiated", Fields: []Field{ Field{ Key: "Version", Value: version, }, Field{ Key: "Cipher suite", Value: ciphersuite, }, Field{ Key: "Security", Value: security, }, }, }, } }