2023-03-01 21:28:48 +00:00
|
|
|
package inspect
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rsa"
|
|
|
|
"crypto/sha512"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
2023-04-29 10:56:58 +01:00
|
|
|
"strconv"
|
2023-03-01 21:28:48 +00:00
|
|
|
"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))
|
|
|
|
}
|
|
|
|
|
2023-04-29 10:56:58 +01:00
|
|
|
// MarshalJSON returns a quoted hex string.
|
|
|
|
func (f Fingerprint) MarshalJSON() ([]byte, error) {
|
|
|
|
return strconv.AppendQuote(nil, FormatHexBytes([]byte(f))), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalYAML returns a hex string.
|
|
|
|
func (f Fingerprint) MarshalYAML() (any, error) {
|
|
|
|
return FormatHexBytes([]byte(f)), nil
|
|
|
|
}
|
|
|
|
|
2023-03-01 21:28:48 +00:00
|
|
|
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()
|
|
|
|
}
|