diff --git a/cmd/scepclient/scepclient.go b/cmd/scepclient/scepclient.go index 3827241..3b61222 100644 --- a/cmd/scepclient/scepclient.go +++ b/cmd/scepclient/scepclient.go @@ -2,7 +2,8 @@ package main import ( "context" - "crypto/sha256" + "crypto" + _ "crypto/sha256" "crypto/x509" "encoding/hex" "flag" @@ -28,6 +29,8 @@ var ( version = "unknown" ) +const fingerprintHashType = crypto.SHA256 + type runCfg struct { dir string csrPath string @@ -213,35 +216,36 @@ func run(cfg runCfg) error { return nil } -// logCerts logs the count, number, RDN, and SHA-256 of certs to logger +// logCerts logs the count, number, RDN, and fingerprint of certs to logger func logCerts(logger log.Logger, certs []*x509.Certificate) { logger.Log("msg", "cacertlist", "count", len(certs)) for i, cert := range certs { + h := fingerprintHashType.New() + h.Write(cert.Raw) logger.Log( "msg", "cacertlist", "number", i, "rdn", cert.Subject.ToRDNSequence().String(), - "sha256", fmt.Sprintf("%x", sha256.Sum256(cert.Raw)), + "hash_type", fingerprintHashType.String(), + "hash", fmt.Sprintf("%x", h.Sum(nil)), ) } } -// validateSHA256Fingerprint makes sure fingerprint looks like a SHA-256 hash. +// validateFingerprint makes sure fingerprint looks like a hash. // We remove spaces and colons from fingerprint as it may come in various forms: // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 // E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 // e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855 // e3:b0:c4:42:98:fc:1c:14:9a:fb:f4:c8:99:6f:b9:24:27:ae:41:e4:64:9b:93:4c:a4:95:99:1b:78:52:b8:55 -func validateSHA256Fingerprint(fingerprint string) (hash [32]byte, err error) { +func validateFingerprint(fingerprint string) (hash []byte, err error) { fingerprint = strings.NewReplacer(" ", "", ":", "").Replace(fingerprint) - byteSlice, err := hex.DecodeString(fingerprint) - copy(hash[:], byteSlice) + hash, err = hex.DecodeString(fingerprint) if err != nil { return } - // check for length of SHA-256 - if len(byteSlice) != 32 { - err = errors.New("invalid SHA-256 hash length") + if len(hash) != fingerprintHashType.Size() { + err = fmt.Errorf("invalid %s hash length", fingerprintHashType) } return } @@ -298,12 +302,12 @@ func main() { caCertsSelector := scep.NopCertsSelector() if *flCAFingerprint != "" { - hash, err := validateSHA256Fingerprint(*flCAFingerprint) + hash, err := validateFingerprint(*flCAFingerprint) if err != nil { - fmt.Println(fmt.Errorf("invalid fingerprint: %v", err)) + fmt.Printf("invalid fingerprint: %s\n", err) os.Exit(1) } - caCertsSelector = scep.SHA256FingerprintCertsSelector(hash) + caCertsSelector = scep.FingerprintCertsSelector(fingerprintHashType, hash) } dir := filepath.Dir(*flPKeyPath) diff --git a/scep/certs_selector.go b/scep/certs_selector.go index 4cc8cea..62d726d 100644 --- a/scep/certs_selector.go +++ b/scep/certs_selector.go @@ -1,7 +1,8 @@ package scep import ( - "crypto/sha256" + "bytes" + "crypto" "crypto/x509" ) @@ -39,12 +40,14 @@ func EnciphermentCertsSelector() CertsSelectorFunc { } } -// SHA256FingerprintCertsSelector selects a certificate that matches -// a SHA-256 hash of the raw certificate DER bytes -func SHA256FingerprintCertsSelector(hash [32]byte) CertsSelectorFunc { +// FingerprintCertsSelector selects a certificate that matches hash using +// hashType against the digest of the raw certificate DER bytes +func FingerprintCertsSelector(hashType crypto.Hash, hash []byte) CertsSelectorFunc { return func(certs []*x509.Certificate) (selected []*x509.Certificate) { for _, cert := range certs { - if sha256.Sum256(cert.Raw) == hash { + h := hashType.New() + h.Write(cert.Raw) + if bytes.Compare(hash, h.Sum(nil)) == 0 { selected = append(selected, cert) return } diff --git a/scep/certs_selector_test.go b/scep/certs_selector_test.go index 0743ad8..643249a 100644 --- a/scep/certs_selector_test.go +++ b/scep/certs_selector_test.go @@ -1,10 +1,66 @@ package scep import ( + "crypto" + _ "crypto/sha256" "crypto/x509" + "encoding/hex" "testing" ) +func TestFingerprintCertsSelector(t *testing.T) { + for _, test := range []struct { + testName string + hashType crypto.Hash + hash string + certRaw []byte + expectedCount int + }{ + { + "null SHA-256 hash", + crypto.SHA256, + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + nil, + 1, + }, + { + "3 byte SHA-256 hash", + crypto.SHA256, + "039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81", + []byte{1, 2, 3}, + 1, + }, + { + "mismatched hash", + crypto.SHA256, + "8db07061ebb4cd0b0cd00825b363e5fb7f8131d8ff2c1fd70d03fa4fd6dc3785", + []byte{4, 5, 6}, + 0, + }, + } { + test := test + t.Run(test.testName, func(t *testing.T) { + t.Parallel() + + fakeCerts := []*x509.Certificate{{Raw: test.certRaw}} + + hash, err := hex.DecodeString(test.hash) + if err != nil { + t.Fatal(err) + } + if want, have := test.hashType.Size(), len(hash); want != have { + t.Errorf("invalid input hash length, want: %d have: %d", want, have) + } + + selected := FingerprintCertsSelector(test.hashType, hash).SelectCerts(fakeCerts) + + if want, have := test.expectedCount, len(selected); want != have { + t.Errorf("wrong selected certs count, want: %d have: %d", want, have) + } + }) + } +} + func TestEnciphermentCertsSelector(t *testing.T) { for _, test := range []struct { testName string