diff --git a/pkg/controller/utils.go b/pkg/controller/utils.go index f9c1f45..44423a5 100644 --- a/pkg/controller/utils.go +++ b/pkg/controller/utils.go @@ -1,7 +1,7 @@ package controller import ( - "crypto/sha1" + "crypto/sha256" "encoding/base32" "reflect" "strings" @@ -10,15 +10,16 @@ import ( ) const ( - maxLength int = 63 - Base32EncodeStdLowerCase = "abcdefghijklmnopqrstuvwxyz234567" + Base32EncodeStdLowerCase = "abcdefghijklmnopqrstuvwxyz234567" ) -// K8sNameHash takes any number of string arguments and computes a hash out of it, which is then base32-encoded to be a valid k8s resource name. +// K8sNameHash takes any number of string arguments and computes a hash out of it, which is then base32-encoded to be a valid DNS1123Subdomain k8s resource name // The arguments are joined with '/' before being hashed. func K8sNameHash(ids ...string) string { name := strings.Join(ids, "/") - h := sha1.New() + // since we are not worried about length-extension attacks (in fact we are not even using hashing for + // any security purposes), use sha2 for better performance compared to sha3 + h := sha256.New() _, _ = h.Write([]byte(name)) // we need base32 encoding as some base64 (even url safe base64) characters are not supported by k8s // see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/ diff --git a/pkg/controller/utils_test.go b/pkg/controller/utils_test.go new file mode 100644 index 0000000..431615b --- /dev/null +++ b/pkg/controller/utils_test.go @@ -0,0 +1,50 @@ +package controller + +import ( + "fmt" + "testing" + + "k8s.io/apimachinery/pkg/util/validation" +) + +func TestK8sNameHash(t *testing.T) { + tt := []struct { + input []string + expHash string + }{ + { + []string{"test1"}, + "dnhq5gcrs4mzrzzsa6cujsllg3b5ahhn67fkgmrvtvxr3a2woaka", + }, + { + // check that the same string produces the same hash + []string{"test1"}, + "dnhq5gcrs4mzrzzsa6cujsllg3b5ahhn67fkgmrvtvxr3a2woaka", + }, + { + []string{"bla"}, + "jxz4h5upzsb3e7u5ileqimnhesm7c6dvzanftg2wnsmitoljm4bq", + }, + { + []string{"some other test", "this is a very, very long string"}, + "rjphpfjbmwn6qqydv6xhtmj3kxrlzepn2tpwy4okw2ypoc3nlffq", + }, + } + + for _, tc := range tt { + t.Run(fmt.Sprint(tc.input), func(t *testing.T) { + res := K8sNameHash(tc.input...) + + if res != tc.expHash { + t.Errorf("exp hash %q, got %q", tc.expHash, res) + } + + // ensure the result is a valid DNS1123Subdomain + if errs := validation.IsDNS1123Subdomain(res); errs != nil { + t.Errorf("value %q is invalid: %v", res, errs) + } + + }) + } + +}