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

feat: support oci image verification #147

Merged
merged 13 commits into from
Aug 17, 2022
71 changes: 51 additions & 20 deletions cli/slsa-verifier/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
serrors "github.com/slsa-framework/slsa-verifier/errors"
"github.com/slsa-framework/slsa-verifier/options"
"github.com/slsa-framework/slsa-verifier/verifiers"
"github.com/slsa-framework/slsa-verifier/verifiers/container"
)

type workflowInputs struct {
Expand All @@ -23,6 +24,7 @@ var (
provenancePath string
builderID string
artifactPath string
artifactImage string
source string
branch string
tag string
Expand Down Expand Up @@ -58,6 +60,7 @@ func main() {
}
flag.StringVar(&provenancePath, "provenance", "", "path to a provenance file")
flag.StringVar(&artifactPath, "artifact-path", "", "path to an artifact to verify")
flag.StringVar(&artifactImage, "artifact-image", "", "name of the OCI image to verify")
flag.StringVar(&source, "source", "",
"expected source repository that should have produced the binary, e.g. github.com/some/repo")
flag.StringVar(&branch, "branch", "", "[optional] expected branch the binary was compiled from")
Expand All @@ -71,7 +74,19 @@ func main() {
"[optional] a workflow input provided by a user at trigger time in the format 'key=value'. (Only for 'workflow_dispatch' events).")
flag.Parse()

if provenancePath == "" || artifactPath == "" || source == "" {
if (provenancePath == "" || artifactPath == "") && artifactImage == "" {
fmt.Fprintf(os.Stderr, "either 'provenance' and 'artifact-path' or 'artifact-image' must be specified\n")
flag.Usage()
os.Exit(1)
}

if artifactImage != "" && (provenancePath != "" || artifactPath != "") {
fmt.Fprintf(os.Stderr, "'provenance' and 'artifact-path' should not be specified when 'artifact-image' is provided\n")
asraa marked this conversation as resolved.
Show resolved Hide resolved
flag.Usage()
os.Exit(1)
}

asraa marked this conversation as resolved.
Show resolved Hide resolved
if source == "" {
flag.Usage()
os.Exit(1)
}
Expand All @@ -97,17 +112,18 @@ func main() {
os.Exit(1)
}

verifiedProvenance, _, err := runVerify(artifactPath, provenancePath, source,
verifiedProvenance, _, err := runVerify(artifactImage, artifactPath, provenancePath, source,
pbranch, pbuilderID, ptag, pversiontag, inputs.AsMap())
if err != nil {
fmt.Fprintf(os.Stderr, "FAILED: SLSA verification failed: %v\n", err)
os.Exit(2)
}

fmt.Fprintf(os.Stderr, "PASSED: Verified SLSA provenance\n")

if printProvenance {
fmt.Fprintf(os.Stdout, "%s\n", string(verifiedProvenance))
for _, verified := range verifiedProvenance {
asraa marked this conversation as resolved.
Show resolved Hide resolved
fmt.Fprintf(os.Stdout, "%s\n", string(verified))
}
}
}

Expand All @@ -121,26 +137,18 @@ func isFlagPassed(name string) bool {
return found
}

func runVerify(artifactPath, provenancePath, source string,

func runVerify(artifactImage, artifactPath, provenancePath, source string,
branch, builderID, ptag, pversiontag *string, inputs map[string]string,
) ([]byte, string, error) {
f, err := os.Open(artifactPath)
if err != nil {
return nil, "", err
}
defer f.Close()
ctx := context.Background()

provenance, err := os.ReadFile(provenancePath)
// Artifact hash retrieval depends on the artifact type.
artifactHash, err := getArtifactHash(artifactImage, artifactPath)
if err != nil {
return nil, "", err
}

h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return nil, "", err
}
artifactHash := hex.EncodeToString(h.Sum(nil))

provenanceOpts := &options.ProvenanceOpts{
ExpectedSourceURI: source,
ExpectedBranch: branch,
Expand All @@ -154,7 +162,30 @@ func runVerify(artifactPath, provenancePath, source string,
ExpectedID: builderID,
}

ctx := context.Background()
return verifiers.Verify(ctx, provenance,
artifactHash, provenanceOpts, builderOpts)
var provenance []byte
if provenancePath != "" {
provenance, err = os.ReadFile(provenancePath)
if err != nil {
return nil, "", err
}
}

return verifiers.Verify(ctx, artifactImage, provenance, artifactHash, provenanceOpts, builderOpts)
asraa marked this conversation as resolved.
Show resolved Hide resolved
}

func getArtifactHash(artifactImage, artifactPath string) (string, error) {
if artifactPath != "" {
f, err := os.Open(artifactPath)
if err != nil {
return "", err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}
// Retrieve image digest
return container.GetImageDigest(artifactImage)
}
Loading