mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-20 06:54:20 +00:00
Introduce type alias CABundleHash for the hash of a CA bundle ([32]byte)
Co-authored-by: Ryan Richard <richardry@vmware.com> Co-authored-by: Ashish Amarnath <ashish.amarnath@broadcom.com>
This commit is contained in:
committed by
Ryan Richard
co-authored by
Ryan Richard
Ashish Amarnath
parent
99cfc4fbce
commit
a888083c50
@@ -1,3 +1,6 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tlsconfigutil
|
||||
|
||||
import (
|
||||
@@ -5,13 +8,24 @@ import (
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
var sHA256OfEmptyData = sha256.Sum256(nil)
|
||||
var zeroSHA256 = [32]byte{}
|
||||
type CABundleHash struct {
|
||||
hash [32]byte
|
||||
}
|
||||
|
||||
func NewCABundleHash(bundle []byte) CABundleHash {
|
||||
return CABundleHash{
|
||||
hash: sha256.Sum256(bundle),
|
||||
}
|
||||
}
|
||||
|
||||
func (a CABundleHash) Equal(b CABundleHash) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// CABundle abstracts the internal representation of CA certificate bundles.
|
||||
type CABundle struct {
|
||||
caBundle []byte
|
||||
sha256 [32]byte
|
||||
sha256 CABundleHash
|
||||
certPool *x509.CertPool
|
||||
}
|
||||
|
||||
@@ -26,7 +40,7 @@ func NewCABundle(caBundle []byte) (*CABundle, bool) {
|
||||
|
||||
return &CABundle{
|
||||
caBundle: caBundle,
|
||||
sha256: sha256.Sum256(caBundle),
|
||||
sha256: NewCABundleHash(caBundle),
|
||||
certPool: certPool,
|
||||
}, ok
|
||||
}
|
||||
@@ -56,13 +70,9 @@ func (c *CABundle) CertPool() *x509.CertPool {
|
||||
}
|
||||
|
||||
// Hash returns a sha256 sum of the CA bundle bytes.
|
||||
func (c *CABundle) Hash() [32]byte {
|
||||
if c == nil || len(c.caBundle) < 1 {
|
||||
return sHA256OfEmptyData
|
||||
func (c *CABundle) Hash() CABundleHash {
|
||||
if c == nil {
|
||||
return NewCABundleHash(nil)
|
||||
}
|
||||
// This handles improperly initialized receivers
|
||||
if c.sha256 == zeroSHA256 {
|
||||
c.sha256 = sha256.Sum256(c.caBundle)
|
||||
}
|
||||
return c.sha256 // note that this will always return the same hash for nil input
|
||||
return c.sha256
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tlsconfigutil
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -11,6 +13,37 @@ import (
|
||||
"go.pinniped.dev/internal/certauthority"
|
||||
)
|
||||
|
||||
func TestNewCABundleHash(t *testing.T) {
|
||||
sha256OfNil := CABundleHash{hash: [32]byte{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
|
||||
// On the command line, `echo "test" | shasum -a 256` yields "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
|
||||
// which is 32 bytes of data encoded as 64 characters.
|
||||
// https://stackoverflow.com/a/70565837
|
||||
// This is the actual binary data:
|
||||
sha256OfTest := CABundleHash{hash: [32]byte{159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8}}
|
||||
|
||||
t.Run("will hash the data given", func(t *testing.T) {
|
||||
caBundleHash := NewCABundleHash([]byte("test"))
|
||||
|
||||
require.True(t, sha256OfTest.Equal(caBundleHash))
|
||||
require.Equal(t, sha256OfTest, caBundleHash)
|
||||
})
|
||||
|
||||
t.Run("will return the hash of nil input", func(t *testing.T) {
|
||||
caBundleHash := NewCABundleHash(nil)
|
||||
|
||||
require.True(t, sha256OfNil.Equal(caBundleHash))
|
||||
require.Equal(t, sha256OfNil, caBundleHash)
|
||||
})
|
||||
|
||||
t.Run("will return the hash of empty input", func(t *testing.T) {
|
||||
caBundleHash := NewCABundleHash([]byte{})
|
||||
|
||||
require.True(t, sha256OfNil.Equal(caBundleHash))
|
||||
require.Equal(t, sha256OfNil, caBundleHash)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewCABundle(t *testing.T) {
|
||||
testCA, err := certauthority.New("Test CA", 1*time.Hour)
|
||||
require.NoError(t, err)
|
||||
@@ -20,7 +53,7 @@ func TestNewCABundle(t *testing.T) {
|
||||
require.True(t, ok)
|
||||
|
||||
require.Equal(t, testCA.Bundle(), caBundle.PEMBytes())
|
||||
require.Equal(t, sha256.Sum256(testCA.Bundle()), caBundle.Hash())
|
||||
require.Equal(t, NewCABundleHash(testCA.Bundle()), caBundle.Hash())
|
||||
require.Equal(t, string(testCA.Bundle()), caBundle.PEMString())
|
||||
require.True(t, testCA.Pool().Equal(caBundle.CertPool()), "should be the cert pool of the testCA")
|
||||
})
|
||||
@@ -30,7 +63,7 @@ func TestNewCABundle(t *testing.T) {
|
||||
require.False(t, ok)
|
||||
|
||||
require.Equal(t, []byte("here are some bytes"), caBundle.PEMBytes())
|
||||
require.Equal(t, sha256.Sum256([]byte("here are some bytes")), caBundle.Hash())
|
||||
require.Equal(t, NewCABundleHash([]byte("here are some bytes")), caBundle.Hash())
|
||||
require.Equal(t, "here are some bytes", caBundle.PEMString())
|
||||
require.True(t, x509.NewCertPool().Equal(caBundle.CertPool()), "should be an empty cert pool")
|
||||
})
|
||||
@@ -113,49 +146,27 @@ func TestCertPool(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
sha256OfNil := [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}
|
||||
t.Run("returns the Hash of the given CA bundle", func(t *testing.T) {
|
||||
caBundle, _ := NewCABundle([]byte("this is a CA bundle"))
|
||||
|
||||
// On the command line, `echo "test" | shasum -a 256` yields "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
|
||||
// which is 32 bytes of data encoded as 64 characters.
|
||||
// https://stackoverflow.com/a/70565837
|
||||
// This is the actual binary data:
|
||||
sha256OfTest := [32]byte{159, 134, 208, 129, 136, 76, 125, 101, 154, 47, 234, 160, 197, 90, 208, 21, 163, 191, 79, 27, 43, 11, 130, 44, 209, 93, 108, 21, 176, 240, 10, 8}
|
||||
|
||||
t.Run("returns the SHA256", func(t *testing.T) {
|
||||
caBundle, _ := NewCABundle([]byte("test"))
|
||||
|
||||
require.Equal(t, sha256OfTest, caBundle.Hash())
|
||||
require.True(t, NewCABundleHash([]byte("this is a CA bundle")).Equal(caBundle.Hash()))
|
||||
})
|
||||
|
||||
t.Run("returns the SHA256 when the PEM is nil", func(t *testing.T) {
|
||||
t.Run("returns the Hash of nil when the CA bundle is nil", func(t *testing.T) {
|
||||
caBundle, _ := NewCABundle(nil)
|
||||
|
||||
require.Equal(t, sha256OfNil, caBundle.Hash())
|
||||
require.True(t, NewCABundleHash(nil).Equal(caBundle.Hash()))
|
||||
})
|
||||
|
||||
t.Run("returns the SHA256 when the PEM is empty", func(t *testing.T) {
|
||||
t.Run("returns the Hash of nil when the CA bundle is empty", func(t *testing.T) {
|
||||
caBundle, _ := NewCABundle([]byte{})
|
||||
|
||||
require.Equal(t, sha256OfNil, caBundle.Hash())
|
||||
require.True(t, NewCABundleHash(nil).Equal(caBundle.Hash()))
|
||||
})
|
||||
|
||||
t.Run("handles nil receiver by returning the hash of nil", func(t *testing.T) {
|
||||
t.Run("returns the Hash of nil when the receiver is nil", func(t *testing.T) {
|
||||
var nilCABundle *CABundle
|
||||
|
||||
require.Equal(t, sha256OfNil, nilCABundle.Hash())
|
||||
})
|
||||
|
||||
t.Run("handles improperly initialized receiver by returning the hash of nil", func(t *testing.T) {
|
||||
caBundle := &CABundle{}
|
||||
|
||||
require.Equal(t, sha256OfNil, caBundle.Hash())
|
||||
})
|
||||
|
||||
t.Run("handles improperly initialized receiver by computing the hash", func(t *testing.T) {
|
||||
caBundle := &CABundle{
|
||||
caBundle: []byte("test"),
|
||||
}
|
||||
|
||||
require.Equal(t, sha256OfTest, caBundle.Hash())
|
||||
require.True(t, NewCABundleHash(nil).Equal(nilCABundle.Hash()))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -90,12 +90,6 @@ func ValidateTLSConfig(
|
||||
secretInformer corev1informers.SecretInformer,
|
||||
configMapInformer corev1informers.ConfigMapInformer,
|
||||
) (*metav1.Condition, *CABundle) {
|
||||
// TODO: This func should return a struct that abstracts away the internals of how a CA bundle is held in memory
|
||||
// and can return the CA bundle as string PEM, []byte base64-encoded, CertPool, hash, etc, as well as compare itself
|
||||
// to either a different struct instance or a hash.
|
||||
//
|
||||
// TODO: There could easily be a hash type struct alias for the specific hash value (e.g. "[32]byte") with an Equality function.
|
||||
|
||||
caBundle, err := buildCABundle(tlsSpec, conditionPrefix, namespace, secretInformer, configMapInformer)
|
||||
if err != nil {
|
||||
return invalidTLSCondition(err.Error()), nil
|
||||
|
||||
Reference in New Issue
Block a user