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

Refactor SCAI generator APIs into pkg/ #38

Merged
merged 2 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ $(VENVDIR):
py-venv: $(VENVDIR) $(PYTHON_DIR)

go-mod:
cd ./go && go build && go install
cd ./scai-gen && go build && go install

clean:
@echo REMOVE SCAI VENV AND PYTHON LIB DIRS
Expand Down
6 changes: 3 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ For information on how to use our CLI tools in [Python] or [Go] environments,
please refer to their instructions.

[in-toto Statement]: https://github.com/in-toto/attestation/blob/main/spec/v1/statement.md
[Resource Descriptors]: https://github.com/in-toto/attestation/blob/main/spec/v1/resource_descriptor.md
[Attribute Assertions]: https://github.com/in-toto/attestation/blob/main/protos/in_toto_attestation/predicates/scai/v0/scai.proto#L16
[ResourceDescriptors]: https://github.com/in-toto/attestation/blob/main/spec/v1/resource_descriptor.md
[AttributeAssertions]: https://github.com/in-toto/attestation/blob/main/protos/in_toto_attestation/predicates/scai/v0/scai.proto#L16
[Report]: https://github.com/in-toto/attestation/blob/main/protos/in_toto_attestation/predicates/scai/v0/scai.proto#L28
[SCAI specification]: https://github.com/in-toto/attestation/blob/main/spec/predicates/scai.md
[Go]: ../go/README.md
[Go]: ../scai-gen/README.md
[Python]: ../python/README.md
15 changes: 4 additions & 11 deletions scai-gen/cmd/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package cmd
import (
"fmt"

"github.com/in-toto/scai-demos/scai-gen/fileio"
"github.com/in-toto/scai-demos/scai-gen/pkg/fileio"
"github.com/in-toto/scai-demos/scai-gen/pkg/generators"

scai "github.com/in-toto/attestation/go/predicates/scai/v0"
ita "github.com/in-toto/attestation/go/v1"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/structpb"
Expand Down Expand Up @@ -94,16 +94,9 @@ func genAttrAssertion(_ *cobra.Command, args []string) error {
}
}

aa := &scai.AttributeAssertion{
Attribute: attribute,
Target: target,
Conditions: conditions,
Evidence: evidence,
}

err := aa.Validate()
aa, err := generators.NewSCAIAssertion(attribute, target, conditions, evidence)
if err != nil {
return fmt.Errorf("invalid SCAI attribute assertion: %w", err)
return fmt.Errorf("unable to generate SCAI attribute assertion: %w", err)
}

return fileio.WritePbToFile(aa, outFile, false)
Expand Down
4 changes: 2 additions & 2 deletions scai-gen/cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"path/filepath"
"strings"

"github.com/in-toto/scai-demos/scai-gen/fileio"
"github.com/in-toto/scai-demos/scai-gen/policy"
"github.com/in-toto/scai-demos/scai-gen/pkg/fileio"
"github.com/in-toto/scai-demos/scai-gen/pkg/policy"

"github.com/in-toto/attestation-verifier/verifier"
scai "github.com/in-toto/attestation/go/predicates/scai/v0"
Expand Down
67 changes: 8 additions & 59 deletions scai-gen/cmd/rd.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package cmd

import (
"encoding/hex"
"fmt"
"os"
"strings"

"github.com/in-toto/scai-demos/scai-gen/fileio"
"github.com/in-toto/scai-demos/scai-gen/policy"
"github.com/in-toto/scai-demos/scai-gen/pkg/fileio"
"github.com/in-toto/scai-demos/scai-gen/pkg/generators"

ita "github.com/in-toto/attestation/go/v1"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/structpb"
)
Expand Down Expand Up @@ -163,41 +159,15 @@ func genRdFromFile(_ *cobra.Command, args []string) error {
}

filename := args[0]
fileBytes, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("error reading resource file: %w", err)
}

var content []byte
if withContent {
content = fileBytes
}

sha256Digest := hex.EncodeToString(policy.GenSHA256(fileBytes))

rdName := filename
if len(name) > 0 {
rdName = name
}

annotations, err := readAnnotations(annotationsFile)
if err != nil {
return fmt.Errorf("error reading annotations file: %w", err)
}

