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))
|
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.
|
// provided public key.
|
||||||
func KeyDigest(key crypto.PublicKey) (string, error) {
|
func KeyDigest(key crypto.PublicKey) (Sha256Digest, error) {
|
||||||
switch t := key.(type) {
|
switch t := key.(type) {
|
||||||
case *jose.JSONWebKey:
|
case *jose.JSONWebKey:
|
||||||
if t == nil {
|
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)
|
return KeyDigest(t.Key)
|
||||||
case jose.JSONWebKey:
|
case jose.JSONWebKey:
|
||||||
|
@ -103,17 +105,26 @@ func KeyDigest(key crypto.PublicKey) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger := blog.Get()
|
logger := blog.Get()
|
||||||
logger.Debugf("Problem marshaling public key: %s", err)
|
logger.Debugf("Problem marshaling public key: %s", err)
|
||||||
return "", err
|
return Sha256Digest{}, err
|
||||||
}
|
}
|
||||||
spkiDigest := sha256.Sum256(keyDER)
|
return sha256.Sum256(keyDER), nil
|
||||||
return base64.StdEncoding.EncodeToString(spkiDigest[0:32]), 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.
|
// KeyDigestEquals determines whether two public keys have the same digest.
|
||||||
func KeyDigestEquals(j, k crypto.PublicKey) bool {
|
func KeyDigestEquals(j, k crypto.PublicKey) bool {
|
||||||
digestJ, errJ := KeyDigest(j)
|
digestJ, errJ := KeyDigestB64(j)
|
||||||
digestK, errK := KeyDigest(k)
|
digestK, errK := KeyDigestB64(k)
|
||||||
// Keys that don't have a valid digest (due to marshalling problems)
|
// Keys that don't have a valid digest (due to marshalling problems)
|
||||||
// are never equal. So, e.g. nil keys are not equal.
|
// are never equal. So, e.g. nil keys are not equal.
|
||||||
if errJ != nil || errK != nil {
|
if errJ != nil || errK != nil {
|
||||||
|
|
|
@ -78,15 +78,15 @@ func TestKeyDigest(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
digest, err := KeyDigest(jwk)
|
digest, err := KeyDigestB64(jwk)
|
||||||
test.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest JWK by value")
|
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")
|
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.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest bare key")
|
||||||
|
|
||||||
// Test with unknown key type
|
// Test with unknown key type
|
||||||
_, err = KeyDigest(struct{}{})
|
_, err = KeyDigestB64(struct{}{})
|
||||||
test.Assert(t, err != nil, "Should have rejected unknown key type")
|
test.Assert(t, err != nil, "Should have rejected unknown key type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@ package goodkey
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
|
@ -10,13 +13,15 @@ import (
|
||||||
yaml "gopkg.in/yaml.v2"
|
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.
|
// of SubjectPublicKeyInfo's that should be considered blocked.
|
||||||
// blockedKeys are created by using loadBlockedKeysList.
|
// 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 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
|
// Important: blocked should not be called except on a blockedKeys instance
|
||||||
// returned from loadBlockedKeysList.
|
// returned from loadBlockedKeysList.
|
||||||
// function should not be used until after `loadBlockedKeysList` has returned.
|
// 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
|
// 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
|
// SHA256 hashes of SubjectPublicKeyInfo's in the input YAML file
|
||||||
// with the expected format:
|
// with the expected format:
|
||||||
//
|
//
|
||||||
|
@ -52,19 +57,41 @@ func loadBlockedKeysList(filename string) (*blockedKeys, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var list struct {
|
var list struct {
|
||||||
BlockedHashes []string `yaml:"blocked"`
|
BlockedHashes []string `yaml:"blocked"`
|
||||||
|
BlockedHashesHex []string `yaml:"blockedHashesHex"`
|
||||||
}
|
}
|
||||||
if err := yaml.Unmarshal(yamlBytes, &list); err != nil {
|
if err := yaml.Unmarshal(yamlBytes, &list); err != nil {
|
||||||
return nil, err
|
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")
|
return nil, errors.New("no blocked hashes in YAML")
|
||||||
}
|
}
|
||||||
|
|
||||||
blockedKeys := make(blockedKeys, len(list.BlockedHashes))
|
blockedKeys := make(blockedKeys, len(list.BlockedHashes)+len(list.BlockedHashesHex))
|
||||||
for _, hash := range list.BlockedHashes {
|
for _, b64Hash := range list.BlockedHashes {
|
||||||
blockedKeys[hash] = true
|
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
|
return &blockedKeys, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@ import (
|
||||||
func TestBlockedKeys(t *testing.T) {
|
func TestBlockedKeys(t *testing.T) {
|
||||||
// Start with an empty list
|
// Start with an empty list
|
||||||
var inList struct {
|
var inList struct {
|
||||||
BlockedHashes []string `yaml:"blocked"`
|
BlockedHashes []string `yaml:"blocked"`
|
||||||
|
BlockedHashesHex []string `yaml:"blockedHashesHex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
yamlList, err := yaml.Marshal(&inList)
|
yamlList, err := yaml.Marshal(&inList)
|
||||||
|
@ -56,7 +57,9 @@ func TestBlockedKeys(t *testing.T) {
|
||||||
// public keys in the test certs/JWKs
|
// public keys in the test certs/JWKs
|
||||||
inList.BlockedHashes = []string{
|
inList.BlockedHashes = []string{
|
||||||
"cuwGhNNI6nfob5aqY90e7BleU6l7rfxku4X3UTJ3Z7M=",
|
"cuwGhNNI6nfob5aqY90e7BleU6l7rfxku4X3UTJ3Z7M=",
|
||||||
"Qebc1V3SkX3izkYRGNJilm9Bcuvf0oox4U2Rn+b4JOE=",
|
}
|
||||||
|
inList.BlockedHashesHex = []string{
|
||||||
|
"41e6dcd55dd2917de2ce461118d262966f4172ebdfd28a31e14d919fe6f824e1",
|
||||||
}
|
}
|
||||||
|
|
||||||
yamlList, err = yaml.Marshal(&inList)
|
yamlList, err = yaml.Marshal(&inList)
|
||||||
|
|
|
@ -190,7 +190,7 @@ func registrationToModel(r *core.Registration) (*regModel, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sha, err := core.KeyDigest(r.Key)
|
sha, err := core.KeyDigestB64(r.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if key == nil {
|
||||||
return core.Registration{}, fmt.Errorf("key argument to GetRegistrationByKey must not be 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 {
|
if err != nil {
|
||||||
return core.Registration{}, err
|
return core.Registration{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ func main() {
|
||||||
log.Fatalf("error loading public key: %v", err)
|
log.Fatalf("error loading public key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
spkiHash, err := core.KeyDigest(key)
|
spkiHash, err := core.KeyDigestB64(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error computing spki hash: %v", err)
|
log.Fatalf("error computing spki hash: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ func TestKeyBlocking(t *testing.T) {
|
||||||
key, err = keyFromCert(tc.certPath)
|
key, err = keyFromCert(tc.certPath)
|
||||||
}
|
}
|
||||||
test.AssertNotError(t, err, "error getting key from input file")
|
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.AssertNotError(t, err, "error computing spki hash")
|
||||||
test.AssertEquals(t, spkiHash, tc.expected)
|
test.AssertEquals(t, spkiHash, tc.expected)
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,3 +26,7 @@ blocked:
|
||||||
- cuwGhNNI6nfob5aqY90e7BleU6l7rfxku4X3UTJ3Z7M=
|
- cuwGhNNI6nfob5aqY90e7BleU6l7rfxku4X3UTJ3Z7M=
|
||||||
# test/block-a-key/test/test.rsa.jwk.json
|
# test/block-a-key/test/test.rsa.jwk.json
|
||||||
- Qebc1V3SkX3izkYRGNJilm9Bcuvf0oox4U2Rn+b4JOE=
|
- Qebc1V3SkX3izkYRGNJilm9Bcuvf0oox4U2Rn+b4JOE=
|
||||||
|
blockedHashesHex:
|
||||||
|
- 41e6dcd55dd2917de2ce461118d262966f4172ebdfd28a31e14d919fe6f824e1
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue