From 67bd820c80a2aeec4545dccd79c9aaa9d4f9e79a Mon Sep 17 00:00:00 2001 From: Ramon Petgrave <32398091+ramonpetgrave64@users.noreply.github.com> Date: Thu, 6 Feb 2025 14:08:17 -0500 Subject: [PATCH] cliplugin: semver, add tests for hash func encoding (#1948) Adds tests against know hash func and their JSON encoding. The current set of hash funcs and their encoding has not changed in ~8 years, but they might change in the future, since they are ordered by iota. https://cs.opensource.google/go/go/+/refs/tags/go1.23.5:src/crypto/crypto.go;l=68-89;bpv=0 This PR adds a test that can give us a warning if and when they do change, and we would likely need a major version bump of the protocol version. adds semver to the plugin protocol version at "v1". Signed-off-by: Ramon Petgrave --- .../kms/cliplugin/common/interface.go | 3 +- .../kms/cliplugin/common/interface_test.go | 117 +++++++++++++++++- test/cliplugin/localkms/main.go | 3 +- 3 files changed, 114 insertions(+), 9 deletions(-) diff --git a/pkg/signature/kms/cliplugin/common/interface.go b/pkg/signature/kms/cliplugin/common/interface.go index bc735263e..5868f79de 100644 --- a/pkg/signature/kms/cliplugin/common/interface.go +++ b/pkg/signature/kms/cliplugin/common/interface.go @@ -26,8 +26,7 @@ const ( // Breaking changes to the PluginClient and this schema necessarily mean major version bumps of // this ProtocolVersion and the sigstore version. // Plugin authors may choose to be backwards compatible with older versions. - // TODO: change this to be semver compatible, like v1, or v1.0.0. - ProtocolVersion = "1" + ProtocolVersion = "v1" DefaultAlgorithmMethodName = "defaultAlgorithm" SupportedAlgorithmsMethodName = "supportedAlgorithms" CreateKeyMethodName = "createKey" diff --git a/pkg/signature/kms/cliplugin/common/interface_test.go b/pkg/signature/kms/cliplugin/common/interface_test.go index 8a0c0530c..71f0d82f2 100644 --- a/pkg/signature/kms/cliplugin/common/interface_test.go +++ b/pkg/signature/kms/cliplugin/common/interface_test.go @@ -19,6 +19,7 @@ package common import ( "crypto" "encoding/json" + "strconv" "strings" "testing" "time" @@ -71,7 +72,7 @@ func TestPluginArgsJSON(t *testing.T) { want: `{ "initOptions": { "ctxDeadline": "2025-04-01T02:47:00Z", - "protocolVersion": "1", + "protocolVersion": "v1", "keyResourceID": "testkms://testkey", "hashFunc": 17, "rpcOptions": { @@ -96,7 +97,7 @@ func TestPluginArgsJSON(t *testing.T) { want: `{ "initOptions": { "ctxDeadline": "2025-04-01T02:47:00Z", - "protocolVersion": "1", + "protocolVersion": "v1", "keyResourceID": "testkms://testkey", "hashFunc": 17, "rpcOptions": { @@ -124,7 +125,7 @@ func TestPluginArgsJSON(t *testing.T) { want: `{ "initOptions": { "ctxDeadline": "2025-04-01T02:47:00Z", - "protocolVersion": "1", + "protocolVersion": "v1", "keyResourceID": "testkms://testkey", "hashFunc": 17, "rpcOptions": { @@ -160,7 +161,7 @@ func TestPluginArgsJSON(t *testing.T) { want: `{ "initOptions": { "ctxDeadline": "2025-04-01T02:47:00Z", - "protocolVersion": "1", + "protocolVersion": "v1", "keyResourceID": "testkms://testkey", "hashFunc": 17, "rpcOptions": { @@ -205,7 +206,7 @@ func TestPluginArgsJSON(t *testing.T) { want: `{ "initOptions": { "ctxDeadline": "2025-04-01T02:47:00Z", - "protocolVersion": "1", + "protocolVersion": "v1", "keyResourceID": "testkms://testkey", "hashFunc": 17, "rpcOptions": { @@ -255,7 +256,7 @@ func TestPluginArgsJSON(t *testing.T) { want: `{ "initOptions": { "ctxDeadline": "2025-04-01T02:47:00Z", - "protocolVersion": "1", + "protocolVersion": "v1", "keyResourceID": "testkms://testkey", "hashFunc": 17, "rpcOptions": { @@ -416,3 +417,107 @@ func TestPluginRespJSON(t *testing.T) { }) } } + +// TestHashFuncJSON ensures that some of our well-known hashfuncs consistently encode to known int values. +// just in case the order of thoe iota changes in the future. If this test fails, then we likely need a new protocol version. +// See iota values at https://pkg.go.dev/crypto@go1.23.5#Hash. +func TestHashFuncJSON(t *testing.T) { + t.Parallel() + + tests := []struct { + hash crypto.Hash + wantInt int + }{ + // all know crypto.HASH + { + crypto.MD4, + 1, + }, + { + crypto.MD5, + 2, + }, + { + crypto.SHA1, + 3, + }, + { + crypto.SHA224, + 4, + }, + { + crypto.SHA256, + 5, + }, + { + crypto.SHA384, + 6, + }, + { + crypto.SHA512, + 7, + }, + { + crypto.MD5SHA1, + 8, + }, + { + crypto.RIPEMD160, + 9, + }, + { + crypto.SHA3_224, + 10, + }, + { + crypto.SHA3_256, + 11, + }, + { + crypto.SHA3_384, + 12, + }, + { + crypto.SHA3_512, + 13, + }, + { + crypto.SHA512_224, + 14, + }, + { + crypto.SHA512_256, + 15, + }, + { + crypto.BLAKE2s_256, + 16, + }, + { + crypto.BLAKE2b_256, + 17, + }, + { + crypto.BLAKE2b_384, + 18, + }, + { + crypto.BLAKE2b_512, + 19, + }, + } + for _, tc := range tests { + t.Run(tc.hash.String(), func(t *testing.T) { + t.Parallel() + + got, err := json.Marshal(tc.hash) + if err != nil { + t.Errorf("marshaling hash %s: %v", tc.hash.String(), err) + } + want := []byte(strconv.Itoa(tc.wantInt)) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("unexpected JSON (-want +got): \n%s", diff) + } + }) + } +} diff --git a/test/cliplugin/localkms/main.go b/test/cliplugin/localkms/main.go index fa0a5b8e9..5f23166f1 100644 --- a/test/cliplugin/localkms/main.go +++ b/test/cliplugin/localkms/main.go @@ -23,7 +23,8 @@ import ( "github.com/sigstore/sigstore/pkg/signature/kms/cliplugin/handler" ) -const expectedProtocolVersion = "1" +// See cliplugin.common.ProtocolVersion +const expectedProtocolVersion = "v1" func main() { // we log to stderr, not stdout. stdout is reserved for the plugin return value.