rd := &ita.ResourceDescriptor{
Name: rdName,
Uri: uri,
Digest: map[string]string{"sha256": strings.ToLower(sha256Digest)},
Content: content,
DownloadLocation: downloadLocation,
MediaType: mediaType,
Annotations: annotations,
return fmt.Errorf("unable to read annotations file: %w", err)
}

err = rd.Validate()
rd, err := generators.NewRdForFile(filename, name, uri, hashAlg, withContent, mediaType, downloadLocation, annotations)
if err != nil {
return fmt.Errorf("invalid resource descriptor: %w", err)
return fmt.Errorf("unable to generate RD: %w", err)
}

return fileio.WritePbToFile(rd, outFile, false)
Expand All @@ -211,35 +181,14 @@ func genRdForRemote(_ *cobra.Command, args []string) error {

remoteURI := args[0]

digestSet := make(map[string]string)
if len(digest) > 0 {
// the in-toto spec expects a hex-encoded string in DigestSets
// https://github.com/in-toto/attestation/blob/main/spec/v1/digest_set.md
_, err := hex.DecodeString(digest)
if err != nil {
return fmt.Errorf("digest is not valid hex-encoded string: %w", err)
}

// we can assume that we have both variables set at this point
digestSet = map[string]string{hashAlg: strings.ToLower(digest)}
}

annotations, err := readAnnotations(annotationsFile)
if err != nil {
return fmt.Errorf("error reading annotations file: %w", err)
}

rd := &ita.ResourceDescriptor{
Name: name,
Uri: remoteURI,
Digest: digestSet,
DownloadLocation: downloadLocation,
Annotations: annotations,
return fmt.Errorf("unable to read annotations file: %w", err)
}

err = rd.Validate()
rd, err := generators.NewRdForRemote(remoteURI, name, hashAlg, digest, downloadLocation, annotations)
if err != nil {
return fmt.Errorf("invalid resource descriptor: %w", err)
return fmt.Errorf("unable to generate RD: %w", err)
}

return fileio.WritePbToFile(rd, outFile, false)
Expand Down
22 changes: 6 additions & 16 deletions scai-gen/cmd/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package cmd
import (
"fmt"

"github.com/in-toto/scai-demos/scai-gen/fileio"
"github.com/in-toto/scai-demos/scai-gen/pkg/fileio"
"github.com/in-toto/scai-demos/scai-gen/pkg/generators"

scai "github.com/in-toto/attestation/go/predicates/scai/v0"
ita "github.com/in-toto/attestation/go/v1"
Expand Down Expand Up @@ -87,14 +88,10 @@ func genAttrReport(_ *cobra.Command, args []string) error {
}

// first, generate the SCAI Report
ar := &scai.AttributeReport{
Attributes: attrAsserts,
Producer: producer,
}

err := ar.Validate()
ar, err := generators.NewSCAIReport(attrAsserts, producer)
if err != nil {
return fmt.Errorf("invalid SCAI attribute report: %w", err)
return fmt.Errorf("unable to generate SCAI Report: %w", err)
}

// then, plug the Report into an in-toto Statement
Expand All @@ -118,16 +115,9 @@ func genAttrReport(_ *cobra.Command, args []string) error {
return err
}

statement := &ita.Statement{
Type: ita.StatementTypeUri,
Subject: []*ita.ResourceDescriptor{subject},
PredicateType: "https://in-toto.io/attestation/scai/attribute-report/v0.2",
Predicate: reportStruct,
}

err = statement.Validate()
statement, err := generators.NewStatement([]*ita.ResourceDescriptor{subject}, "https://in-toto.io/attestation/scai/attribute-report/v0.2", reportStruct)
if err != nil {
return fmt.Errorf("invalid in-toto Statement: %w", err)
return fmt.Errorf("unable to generate in-toto Statement: %w", err)
}

return fileio.WritePbToFile(statement, outFile, prettyPrint)
Expand Down
2 changes: 1 addition & 1 deletion scai-gen/cmd/sigstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"context"
"fmt"

"github.com/in-toto/scai-demos/scai-gen/fileio"
"github.com/in-toto/scai-demos/scai-gen/pkg/fileio"

ita "github.com/in-toto/attestation/go/v1"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio"
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
43 changes: 43 additions & 0 deletions scai-gen/pkg/generators/scai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package generators

import (
"fmt"

scai "github.com/in-toto/attestation/go/predicates/scai/v0"
ita "github.com/in-toto/attestation/go/v1"
"google.golang.org/protobuf/types/known/structpb"
)

// Generates a SCAI v0 AttributeAssertion struct.
// Throws an error if the resulting AttributeAssertion does not meet the spec.
func NewSCAIAssertion(attribute string, target *ita.ResourceDescriptor, conditions *structpb.Struct, evidence *ita.ResourceDescriptor) (*scai.AttributeAssertion, error) {
aa := &scai.AttributeAssertion{
Attribute: attribute,
Target: target,
Conditions: conditions,
Evidence: evidence,
}

err := aa.Validate()
if err != nil {
return nil, fmt.Errorf("invalid SCAI attribute assertion: %w", err)
}

return aa, nil
}

// Generates a SCAI v0 AttributeReport struct to be used as an in-toto attestation predicate.
// Throws an error if the resulting AttributeReport does not meet the spec.
func NewSCAIReport(attrAssertions []*scai.AttributeAssertion, producer *ita.ResourceDescriptor) (*scai.AttributeReport, error) {
ar := &scai.AttributeReport{
Attributes: attrAssertions,
Producer: producer,
}

err := ar.Validate()
if err != nil {
return nil, fmt.Errorf("invalid SCAI attribute report: %w", err)
}

return ar, nil
}
101 changes: 101 additions & 0 deletions scai-gen/pkg/generators/v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package generators

import (
"encoding/hex"
"fmt"
"os"
"strings"

"github.com/in-toto/scai-demos/scai-gen/pkg/policy"

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

// Generates an in-toto Attestation Framework v1 ResourceDescriptor for a local file, including its digest (default sha256).
// Throws an error if the resulting ResourceDescriptor does not meet the spec.
func NewRdForFile(filename, name, uri, hashAlg string, withContent bool, mediaType, downloadLocation string, annotations *structpb.Struct) (*ita.ResourceDescriptor, error) {
fileBytes, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading resource file: %w", err)
}

var content []byte
if withContent {
content = fileBytes
}

var digest string
var alg string
if hashAlg == "sha256" || hashAlg == "" {
digest = hex.EncodeToString(policy.GenSHA256(fileBytes))
alg = "sha256"
} else {
return nil, fmt.Errorf("hash algorithm %s not supported", hashAlg)
}

rdName := filename
if len(name) > 0 {
rdName = name
}

rd := &ita.ResourceDescriptor{
Name: rdName,
Uri: uri,
Digest: map[string]string{alg: strings.ToLower(digest)},
Content: content,
DownloadLocation: downloadLocation,
MediaType: mediaType,
Annotations: annotations,
}

err = rd.Validate()
if err != nil {
return nil, fmt.Errorf("invalid resource descriptor: %w", err)
}

return rd, nil
}

// Generates an in-toto Attestation Framework v1 ResourceDescriptor for a remote resource identified by a name or URI).
// Does not check if the URI resolves to a valid remote location.
// Throws an error if the resulting ResourceDescriptor does not meet the spec.
func NewRdForRemote(name, uri, hashAlg, digest, downloadLocation string, annotations *structpb.Struct) (*ita.ResourceDescriptor, error) {
digestSet := make(map[string]string)
if len(hashAlg) > 0 && len(digest) > 0 {
digestSet = map[string]string{hashAlg: strings.ToLower(digest)}
}

rd := &ita.ResourceDescriptor{
Name: name,
Uri: uri,
Digest: digestSet,
DownloadLocation: downloadLocation,
Annotations: annotations,
}

err := rd.Validate()
if err != nil {
return nil, fmt.Errorf("invalid resource descriptor: %w", err)
}

return rd, nil
}

// Generates an in-toto Attestation Framework v1 Statement including a given predicate.
// Throws an error if the resulting Statement does not meet the spec.
func NewStatement(subjects []*ita.ResourceDescriptor, predicateType string, predicate *structpb.Struct) (*ita.Statement, error) {
statement := &ita.Statement{
Type: ita.StatementTypeUri,
Subject: subjects,
PredicateType: predicateType,
Predicate: predicate,
}

err := statement.Validate()
if err != nil {
return nil, fmt.Errorf("invalid in-toto Statement: %w", err)
}

return statement, nil
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading