diff --git a/pkg/generated/models/hashedrekord_v001_schema.go b/pkg/generated/models/hashedrekord_v001_schema.go index f8bf233ed..3b906ae29 100644 --- a/pkg/generated/models/hashedrekord_v001_schema.go +++ b/pkg/generated/models/hashedrekord_v001_schema.go @@ -277,10 +277,10 @@ type HashedrekordV001SchemaDataHash struct { // The hashing function used to compute the hash value // Required: true - // Enum: [sha256] + // Enum: [sha256 sha384 sha512] Algorithm *string `json:"algorithm"` - // The hash value for the content + // The hash value for the content, as represented by a lower case hexadecimal string // Required: true Value *string `json:"value"` } @@ -307,7 +307,7 @@ var hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum []interface{} func init() { var res []string - if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["sha256","sha384","sha512"]`), &res); err != nil { panic(err) } for _, v := range res { @@ -319,6 +319,12 @@ const ( // HashedrekordV001SchemaDataHashAlgorithmSha256 captures enum value "sha256" HashedrekordV001SchemaDataHashAlgorithmSha256 string = "sha256" + + // HashedrekordV001SchemaDataHashAlgorithmSha384 captures enum value "sha384" + HashedrekordV001SchemaDataHashAlgorithmSha384 string = "sha384" + + // HashedrekordV001SchemaDataHashAlgorithmSha512 captures enum value "sha512" + HashedrekordV001SchemaDataHashAlgorithmSha512 string = "sha512" ) // prop value enum diff --git a/pkg/generated/restapi/embedded_spec.go b/pkg/generated/restapi/embedded_spec.go index 3e353768c..234f9122c 100644 --- a/pkg/generated/restapi/embedded_spec.go +++ b/pkg/generated/restapi/embedded_spec.go @@ -1659,11 +1659,13 @@ func init() { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ - "sha256" + "sha256", + "sha384", + "sha512" ] }, "value": { - "description": "The hash value for the content", + "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } } @@ -1682,11 +1684,13 @@ func init() { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ - "sha256" + "sha256", + "sha384", + "sha512" ] }, "value": { - "description": "The hash value for the content", + "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } } @@ -3261,11 +3265,13 @@ func init() { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ - "sha256" + "sha256", + "sha384", + "sha512" ] }, "value": { - "description": "The hash value for the content", + "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } } diff --git a/pkg/types/hashedrekord/v0.0.1/entry.go b/pkg/types/hashedrekord/v0.0.1/entry.go index 5f0cb138a..d94e22326 100644 --- a/pkg/types/hashedrekord/v0.0.1/entry.go +++ b/pkg/types/hashedrekord/v0.0.1/entry.go @@ -18,6 +18,7 @@ package hashedrekord import ( "bytes" "context" + "crypto" "crypto/ed25519" "crypto/sha256" "encoding/hex" @@ -178,11 +179,21 @@ func (v *V001Entry) validate() (pki.Signature, pki.PublicKey, error) { return nil, nil, types.ValidationError(errors.New("invalid value for hash")) } + var alg crypto.Hash + switch swag.StringValue(hash.Algorithm) { + case models.HashedrekordV001SchemaDataHashAlgorithmSha384: + alg = crypto.SHA384 + case models.HashedrekordV001SchemaDataHashAlgorithmSha512: + alg = crypto.SHA512 + default: + alg = crypto.SHA256 + } + decoded, err := hex.DecodeString(*hash.Value) if err != nil { return nil, nil, err } - if err := sigObj.Verify(nil, keyObj, options.WithDigest(decoded)); err != nil { + if err := sigObj.Verify(nil, keyObj, options.WithDigest(decoded), options.WithCryptoSignerOpts(alg)); err != nil { return nil, nil, types.ValidationError(fmt.Errorf("verifying signature: %w", err)) } diff --git a/pkg/types/hashedrekord/v0.0.1/entry_test.go b/pkg/types/hashedrekord/v0.0.1/entry_test.go index 343712dc8..efb096d5b 100644 --- a/pkg/types/hashedrekord/v0.0.1/entry_test.go +++ b/pkg/types/hashedrekord/v0.0.1/entry_test.go @@ -24,6 +24,7 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/sha256" + "crypto/sha512" "crypto/x509" "crypto/x509/pkix" "encoding/hex" @@ -57,6 +58,7 @@ func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry + expectedHashValue string expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectedVerifierSuccess bool @@ -90,11 +92,19 @@ func TestCrossFieldValidation(t *testing.T) { }) dataBytes := []byte("sign me!") - h := sha256.Sum256(dataBytes) - dataSHA := hex.EncodeToString(h[:]) - - signer, _ := signature.LoadSigner(key, crypto.SHA256) - sigBytes, _ := signer.SignMessage(bytes.NewReader(dataBytes)) + sha256Sum := sha256.Sum256(dataBytes) + sha384Sum := sha512.Sum384(dataBytes) + sha512Sum := sha512.Sum512(dataBytes) + dataSHA256 := hex.EncodeToString(sha256Sum[:]) + dataSHA384 := hex.EncodeToString(sha384Sum[:]) + dataSHA512 := hex.EncodeToString(sha512Sum[:]) + + sha256Signer, _ := signature.LoadSigner(key, crypto.SHA256) + sha256SigBytes, _ := sha256Signer.SignMessage(bytes.NewReader(dataBytes)) + sha384Signer, _ := signature.LoadSigner(key, crypto.SHA384) + sha384SigBytes, _ := sha384Signer.SignMessage(bytes.NewReader(dataBytes)) + sha512Signer, _ := signature.LoadSigner(key, crypto.SHA512) + sha512SigBytes, _ := sha512Signer.SignMessage(bytes.NewReader(dataBytes)) incorrectLengthHash := sha256.Sum224(dataBytes) incorrectLengthSHA := hex.EncodeToString(incorrectLengthHash[:]) @@ -124,10 +134,11 @@ func TestCrossFieldValidation(t *testing.T) { entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, }, }, }, + expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, @@ -136,11 +147,12 @@ func TestCrossFieldValidation(t *testing.T) { entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{}, }, }, }, + expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, @@ -149,13 +161,14 @@ func TestCrossFieldValidation(t *testing.T) { entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: invalidKeyBytes, }, }, }, }, + expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, // successful even if unmarshalling fails, because the ed25519 key is valid expectedVerifierSuccess: true, @@ -165,13 +178,14 @@ func TestCrossFieldValidation(t *testing.T) { entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, }, }, + expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, @@ -180,7 +194,7 @@ func TestCrossFieldValidation(t *testing.T) { entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, @@ -188,27 +202,75 @@ func TestCrossFieldValidation(t *testing.T) { Data: &models.HashedrekordV001SchemaData{}, }, }, + expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { - caseDesc: "signature with hash", + caseDesc: "signature with sha256 hash", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ - Value: swag.String(dataSHA), + Value: swag.String(dataSHA256), Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), }, }, }, }, + expectedHashValue: "sha256:" + dataSHA256, + expectUnmarshalSuccess: true, + expectCanonicalizeSuccess: true, + expectedVerifierSuccess: true, + }, + { + caseDesc: "signature with sha384 hash", + entry: V001Entry{ + HashedRekordObj: models.HashedrekordV001Schema{ + Signature: &models.HashedrekordV001SchemaSignature{ + Content: sha384SigBytes, + PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ + Content: keyBytes, + }, + }, + Data: &models.HashedrekordV001SchemaData{ + Hash: &models.HashedrekordV001SchemaDataHash{ + Value: swag.String(dataSHA384), + Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha384), + }, + }, + }, + }, + expectedHashValue: "sha384:" + dataSHA384, + expectUnmarshalSuccess: true, + expectCanonicalizeSuccess: true, + expectedVerifierSuccess: true, + }, + { + caseDesc: "signature with sha512 hash", + entry: V001Entry{ + HashedRekordObj: models.HashedrekordV001Schema{ + Signature: &models.HashedrekordV001SchemaSignature{ + Content: sha512SigBytes, + PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ + Content: keyBytes, + }, + }, + Data: &models.HashedrekordV001SchemaData{ + Hash: &models.HashedrekordV001SchemaDataHash{ + Value: swag.String(dataSHA512), + Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha512), + }, + }, + }, + }, + expectedHashValue: "sha512:" + dataSHA512, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, @@ -218,7 +280,7 @@ func TestCrossFieldValidation(t *testing.T) { entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, @@ -231,6 +293,7 @@ func TestCrossFieldValidation(t *testing.T) { }, }, }, + expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, @@ -240,7 +303,7 @@ func TestCrossFieldValidation(t *testing.T) { entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ - Content: sigBytes, + Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, @@ -253,6 +316,30 @@ func TestCrossFieldValidation(t *testing.T) { }, }, }, + expectedHashValue: "sha256:" + dataSHA256, + expectUnmarshalSuccess: false, + expectCanonicalizeSuccess: false, + expectedVerifierSuccess: true, + }, + { + caseDesc: "signature with mismatched hash & invalid signature", + entry: V001Entry{ + HashedRekordObj: models.HashedrekordV001Schema{ + Signature: &models.HashedrekordV001SchemaSignature{ + Content: sha512SigBytes, + PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ + Content: keyBytes, + }, + }, + Data: &models.HashedrekordV001SchemaData{ + Hash: &models.HashedrekordV001SchemaDataHash{ + Value: swag.String(dataSHA256), + Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), + }, + }, + }, + }, + expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, @@ -293,7 +380,7 @@ func TestCrossFieldValidation(t *testing.T) { hash, err := v.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) - } else if hash != "sha256:"+dataSHA { + } else if hash != tc.expectedHashValue { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } @@ -323,7 +410,7 @@ func TestCrossFieldValidation(t *testing.T) { hash, err := ei.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) - } else if hash != "sha256:"+dataSHA { + } else if hash != tc.expectedHashValue { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } diff --git a/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json b/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json index 8752ae60f..576071ed8 100644 --- a/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json +++ b/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json @@ -38,10 +38,10 @@ "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", - "enum": [ "sha256" ] + "enum": [ "sha256", "sha384", "sha512" ] }, "value": { - "description": "The hash value for the content", + "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } },