Skip to content
Snippets Groups Projects
Unverified Commit aa5d132c authored by Bob Callaway's avatar Bob Callaway Committed by GitHub
Browse files

Add the SHA256 digest of the intoto payload into the rekor entry (#764)


* include hash of attestation in rekor entry

Signed-off-by: default avatarBob Callaway <bcallaway@google.com>

* compute sha off of decoded attestation

Signed-off-by: default avatarBob Callaway <bcallaway@google.com>

* change name to reflect DSSE terminology

Signed-off-by: default avatarBob Callaway <bcallaway@google.com>
parent 91258e65
No related branches found
No related tags found
No related merge requests found
......@@ -221,6 +221,7 @@ func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middl
log.RequestIDLogger(params.HTTPRequest).Infof("no attestation for %s", uuid)
return
}
// TODO stop using uuid and use attestation hash
if err := storeAttestation(context.Background(), uuid, attestation); err != nil {
log.RequestIDLogger(params.HTTPRequest).Errorf("error storing attestation: %s", err)
}
......
......@@ -153,6 +153,9 @@ type IntotoV001SchemaContent struct {
// hash
Hash *IntotoV001SchemaContentHash `json:"hash,omitempty"`
// payload hash
PayloadHash *IntotoV001SchemaContentPayloadHash `json:"payloadHash,omitempty"`
}
// Validate validates this intoto v001 schema content
......@@ -163,6 +166,10 @@ func (m *IntotoV001SchemaContent) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validatePayloadHash(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
......@@ -188,6 +195,25 @@ func (m *IntotoV001SchemaContent) validateHash(formats strfmt.Registry) error {
return nil
}
func (m *IntotoV001SchemaContent) validatePayloadHash(formats strfmt.Registry) error {
if swag.IsZero(m.PayloadHash) { // not required
return nil
}
if m.PayloadHash != nil {
if err := m.PayloadHash.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "payloadHash")
}
return err
}
}
return nil
}
// ContextValidate validate this intoto v001 schema content based on the context it is used
func (m *IntotoV001SchemaContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
......@@ -196,6 +222,10 @@ func (m *IntotoV001SchemaContent) ContextValidate(ctx context.Context, formats s
res = append(res, err)
}
if err := m.contextValidatePayloadHash(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
......@@ -218,6 +248,22 @@ func (m *IntotoV001SchemaContent) contextValidateHash(ctx context.Context, forma
return nil
}
func (m *IntotoV001SchemaContent) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error {
if m.PayloadHash != nil {
if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("content" + "." + "payloadHash")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("content" + "." + "payloadHash")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV001SchemaContent) MarshalBinary() ([]byte, error) {
if m == nil {
......@@ -345,3 +391,113 @@ func (m *IntotoV001SchemaContentHash) UnmarshalBinary(b []byte) error {
*m = res
return nil
}
// IntotoV001SchemaContentPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope
//
// swagger:model IntotoV001SchemaContentPayloadHash
type IntotoV001SchemaContentPayloadHash struct {
// The hashing function used to compute the hash value
// Required: true
// Enum: [sha256]
Algorithm *string `json:"algorithm"`
// The hash value for the envelope's payload
// Required: true
Value *string `json:"value"`
}
// Validate validates this intoto v001 schema content payload hash
func (m *IntotoV001SchemaContentPayloadHash) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAlgorithm(formats); err != nil {
res = append(res, err)
}
if err := m.validateValue(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum = append(intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum, v)
}
}
const (
// IntotoV001SchemaContentPayloadHashAlgorithmSha256 captures enum value "sha256"
IntotoV001SchemaContentPayloadHashAlgorithmSha256 string = "sha256"
)
// prop value enum
func (m *IntotoV001SchemaContentPayloadHash) validateAlgorithmEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum, true); err != nil {
return err
}
return nil
}
func (m *IntotoV001SchemaContentPayloadHash) validateAlgorithm(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil {
return err
}
// value enum
if err := m.validateAlgorithmEnum("content"+"."+"payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil {
return err
}
return nil
}
func (m *IntotoV001SchemaContentPayloadHash) validateValue(formats strfmt.Registry) error {
if err := validate.Required("content"+"."+"payloadHash"+"."+"value", "body", m.Value); err != nil {
return err
}
return nil
}
// ContextValidate validate this intoto v001 schema content payload hash based on the context it is used
func (m *IntotoV001SchemaContentPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// MarshalBinary interface implementation
func (m *IntotoV001SchemaContentPayloadHash) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IntotoV001SchemaContentPayloadHash) UnmarshalBinary(b []byte) error {
var res IntotoV001SchemaContentPayloadHash
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
......@@ -23,7 +23,6 @@ import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
......@@ -35,6 +34,8 @@ import (
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/pkg/errors"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/pki"
......@@ -150,6 +151,18 @@ func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) {
},
},
}
attestation := v.Attestation()
if attestation != nil {
decodedAttestation, err := base64.StdEncoding.DecodeString(string(attestation))
if err != nil {
return nil, errors.Wrap(err, "decoding attestation")
}
attH := sha256.Sum256(decodedAttestation)
canonicalEntry.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{
Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256),
Value: swag.String(hex.EncodeToString(attH[:])),
}
}
itObj := models.Intoto{}
itObj.APIVersion = swag.String(APIVERSION)
......@@ -194,8 +207,9 @@ func (v *V001Entry) validate() error {
}
func (v *V001Entry) Attestation() []byte {
if len(v.env.Payload) > viper.GetInt("max_attestation_size") {
log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", len(v.env.Payload), viper.GetInt("max_attestation_size"))
storageSize := base64.StdEncoding.DecodedLen(len(v.env.Payload))
if storageSize > viper.GetInt("max_attestation_size") {
log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", storageSize, viper.GetInt("max_attestation_size"))
return nil
}
return []byte(v.env.Payload)
......
......@@ -34,6 +34,26 @@
"value"
],
"readOnly": true
},
"payloadHash": {
"description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope",
"type": "object",
"properties": {
"algorithm": {
"description": "The hashing function used to compute the hash value",
"type": "string",
"enum": [ "sha256" ]
},
"value": {
"description": "The hash value for the envelope's payload",
"type": "string"
}
},
"required": [
"algorithm",
"value"
],
"readOnly": true
}
}
},
......
......@@ -56,6 +56,7 @@ import (
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/sharding"
"github.com/sigstore/rekor/pkg/signer"
"github.com/sigstore/rekor/pkg/types"
rekord "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1"
"github.com/sigstore/rekor/pkg/util"
"github.com/sigstore/sigstore/pkg/cryptoutils"
......@@ -449,7 +450,7 @@ func TestIntoto(t *testing.T) {
if err := json.Unmarshal([]byte(out), &g); err != nil {
t.Fatal(err)
}
// The atteestation should be stored at /var/run/attestations/$uuid
// The attestation should be stored at /var/run/attestations/$uuid
got := in_toto.ProvenanceStatement{}
if err := json.Unmarshal(g.Attestation, &got); err != nil {
......@@ -459,6 +460,25 @@ func TestIntoto(t *testing.T) {
t.Errorf("diff: %s", diff)
}
attHash := sha256.Sum256(g.Attestation)
intotoModel := &models.IntotoV001Schema{}
if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoModel); err != nil {
t.Errorf("could not convert body into intoto type: %v", err)
}
if intotoModel.Content == nil || intotoModel.Content.PayloadHash == nil {
t.Errorf("could not find hash over attestation %v", intotoModel)
}
recordedPayloadHash, err := hex.DecodeString(*intotoModel.Content.PayloadHash.Value)
if err != nil {
t.Errorf("error converting attestation hash to []byte: %v", err)
}
if !bytes.Equal(attHash[:], recordedPayloadHash) {
t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]),
*intotoModel.Content.PayloadHash.Value))
}
out = runCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath)
outputContains(t, out, "Entry already exists")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment