Initial commit of pkg functions

This commit is contained in:
Laurence Withers 2023-03-01 21:28:48 +00:00
commit 6b6866077c
17 changed files with 1689 additions and 0 deletions

283
pkg/inspect/cert.go Normal file
View file

@ -0,0 +1,283 @@
package inspect
import (
"crypto/rsa"
"crypto/x509"
"errors"
"strings"
)
// DERCertificate attempts to parse a DER-form X.509 certificate, and returns
// information about it.
func DERCertificateInfo(loc string, der []byte) Info {
c, err := x509.ParseCertificate(der)
if err != nil {
return &BadInfo{
Typ: TypeX509Certificate,
Loc: loc,
Underlying: err,
}
}
return CertificateInfo(loc, c)
}
// CertificateInfo returns structured information about the given certificate.
// It can return BadInfo if the certificate is not for an RSA key. Note that
// the returned Info structure holds a reference to the passed certificate.
func CertificateInfo(loc string, cert *x509.Certificate) Info {
pubkey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return &BadInfo{
Typ: TypeX509Certificate,
Loc: loc,
Underlying: errors.New("certificate is not for an RSA key"),
}
}
c := &Certificate{
Loc: loc,
Key: PublicKeyInfo(loc, pubkey),
Cert: cert,
}
if (cert.KeyUsage & x509.KeyUsageDigitalSignature) != 0 {
c.ValidUses = append(c.ValidUses, "DigitalSignature")
}
if (cert.KeyUsage & x509.KeyUsageCertSign) != 0 {
c.ValidUses = append(c.ValidUses, "CertSign")
}
if (cert.KeyUsage & x509.KeyUsageCRLSign) != 0 {
c.ValidUses = append(c.ValidUses, "CRLSign")
}
for _, use := range cert.ExtKeyUsage {
switch use {
case x509.ExtKeyUsageAny:
c.ValidUses = append(c.ValidUses, "any")
case x509.ExtKeyUsageServerAuth:
c.ValidUses = append(c.ValidUses, "ServerAuth")
case x509.ExtKeyUsageClientAuth:
c.ValidUses = append(c.ValidUses, "ClientAuth")
}
}
if err := cert.CheckSignatureFrom(cert); err == nil {
c.IsSelfSigned = true
}
return c
}
type Certificate struct {
// Loc is the location the certificate was encountered.
Loc string
// Key holds information about the public key of the certificate.
Key *PublicKey
// Cert is the original certificate.
Cert *x509.Certificate
// The following section contains any computed values that are not
// immediately obtainable from the certificate.
// ValidUses is a set of named certificate uses.
ValidUses []string
// IsSelfSigned reports whether the certificate is self-signed.
IsSelfSigned bool
}
// Type indicates this is an X.509 certificate.
func (c *Certificate) Type() Type {
return TypeX509Certificate
}
// Location returns the location at which the certificate was encountered.
func (c *Certificate) Location() string {
return c.Loc
}
// Info returns structured information about the X.509 Certificate.
func (c *Certificate) Info() []Section {
var s []Section
s = append(s, c.UsageSection())
s = append(s, c.ValiditySection())
s = append(s, c.SubjectSection())
s = append(s, c.IssuerSection())
if c.Cert.IsCA {
s = append(s, c.CASection())
}
s = append(s, c.Key.PublicKeyInfoSection())
return s
}
// UsageSection returns information regarding the key usage bits.
func (c *Certificate) UsageSection() Section {
return Section{
Title: "Certificate usage",
Fields: []Field{
Field{
Key: "Is CA",
Value: c.Cert.IsCA,
},
Field{
Key: "Self-signed",
Value: c.IsSelfSigned,
},
Field{
Key: "Valid uses",
Value: strings.Join(c.ValidUses, " | "),
},
},
}
}
// ValiditySection returns information regarding the validity of the certificate.
func (c *Certificate) ValiditySection() Section {
return Section{
Title: "Validity",
Fields: []Field{
Field{
Key: "Valid from",
Value: c.Cert.NotBefore,
},
Field{
Key: "Valid until",
Value: c.Cert.NotAfter,
},
},
}
}
// SubjectSection returns information about the Subject of the certificate.
func (c *Certificate) SubjectSection() Section {
f := []Field{
Field{
Key: "Description",
Value: c.Cert.Subject.CommonName,
},
}
f = appendX509DNField(f, c.Cert.Subject)
if len(c.Cert.DNSNames) > 0 {
f = append(f, Field{
Key: "Hostnames",
Value: c.Cert.DNSNames,
})
}
if len(c.Cert.IPAddresses) > 0 {
var ips []string
for _, ip := range c.Cert.IPAddresses {
ips = append(ips, ip.String())
}
f = append(f, Field{
Key: "IP addresses",
Value: ips,
})
}
if len(c.Cert.SubjectKeyId) > 0 {
f = append(f, Field{
Key: "Key ID",
Value: FormatHexBytes(c.Cert.SubjectKeyId),
})
}
return Section{
Title: "Subject",
Fields: f,
}
}
// IssuerSection returns information about the Issuer of the certificate.
func (c *Certificate) IssuerSection() Section {
f := []Field{
Field{
Key: "Serial",
Value: FormatHexBytes(c.Cert.SerialNumber.Bytes()),
},
}
if c.Cert.Issuer.CommonName != "" {
f = append(f, Field{
Key: "Description",
Value: c.Cert.Issuer.CommonName,
})
}
f = appendX509DNField(f, c.Cert.Issuer)
if len(c.Cert.AuthorityKeyId) > 0 {
f = append(f, Field{
Key: "Key ID",
Value: FormatHexBytes(c.Cert.AuthorityKeyId),
})
}
return Section{
Title: "Issuer",
Fields: f,
}
}
// CASection returns information about a CA certificate.
func (c *Certificate) CASection() Section {
var f []Field
switch {
case c.Cert.MaxPathLen < 0,
c.Cert.MaxPathLen == 0 && !c.Cert.MaxPathLenZero:
f = append(f, Field{
Key: "Max path len",
Value: "unlimited",
})
default:
f = append(f, Field{
Key: "Max path len",
Value: c.Cert.MaxPathLen,
})
}
if len(c.Cert.PermittedDNSDomains) > 0 {
f = append(f, Field{
Key: "Permitted DNS names",
Value: c.Cert.PermittedDNSDomains,
})
}
if len(c.Cert.ExcludedDNSDomains) > 0 {
f = append(f, Field{
Key: "Excluded DNS names",
Value: c.Cert.ExcludedDNSDomains,
})
}
if len(c.Cert.PermittedIPRanges) > 0 {
var ips []string
for _, ip := range c.Cert.PermittedIPRanges {
ips = append(ips, ip.String())
}
f = append(f, Field{
Key: "Permitted IP addresses",
Value: ips,
})
}
if len(c.Cert.ExcludedIPRanges) > 0 {
var ips []string
for _, ip := range c.Cert.ExcludedIPRanges {
ips = append(ips, ip.String())
}
f = append(f, Field{
Key: "Excluded IP addresses",
Value: ips,
})
}
if len(c.Cert.CRLDistributionPoints) > 0 {
f = append(f, Field{
Key: "CRL distribution points",
Value: c.Cert.CRLDistributionPoints,
})
}
return Section{
Title: "Certificate authority",
Fields: f,
}
}

119
pkg/inspect/crl.go Normal file
View file

@ -0,0 +1,119 @@
package inspect
import (
"crypto/x509"
)
// DERCRLInfo attempts to parse a DER-form X.509 CRL and returns information
// about it.
func DERCRLInfo(loc string, der []byte) Info {
crl, err := x509.ParseRevocationList(der)
if err != nil {
return &BadInfo{
Typ: TypeX509CRL,
Loc: loc,
Underlying: err,
}
}
return CRLInfo(loc, crl)
}
// CRLInfo returns information about a CRL. Note it holds a reference to the
// crl argument.
func CRLInfo(loc string, crl *x509.RevocationList) *CRL {
return &CRL{
Loc: loc,
CRL: crl,
}
}
// CRL holds structured information about a CRL. It implements Info.
type CRL struct {
// Loc is the location the CRL was encountered.
Loc string
// CRL is the raw CRL.
CRL *x509.RevocationList
}
// Type indicates this is a private key.
func (crl *CRL) Type() Type {
return TypeX509CRL
}
// Location returns the location data stored by PrivateKeyInfo.
func (crl *CRL) Location() string {
return crl.Loc
}
// Info returns structured information about the prvate key.
func (crl *CRL) Info() []Section {
return []Section{
crl.CRLSection(),
crl.IssuerSection(),
crl.RevocationSection(),
}
}
// CRLSection returns metadata about the CRL itself.
func (crl *CRL) CRLSection() Section {
return Section{
Title: "Metadata",
Fields: []Field{
Field{
Key: "Serial number",
Value: FormatHexBytes(crl.CRL.Number.Bytes()),
},
Field{
Key: "Valid from",
Value: crl.CRL.ThisUpdate,
},
Field{
Key: "Valid until",
Value: crl.CRL.NextUpdate,
},
},
}
}
func (crl *CRL) IssuerSection() Section {
f := []Field{
Field{
Key: "Description",
Value: crl.CRL.Issuer.CommonName,
},
}
f = appendX509DNField(f, crl.CRL.Issuer)
if len(crl.CRL.AuthorityKeyId) > 0 {
f = append(f, Field{
Key: "Key ID",
Value: FormatHexBytes(crl.CRL.AuthorityKeyId),
})
}
return Section{
Title: "Issuer",
Fields: f,
}
}
func (crl *CRL) RevocationSection() Section {
revoked := make([]string, len(crl.CRL.RevokedCertificates))
for i := range crl.CRL.RevokedCertificates {
revoked[i] = FormatHexBytes(crl.CRL.RevokedCertificates[i].SerialNumber.Bytes())
}
return Section{
Title: "Revoked certificates",
Fields: []Field{
Field{
Key: "Count",
Value: len(revoked),
},
Field{
Key: "Revoked",
Value: revoked,
},
},
}
}

103
pkg/inspect/csr.go Normal file
View file

@ -0,0 +1,103 @@
package inspect
import (
"crypto/rsa"
"crypto/x509"
"errors"
)
// DERCSRInfo attempts to parse a DER-form X.509 CSR and returns information
// about it.
func DERCSRInfo(loc string, der []byte) Info {
csr, err := x509.ParseCertificateRequest(der)
if err != nil {
return &BadInfo{
Typ: TypeX509CSR,
Loc: loc,
Underlying: err,
}
}
return CSRInfo(loc, csr)
}
// CSRInfo returns information about a CSR. Note it holds a reference to the
// csr argument.
func CSRInfo(loc string, csr *x509.CertificateRequest) Info {
key, ok := csr.PublicKey.(*rsa.PublicKey)
if !ok {
return &BadInfo{
Typ: TypeX509CSR,
Loc: loc,
Underlying: errors.New("CSR public key is not RSA"),
}
}
return &CSR{
Loc: loc,
CSR: csr,
Public: PublicKeyInfo(loc, key),
}
}
// CSR holds structured information about a CSR. It implements Info.
type CSR struct {
// Loc is the location the CSR was encountered.
Loc string
// CSR is the raw CSR.
CSR *x509.CertificateRequest
// Public holds information about the public portion of the key.
Public *PublicKey
}
// Type indicates this is a private key.
func (csr *CSR) Type() Type {
return TypeX509CSR
}
// Location returns the location data stored by PrivateKeyInfo.
func (csr *CSR) Location() string {
return csr.Loc
}
// Info returns structured information about the prvate key.
func (csr *CSR) Info() []Section {
return []Section{
csr.SubjectSection(),
csr.Public.PublicKeyInfoSection(),
}
}
// SubjectSection returns information about the subject.
func (csr *CSR) SubjectSection() Section {
f := []Field{
Field{
Key: "Description",
Value: csr.CSR.Subject.CommonName,
},
}
f = appendX509DNField(f, csr.CSR.Subject)
if len(csr.CSR.DNSNames) > 0 {
f = append(f, Field{
Key: "Hostnames",
Value: csr.CSR.DNSNames,
})
}
if len(csr.CSR.IPAddresses) > 0 {
var ips []string
for _, ip := range csr.CSR.IPAddresses {
ips = append(ips, ip.String())
}
f = append(f, Field{
Key: "IP addresses",
Value: ips,
})
}
return Section{
Title: "Subject",
Fields: f,
}
}

110
pkg/inspect/inspect.go Normal file
View file

@ -0,0 +1,110 @@
/*
Package inspect describes RSA-related objects such as private keys and X.509
certificates using structured, annotated key-value pairs.
*/
package inspect
// Type of item.
type Type int
const (
TypePrivateKey Type = iota
TypePublicKey
TypeX509Certificate
TypeX509CRL
TypeX509CSR
TypeTLSConnectionState
)
func (t Type) String() string {
switch t {
case TypePrivateKey:
return "RSA private key"
case TypePublicKey:
return "RSA public key"
case TypeX509Certificate:
return "X.509 certificate"
case TypeX509CRL:
return "certificate revocation list"
case TypeX509CSR:
return "certificate signing request"
case TypeTLSConnectionState:
return "TLS connection state"
default:
return "???"
}
}
// Info about an item.
type Info interface {
// Type of item.
Type() Type
// Location where the item was encountered (e.g. line number for a file,
// or position in certificate chain for TLS handshake.
Location() string
// Info returns a structured set of information about the item.
Info() []Section
}
// Section is a set of related fields.
type Section struct {
// Title of section.
Title string
// Fields contain a related set of key, value pairs.
Fields []Field
}
// Field is an aspect of information about an item.
type Field struct {
// Key is the name of the aspect.
Key string
// Value is its value.
Value any
}
// BadInfo is an item which cannot be parsed, e.g. an invalid DER-encoded item
// within a PEM block. It implements both error and Info.
type BadInfo struct {
Typ Type
Loc string
Underlying error
}
// Type reports the type of item that was being parsed.
func (b *BadInfo) Type() Type {
return b.Typ
}
// Location reports further information about the location of the item.
func (b *BadInfo) Location() string {
return b.Loc
}
// Info reports an "Error encountered" section with an "Error" field.
func (b *BadInfo) Info() []Section {
return []Section{
Section{
Title: "Error encountered",
Fields: []Field{
Field{
Key: "Error",
Value: b.Underlying,
},
},
},
}
}
// Error returns the underlying error string.
func (b *BadInfo) Error() string {
return b.Underlying.Error()
}
// Unwrap returns the underlying error.
func (b *BadInfo) Unwrap() error {
return b.Underlying
}

44
pkg/inspect/pem.go Normal file
View file

@ -0,0 +1,44 @@
package inspect
import (
"bytes"
"encoding/pem"
"fmt"
"src.lwithers.me.uk/go/rsa/pkg/pemfile"
)
// LoadPEM returns a set of information about a PEM file.
func LoadPEM(data []byte) []Info {
var (
info []Info
line int = 1
)
for {
p, rest := pem.Decode(data)
if p == nil {
return info
}
loc := fmt.Sprintf("line %d", line)
switch p.Type {
case pemfile.TypePKCS1PrivateKey:
info = append(info, PKCS1PrivateKeyInfo(loc, p.Bytes))
case pemfile.TypePKCS8PrivateKey:
info = append(info, PKCS8PrivateKeyInfo(loc, p.Bytes))
case pemfile.TypePKCS1PublicKey:
info = append(info, PKCS1PublicKeyInfo(loc, p.Bytes))
case pemfile.TypePKIXPublicKey:
info = append(info, PKIXPublicKeyInfo(loc, p.Bytes))
case pemfile.TypeX509Certificate:
info = append(info, DERCertificateInfo(loc, p.Bytes))
case pemfile.TypeX509CRL:
info = append(info, DERCRLInfo(loc, p.Bytes))
case pemfile.TypeX509CSR:
info = append(info, DERCSRInfo(loc, p.Bytes))
}
line += bytes.Count(data[:len(data)-len(rest)], []byte{'\n'})
data = rest
}
}

112
pkg/inspect/private_key.go Normal file
View file

@ -0,0 +1,112 @@
package inspect
import (
"crypto/rsa"
"crypto/x509"
"errors"
)
// PKCS1PrivateKeyInfo attempts to parse a DER-form PKCS#1-encoded private RSA
// key, and returns information about it.
func PKCS1PrivateKeyInfo(loc string, der []byte) Info {
key, err := x509.ParsePKCS1PrivateKey(der)
if err != nil {
return &BadInfo{
Typ: TypePrivateKey,
Loc: loc,
Underlying: err,
}
}
return PrivateKeyInfo(loc, key)
}
// PKCS1PrivateKeyInfo attempts to parse a DER-form PKCS#8-encoded private RSA
// key, and returns information about it.
func PKCS8PrivateKeyInfo(loc string, der []byte) Info {
key, err := x509.ParsePKCS8PrivateKey(der)
if err != nil {
return &BadInfo{
Typ: TypePrivateKey,
Loc: loc,
Underlying: err,
}
}
rsakey, ok := key.(*rsa.PrivateKey)
if !ok {
return &BadInfo{
Typ: TypePrivateKey,
Underlying: errors.New("PKCS#8 private key ytpe is not RSA"),
}
}
return PrivateKeyInfo(loc, rsakey)
}
// PrivateKeyInfo returns structured information about the given RSA private key.
func PrivateKeyInfo(loc string, key *rsa.PrivateKey) *PrivateKey {
pl := make([]int, len(key.Primes))
for i, n := range key.Primes {
pl[i] = n.BitLen()
}
return &PrivateKey{
Loc: loc,
Primes: len(key.Primes),
PrimeLens: pl,
Public: PublicKeyInfo(loc, &key.PublicKey),
}
}
// PrivateKey holds structured information about an RSA private key. It
// implements Info.
type PrivateKey struct {
// Loc is the location the key was encountered.
Loc string
// Primes is the number of primes, ≥ 2.
Primes int
// PrimeLens holds the bit length of each prime.
PrimeLens []int
// Public holds information about the public portion of the key.
Public *PublicKey
}
// Type indicates this is a private key.
func (priv *PrivateKey) Type() Type {
return TypePrivateKey
}
// Location returns the location data stored by PrivateKeyInfo.
func (priv *PrivateKey) Location() string {
return priv.Loc
}
// Info returns structured information about the prvate key.
func (priv *PrivateKey) Info() []Section {
return []Section{
priv.PrivateKeyInfoSection(),
priv.Public.PublicKeyInfoSection(),
}
}
// PrivateKeyInfoSection returns the RSA private key specific information
// section.
func (priv *PrivateKey) PrivateKeyInfoSection() Section {
return Section{
Title: "RSA private key",
Fields: []Field{
Field{
Key: "Primes",
Value: priv.Primes,
},
Field{
Key: "Prime lengths",
Value: priv.PrimeLens,
},
},
}
}

145
pkg/inspect/public_key.go Normal file
View file

@ -0,0 +1,145 @@
package inspect
import (
"crypto/rsa"
"crypto/sha512"
"crypto/x509"
"encoding/binary"
"errors"
"strings"
)
// PKCS1PublicKeyInfo attempts to parse a DER-form PKCS#1-encoded public RSA
// key, and returns information about it.
func PKCS1PublicKeyInfo(loc string, der []byte) Info {
key, err := x509.ParsePKCS1PublicKey(der)
if err != nil {
return &BadInfo{
Typ: TypePublicKey,
Loc: loc,
Underlying: err,
}
}
return PublicKeyInfo(loc, key)
}
// PKCS1PublicKeyInfo attempts to parse a DER-form PKIX-encoded public RSA
// key, and returns information about it.
func PKIXPublicKeyInfo(loc string, der []byte) Info {
key, err := x509.ParsePKIXPublicKey(der)
if err != nil {
return &BadInfo{
Typ: TypePublicKey,
Loc: loc,
Underlying: err,
}
}
rsakey, ok := key.(*rsa.PublicKey)
if !ok {
return &BadInfo{
Typ: TypePublicKey,
Loc: loc,
Underlying: errors.New("PKIX public key type is not RSA"),
}
}
return PublicKeyInfo(loc, rsakey)
}
// PublicKeyInfo returns structured information about the given RSA public key.
func PublicKeyInfo(loc string, key *rsa.PublicKey) *PublicKey {
return &PublicKey{
Loc: loc,
Bits: key.N.BitLen(),
Fingerprint: KeyFingerprint(key),
}
}
// PublicKey holds structured information about an RSA public key. It
// implements Info.
type PublicKey struct {
// Loc is the location the key was encountered.
Loc string
// Bits is the key size.
Bits int
// Fingerprint of the modulus, which can be used to compare keys.
Fingerprint Fingerprint
}
// Type indicates this a a public key.
func (pub *PublicKey) Type() Type {
return TypePublicKey
}
// Location returns the location data stored by PublicKeyInfo.
func (pub *PublicKey) Location() string {
return pub.Loc
}
// Info returns structured information about the public key.
func (pub *PublicKey) Info() []Section {
return []Section{
pub.PublicKeyInfoSection(),
}
}
// PublicKeyInfoSection returns the RSA public key specific information
// section.
func (pub *PublicKey) PublicKeyInfoSection() Section {
return Section{
Title: "RSA public key",
Fields: []Field{
Field{
Key: "Bits",
Value: pub.Bits,
},
Field{
Key: "Fingerprint",
Value: pub.Fingerprint,
},
},
}
}
// Fingerprint of a key.
type Fingerprint []byte
// KeyFingerprint computes the fingerprint of an RSA key. It only requires the
// public section of the key for this.
func KeyFingerprint(key *rsa.PublicKey) Fingerprint {
f := sha512.New512_224()
var b [8]byte
E := uint64(key.E)
binary.BigEndian.PutUint64(b[:], E)
f.Write(b[:])
f.Write(key.N.Bytes())
return Fingerprint(f.Sum(nil))
}
// String returns a nicely-formatted hex string that is easy to read/compare.
func (f Fingerprint) String() string {
return FormatHexBytes([]byte(f))
}
const hexDig = "0123456789ABCDEF"
// FormatHexBytes returns a nicely-formatted hex string that is easy to read/compare.
func FormatHexBytes(b []byte) string {
var s strings.Builder
for len(b) > 1 {
s.WriteByte(hexDig[b[0]>>4])
s.WriteByte(hexDig[b[0]&15])
s.WriteByte(':')
b = b[1:]
}
if len(b) > 0 {
s.WriteByte(hexDig[b[0]>>4])
s.WriteByte(hexDig[b[0]&15])
}
return s.String()
}

158
pkg/inspect/tls.go Normal file
View file

@ -0,0 +1,158 @@
package inspect
import (
"crypto/tls"
"fmt"
)
// 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
}
// PrivateKeyInfo returns structured information about the given RSA private key.
/*
func PrivateKeyInfo(loc string, key *rsa.PrivateKey) *PrivateKey {
pl := make([]int, len(key.Primes))
for i, n := range key.Primes {
pl[i] = n.BitLen()
}
return &PrivateKey{
Loc: loc,
Primes: len(key.Primes),
PrimeLens: pl,
Public: PublicKeyInfo(loc, &key.PublicKey),
}
}
*/
// 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 "???"
}
// 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 private key.
func (tcs *TLSConnectionState) Type() Type {
return TypeTLSConnectionState
}
// Location returns the location data stored by PrivateKeyInfo.
func (tcs *TLSConnectionState) Location() string {
return "TLS handshake"
}
// Info returns structured information about the prvate key.
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,
},
},
},
}
}

18
pkg/inspect/x509_dn.go Normal file
View file

@ -0,0 +1,18 @@
package inspect
import "crypto/x509/pkix"
// appendX509DNField will append a field containing the X.509 distinguished name
// (approximately) if it contains anything more than a common name.
func appendX509DNField(in []Field, name pkix.Name) []Field {
name.CommonName = ""
s := name.String()
if s == "" {
return in
}
return append(in, Field{
Key: "Other",
Value: s,
})
}