Block keys using hex(sha256(spki)). (#4745)
In addition to base64(sha256(spki)). As part of that, change KeyDigest to return [32]byte, and add KeyDigestB64 which provides the base64-encoded output that KeyDigest used to provide. Also update all call sites.
This commit is contained in:
parent
324d92d7c5
commit
0db7d9ff89
27
core/util.go
27
core/util.go
|
@ -87,13 +87,15 @@ func Fingerprint256(data []byte) string {
|
|||
return base64.RawURLEncoding.EncodeToString(d.Sum(nil))
|
||||
}
|
||||
|
||||
// KeyDigest produces a padded, standard Base64-encoded SHA256 digest of a
|
||||
type Sha256Digest [sha256.Size]byte
|
||||
|
||||
// KeyDigest produces a Base64-encoded SHA256 digest of a
|
||||
// provided public key.
|
||||
func KeyDigest(key crypto.PublicKey) (string, error) {
|
||||
func KeyDigest(key crypto.PublicKey) (Sha256Digest, error) {
|
||||
switch t := key.(type) {
|
||||
case *jose.JSONWebKey:
|
||||
if t == nil {
|
||||
return "", fmt.Errorf("Cannot compute digest of nil key")
|
||||
return Sha256Digest{}, fmt.Errorf("Cannot compute digest of nil key")
|
||||
}
|
||||
return KeyDigest(t.Key)
|
||||
case jose.JSONWebKey:
|
||||
|
@ -103,17 +105,26 @@ func KeyDigest(key crypto.PublicKey) (string, error) {
|
|||
if err != nil {
|
||||
logger := blog.Get()
|
||||
logger.Debugf("Problem marshaling public key: %s", err)
|
||||
return "", err
|
||||
return Sha256Digest{}, err
|
||||
}
|
||||
spkiDigest := sha256.Sum256(keyDER)
|
||||
return base64.StdEncoding.EncodeToString(spkiDigest[0:32]), nil
|
||||
return sha256.Sum256(keyDER), nil
|
||||
}
|
||||
}
|
||||
|
||||
// KeyDigestB64 produces a padded, standard Base64-encoded SHA256 digest of a
|
||||
// provided public key.
|
||||
func KeyDigestB64(key crypto.PublicKey) (string, error) {
|
||||
digest, err := KeyDigest(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(digest[:]), nil
|
||||
}
|
||||
|
||||
// KeyDigestEquals determines whether two public keys have the same digest.
|
||||
func KeyDigestEquals(j, k crypto.PublicKey) bool {
|
||||
digestJ, errJ := KeyDigest(j)
|
||||
digestK, errK := KeyDigest(k)
|
||||
digestJ, errJ := KeyDigestB64(j)
|
||||
digestK, errK := KeyDigestB64(k)
|
||||
// Keys that don't have a valid digest (due to marshalling problems)
|
||||
// are never equal. So, e.g. nil keys are not equal.
|
||||
if errJ != nil || errK != nil {
|
||||
|
|
|
@ -78,15 +78,15 @@ func TestKeyDigest(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
digest, err := KeyDigest(jwk)
|
||||
digest, err := KeyDigestB64(jwk)
|
||||
test.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest JWK by value")
|
||||
digest, err = KeyDigest(&jwk)
|
||||
digest, err = KeyDigestB64(&jwk)
|
||||
test.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest JWK by reference")
|
||||
digest, err = KeyDigest(jwk.Key)
|
||||
digest, err = KeyDigestB64(jwk.Key)
|
||||
test.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest bare key")
|
||||
|
||||
// Test with unknown key type
|
||||
_, err = KeyDigest(struct{}{})
|
||||
_, err = KeyDigestB64(struct{}{})
|
||||
test.Assert(t, err != nil, "Should have rejected unknown key type")
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@ package goodkey
|
|||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
|
@ -10,13 +13,15 @@ import (
|
|||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// blockedKeys is a type for maintaining a map of Base64 encoded SHA256 hashes
|
||||
// blockedKeys is a type for maintaining a map of SHA256 hashes
|
||||
// of SubjectPublicKeyInfo's that should be considered blocked.
|
||||
// blockedKeys are created by using loadBlockedKeysList.
|
||||
type blockedKeys map[string]bool
|
||||
type blockedKeys map[core.Sha256Digest]bool
|
||||
|
||||
var ErrWrongDecodedSize = errors.New("not enough bytes decoded for sha256 hash")
|
||||
|
||||
// blocked checks if the given public key is considered administratively
|
||||
// blocked based on a Base64 encoded SHA256 hash of the SubjectPublicKeyInfo.
|
||||
// blocked based on a SHA256 hash of the SubjectPublicKeyInfo.
|
||||
// Important: blocked should not be called except on a blockedKeys instance
|
||||
// returned from loadBlockedKeysList.
|
||||
// function should not be used until after `loadBlockedKeysList` has returned.
|
||||
|
@ -33,7 +38,7 @@ func (b blockedKeys) blocked(key crypto.PublicKey) (bool, error) {
|
|||
}
|
||||
|
||||
// loadBlockedKeysList creates a blockedKeys object that can be used to check if
|
||||
// a key is blocked. It creates a lookup map from a list of Base64 encoded
|
||||
// a key is blocked. It creates a lookup map from a list of
|
||||
// SHA256 hashes of SubjectPublicKeyInfo's in the input YAML file
|
||||
// with the expected format:
|
||||
//
|
||||
|
@ -52,19 +57,41 @@ func loadBlockedKeysList(filename string) (*blockedKeys, error) {
|
|||
}
|
||||
|
||||
var list struct {
|
||||
BlockedHashes []string `yaml:"blocked"`
|
||||
BlockedHashes []string `yaml:"blocked"`
|
||||
BlockedHashesHex []string `yaml:"blockedHashesHex"`
|
||||
}
|
||||
if err := yaml.Unmarshal(yamlBytes, &list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(list.BlockedHashes) == 0 {
|
||||
if len(list.BlockedHashes) == 0 && len(list.BlockedHashesHex) == 0 {
|
||||
return nil, errors.New("no blocked hashes in YAML")
|
||||
}
|
||||
|
||||
blockedKeys := make(blockedKeys, len(list.BlockedHashes))
|
||||
for _, hash := range list.BlockedHashes {
|
||||
blockedKeys[hash] = true
|
||||
blockedKeys := make(blockedKeys, len(list.BlockedHashes)+len(list.BlockedHashesHex))
|
||||
for _, b64Hash := range list.BlockedHashes {
|
||||
decoded, err := base64.StdEncoding.DecodeString(b64Hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(decoded) != sha256.Size {
|
||||
return nil, ErrWrongDecodedSize
|
||||
}
|
||||
var sha256Digest core.Sha256Digest
|
||||
copy(sha256Digest[:], decoded[0:sha256.Size])
|
||||
blockedKeys[sha256Digest] = true
|
||||
}
|
||||
for _, hexHash := range list.BlockedHashesHex {
|
||||
decoded, err := hex.DecodeString(hexHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(decoded) != sha256.Size {
|
||||
return nil, ErrWrongDecodedSize
|
||||
}
|
||||
var sha256Digest core.Sha256Digest
|
||||
copy(sha256Digest[:], decoded[0:sha256.Size])
|
||||
blockedKeys[sha256Digest] = true
|
||||
}
|
||||
return &blockedKeys, nil
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ import (
|
|||
func TestBlockedKeys(t *testing.T) {
|
||||
// Start with an empty list
|
||||
var inList struct {
|
||||
BlockedHashes []string `yaml:"blocked"`
|
||||
BlockedHashes []string `yaml:"blocked"`
|
||||
BlockedHashesHex []string `yaml:"blockedHashesHex"`
|
||||
}
|
||||
|
||||
yamlList, err := yaml.Marshal(&inList)
|
||||
|
@ -56,7 +57,9 @@ func TestBlockedKeys(t *testing.T) {
|
|||
// public keys in the test certs/JWKs
|
||||
inList.BlockedHashes = []string{
|
||||
"cuwGhNNI6nfob5aqY90e7BleU6l7rfxku4X3UTJ3Z7M=",
|
||||
"Qebc1V3SkX3izkYRGNJilm9Bcuvf0oox4U2Rn+b4JOE=",
|
||||
}
|
||||
inList.BlockedHashesHex = []string{
|
||||
"41e6dcd55dd2917de2ce461118d262966f4172ebdfd28a31e14d919fe6f824e1",
|
||||
}
|
||||
|
||||
yamlList, err = yaml.Marshal(&inList)
|
||||
|
|
|
@ -190,7 +190,7 @@ func registrationToModel(r *core.Registration) (*regModel, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
sha, err := core.KeyDigest(r.Key)
|
||||
sha, err := core.KeyDigestB64(r.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
2
sa/sa.go
2
sa/sa.go
|
@ -115,7 +115,7 @@ func (ssa *SQLStorageAuthority) GetRegistrationByKey(ctx context.Context, key *j
|
|||
if key == nil {
|
||||
return core.Registration{}, fmt.Errorf("key argument to GetRegistrationByKey must not be nil")
|
||||
}
|
||||
sha, err := core.KeyDigest(key.Key)
|
||||
sha, err := core.KeyDigestB64(key.Key)
|
||||
if err != nil {
|
||||
return core.Registration{}, err
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ func main() {
|
|||
log.Fatalf("error loading public key: %v", err)
|
||||
}
|
||||
|
||||
spkiHash, err := core.KeyDigest(key)
|
||||
spkiHash, err := core.KeyDigestB64(key)
|
||||
if err != nil {
|
||||
log.Fatalf("error computing spki hash: %v", err)
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func TestKeyBlocking(t *testing.T) {
|
|||
key, err = keyFromCert(tc.certPath)
|
||||
}
|
||||
test.AssertNotError(t, err, "error getting key from input file")
|
||||
spkiHash, err := core.KeyDigest(key)
|
||||
spkiHash, err := core.KeyDigestB64(key)
|
||||
test.AssertNotError(t, err, "error computing spki hash")
|
||||
test.AssertEquals(t, spkiHash, tc.expected)
|
||||
})
|
||||
|
|
|
@ -26,3 +26,7 @@ blocked:
|
|||
- cuwGhNNI6nfob5aqY90e7BleU6l7rfxku4X3UTJ3Z7M=
|
||||
# test/block-a-key/test/test.rsa.jwk.json
|
||||
- Qebc1V3SkX3izkYRGNJilm9Bcuvf0oox4U2Rn+b4JOE=
|
||||
blockedHashesHex:
|
||||
- 41e6dcd55dd2917de2ce461118d262966f4172ebdfd28a31e14d919fe6f824e1
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue