Skip to content

Commit

Permalink
Make FingerprintCertsSelector hash-independent (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
jessepeterson authored Mar 30, 2021
1 parent 04955d0 commit fa847ce
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 18 deletions.
30 changes: 17 additions & 13 deletions cmd/scepclient/scepclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package main

import (
"context"
"crypto/sha256"
"crypto"
_ "crypto/sha256"
"crypto/x509"
"encoding/hex"
"flag"
Expand All @@ -28,6 +29,8 @@ var (
version = "unknown"
)

const fingerprintHashType = crypto.SHA256

type runCfg struct {
dir string
csrPath string
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
13 changes: 8 additions & 5 deletions scep/certs_selector.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package scep

import (
"crypto/sha256"
"bytes"
"crypto"
"crypto/x509"
)

Expand Down Expand Up @@ -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
}
Expand Down
56 changes: 56 additions & 0 deletions scep/certs_selector_test.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit fa847ce

Please sign in to comment.