Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APIs for Statement v1 and SLSA Provenance v1 protos #268

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ func run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("no command arguments passed, please specify or use --no-command option")
}

metadata, err := intoto.InTotoRun(stepName, runDir, materialsPaths, productsPaths, args, key, []string{"sha256"}, exclude, lStripPaths, lineNormalization, followSymlinkDirs, useDSSE)
// FIXME: The `attest` parameter is always false until full Attestation Framework support is added
metadata, err := intoto.InTotoRun(stepName, runDir, materialsPaths, productsPaths, args, key, []string{"sha256"}, exclude, lStripPaths, lineNormalization, followSymlinkDirs, useDSSE, false)
if err != nil {
return fmt.Errorf("failed to create link metadata: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ go 1.20

require (
github.com/google/go-cmp v0.6.0
github.com/in-toto/attestation v0.1.1-0.20230828220013-11b7a1a4ca51
github.com/in-toto/attestation v1.0.1
github.com/secure-systems-lab/go-securesystemslib v0.7.0
github.com/shibumi/go-pathspec v1.3.0
github.com/spf13/cobra v1.8.0
github.com/spiffe/go-spiffe/v2 v2.1.6
github.com/stretchr/testify v1.8.4
golang.org/x/sys v0.15.0
google.golang.org/grpc v1.60.1
google.golang.org/protobuf v1.31.0
)

require (
Expand All @@ -32,6 +33,5 @@ require (
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/in-toto/attestation v0.1.1-0.20230828220013-11b7a1a4ca51 h1:79cutIt/QsUDEWEPKUdC9OiI0C9fYxRuU1VvYTGYTuo=
github.com/in-toto/attestation v0.1.1-0.20230828220013-11b7a1a4ca51/go.mod h1:hCR5COCuENh5+VfojEkJnt7caOymbEgvyZdKifD6pOw=
github.com/in-toto/attestation v1.0.1 h1:DgX1XuBkryTpj1Piq8AiMK3CMfEcec3Qv6+Ku+uI3WY=
github.com/in-toto/attestation v1.0.1/go.mod h1:hCR5COCuENh5+VfojEkJnt7caOymbEgvyZdKifD6pOw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
Expand Down
29 changes: 29 additions & 0 deletions in_toto/attestation/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package common

import (
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)

/*
PredicatePbToStruct converts a given protobuf representation of
an in-toto attestation Predicate into a generic Struct struct
needed to construct an in-toto Attestation Framework v1
compliant Statement. If there are any marshalling problems,
this function returns an error.
*/
func PredicatePbToStruct(predicatePb proto.Message) (*structpb.Struct, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if want this to be internal/, I don't think we want to accidentally have people use this. Or it could be a private function in attestation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I had set it public since it's called from within runlib, but either moving this to internal/ or even as a private function to be called only within GenerateValidStatement() could work. We would have to change the signature of GenerateValidStatement() to take a protobuf message instead of Struct in the latter case, not sure how I feel about that.

predJson, err := protojson.Marshal(predicatePb)
if err != nil {
return nil, err
}

predStruct := &structpb.Struct{}
err = protojson.Unmarshal(predJson, predStruct)
if err != nil {
return nil, err
}

return predStruct, nil
}
98 changes: 98 additions & 0 deletions in_toto/attestation/predicates/provenance/v1/provenance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package v1

import (
"fmt"
"time"

prov1 "github.com/in-toto/attestation/go/predicates/provenance/v1"
ita1 "github.com/in-toto/attestation/go/v1"

"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
)

const (
// PredicateSLSAProvenance represents a build provenance for an artifact.
SLSAProvenancePredicateType = "https://slsa.dev/provenance/v1"
)

/*
GenBuildDefinition is a helper function to construct
a SLSA v1 BuildDefinition struct.
Validation is handled in GenProvenance().
*/
func GenBuildDefinition(buildType string, externalParams *structpb.Struct, internalParams *structpb.Struct, resolvedDependencies []*ita1.ResourceDescriptor) *prov1.BuildDefinition {
buildDef := &prov1.BuildDefinition{
BuildType: buildType,
ExternalParameters: externalParams,
InternalParameters: internalParams,
ResolvedDependencies: resolvedDependencies,
}

return buildDef
}

/*
GenRunDetails is a helper function to construct
a SLSA v1 RunDetails struct.
Validation is handled in GenProvenance().
*/
func GenRunDetails(builder *prov1.Builder, metadata *prov1.BuildMetadata, byproducts []*ita1.ResourceDescriptor) *prov1.RunDetails {
runDetails := &prov1.RunDetails{
Builder: builder,
Metadata: metadata,
Byproducts: byproducts,
}

return runDetails
}

/*
GenBuilder is a helper function to construct a
SLSA v1 Builder struct.
Validation is handled in GenProvenance().
*/
func GenBuilder(id string, version map[string]string, builderDependencies []*ita1.ResourceDescriptor) *prov1.Builder {
builder := &prov1.Builder{
Id: id,
Version: version,
BuilderDependencies: builderDependencies,
}

return builder
}

/*
GenBuildMetadata is a helper function to construct a
SLSA v1 BuildMetadata struct.
Because none of the fields of the object are required, this
constructor does not validate the contents of the struct
and always succeeds.
*/
func GenBuildMetadata(invocationID string, startedOn time.Time, finishedOn time.Time) *prov1.BuildMetadata {
buildMetadata := &prov1.BuildMetadata{
InvocationId: invocationID,
StartedOn: timestamppb.New(startedOn),
FinishedOn: timestamppb.New(finishedOn),
}

return buildMetadata
}

/*
GenerateValidPredicate constructs a SLSA v1 Provenance struct.
If the created object does not represent a valid Provenance, this
function returns an error.
*/
func GenerateValidPredicate(buildDefinition *prov1.BuildDefinition, runDetails *prov1.RunDetails) (*prov1.Provenance, error) {
provenance := &prov1.Provenance{
BuildDefinition: buildDefinition,
RunDetails: runDetails,
}

if err := provenance.Validate(); err != nil {
return nil, fmt.Errorf("Invalid Provenance format: %w", err)
marcelamelara marked this conversation as resolved.
Show resolved Hide resolved
}

return provenance, nil
}
53 changes: 53 additions & 0 deletions in_toto/attestation/v1/resource_descriptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package v1

import (
"fmt"

ita1 "github.com/in-toto/attestation/go/v1"
"google.golang.org/protobuf/types/known/structpb"
)

/*
GenerateValidResourceDescriptor constructs an in-toto Attestation Framework v1
ResourceDescriptor struct. If the created object does not represent a
valid ResourceDescriptor, this function returns an error.
*/
func GenerateValidResourceDescriptor(name string, uri string, digestSet map[string]string, content []byte, downloadLocation string, mediaType string, annotations *structpb.Struct) (*ita1.ResourceDescriptor, error) {
rd := &ita1.ResourceDescriptor{
Name: name,
Uri: uri,
Digest: digestSet,
Content: content,
DownloadLocation: downloadLocation,
MediaType: mediaType,
Annotations: annotations,
}

err := rd.Validate()
if err != nil {
return nil, fmt.Errorf("Invalid resource descriptor: %w", err)
marcelamelara marked this conversation as resolved.
Show resolved Hide resolved
}

return rd, nil
}

/*
RDListFromRecord converts a map of artifacts as collected
by RecordArtifacts() in runlib.go and converts it into a list
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be expanded with the structure of the map? We may retire RecordArtifacts at some point, it'll be helpful to track this here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be 100% sure I've understood what you're asking, you're suggesting we include map[string]map[string][string] in this comment?

of ITE-6 ResourceDescriptors to be used in v1 Statements.
*/
func RDListFromRecord(evalArtifacts map[string]map[string]string) ([]*ita1.ResourceDescriptor, error) {
var rds []*ita1.ResourceDescriptor
for name, digestSet := range evalArtifacts {

rd, err := GenerateValidResourceDescriptor(name, "", digestSet, nil, "", "", nil)

if err != nil {
return nil, err
}

rds = append(rds, rd)
}

return rds, nil
}
29 changes: 29 additions & 0 deletions in_toto/attestation/v1/statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package v1

import (
"fmt"

ita1 "github.com/in-toto/attestation/go/v1"
"google.golang.org/protobuf/types/known/structpb"
)

/*
GenerateValidStatement constructs an in-toto Attestation Framework v1
Statement struct. If the created object does not represent a
compliant Statement, this function returns an error.
*/
func GenerateValidStatement(subject []*ita1.ResourceDescriptor, predicateType string, predicate *structpb.Struct) (*ita1.Statement, error) {
st := &ita1.Statement{
Type: ita1.StatementTypeUri,
Subject: subject,
PredicateType: predicateType,
Predicate: predicate,
}

err := st.Validate()
if err != nil {
return nil, fmt.Errorf("Invalid in-toto Statement: %w", err)
marcelamelara marked this conversation as resolved.
Show resolved Hide resolved
}

return st, nil
}
4 changes: 4 additions & 0 deletions in_toto/attestations.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const (

// StatementInTotoV1 is the type URI for ITE-6 v1 Statements.
// This is constant for all predicate types.
//
// Deprecated: This constant exists for historical compatibility and should
// not be used. This constant has been superseded by the constant provided
// in https://github.com/in-toto/attestation/tree/main/go/v1/statement.go
StatementInTotoV1 = ita1.StatementTypeUri

// PredicateSPDX represents a SBOM using the SPDX standard.
Expand Down
35 changes: 25 additions & 10 deletions in_toto/runlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ and materials at the passed materialPaths. The returned link is wrapped in a
Metablock object. If command execution or artifact recording fails the first
return value is an empty Metablock and the second return value is the error.
*/
func InTotoRun(name string, runDir string, materialPaths []string, productPaths []string, cmdArgs []string, key Key, hashAlgorithms []string, gitignorePatterns []string, lStripPaths []string, lineNormalization bool, followSymlinkDirs bool, useDSSE bool) (Metadata, error) {
func InTotoRun(name string, runDir string, materialPaths []string, productPaths []string, cmdArgs []string, key Key, hashAlgorithms []string, gitignorePatterns []string, lStripPaths []string, lineNormalization bool, followSymlinkDirs bool, useDSSE bool, attest bool) (Metadata, error) {
materials, err := RecordArtifacts(materialPaths, hashAlgorithms, gitignorePatterns, lStripPaths, lineNormalization, followSymlinkDirs)
if err != nil {
return nil, err
Expand All @@ -339,22 +339,37 @@ func InTotoRun(name string, runDir string, materialPaths []string, productPaths
return nil, err
}

link := Link{
Type: "link",
Name: name,
Materials: materials,
Products: products,
ByProducts: byProducts,
Command: cmdArgs,
Environment: map[string]interface{}{},
// FIXME: replace when Attestation Framework support is fully implemented
var link Link
if attest {
// FIXME

/* Pseudocode:

provenance = provV1.GenerateValidPredicate()
provStruct = common.PredicatePbToStruct(provenance)

subject = v1.RDListFromRecord(products)
statement = v1.GenerateValidStatement(subject, "https://slsa.dev/provenance/v1", provStruct)
*/
return nil, errors.New("in-toto attestation support not implemented yet")
} else {
link = Link{
Type: "link",
Name: name,
Materials: materials,
Products: products,
ByProducts: byProducts,
Command: cmdArgs,
Environment: map[string]interface{}{},
}
}

if useDSSE {
marcelamelara marked this conversation as resolved.
Show resolved Hide resolved
env := &Envelope{}
if err := env.SetPayload(link); err != nil {
return nil, err
}

if !reflect.ValueOf(key).IsZero() {
if err := env.Sign(key); err != nil {
return nil, err
Expand Down
6 changes: 4 additions & 2 deletions in_toto/runlib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,8 @@ func TestInTotoRun(t *testing.T) {
}

for _, table := range tablesCorrect {
result, err := InTotoRun(linkName, "", table.materialPaths, table.productPaths, table.cmdArgs, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, table.useDSSE)
// FIXME: The `attest` parameter is always false until full Attestation Framework support is added
result, err := InTotoRun(linkName, "", table.materialPaths, table.productPaths, table.cmdArgs, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, table.useDSSE, false)
if table.useDSSE {
assert.Equal(t, table.result.(*Envelope).envelope, result.(*Envelope).envelope, fmt.Sprintf("InTotoRun returned '(%s, %s)', expected '(%s, nil)'", result, err, table.result))
} else {
Expand Down Expand Up @@ -526,7 +527,8 @@ func TestInTotoRun(t *testing.T) {
}

for _, table := range tablesInvalid {
result, err := InTotoRun(linkName, "", table.materialPaths, table.productPaths, table.cmdArgs, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, false)
// FIXME: The `attest` parameter is always false until full Attestation Framework support is added
result, err := InTotoRun(linkName, "", table.materialPaths, table.productPaths, table.cmdArgs, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, false, false)
if err == nil {
t.Errorf("InTotoRun returned '(%s, %s)', expected error",
result, err)
Expand Down
3 changes: 2 additions & 1 deletion in_toto/verifylib.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ func RunInspections(layout Layout, runDir string, lineNormalization bool, useDSS
paths = []string{runDir}
}

// FIXME: The `attest` parameter is always false until full Attestation Framework support is added
linkEnv, err := InTotoRun(inspection.Name, runDir, paths, paths,
inspection.Run, Key{}, []string{"sha256"}, nil, nil, lineNormalization, false, useDSSE)
inspection.Run, Key{}, []string{"sha256"}, nil, nil, lineNormalization, false, useDSSE, false)

if err != nil {
return nil, err
Expand Down