From 0bca96770bb72adea48c06a615b58f9bc2c37f79 Mon Sep 17 00:00:00 2001 From: David Dansby <39511285+DataDavD@users.noreply.github.com> Date: Mon, 4 Dec 2023 19:11:19 -0800 Subject: [PATCH] feat: add algo hash list for digest calc in config (#292) Introduce cobra/config flag that allows for slice of string's indicating hash algorithms to be used for digest calculation. Uses go-witness.attestation.WithHashes to add the slice to attestation run options. Will error if can't parse hash from its string value. Add test for testing acceptable/unacceptable hash algorithms accepted. Refs: #73 --- cmd/run.go | 12 +++++++++- cmd/run_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++ docs/witness_run.md | 1 + options/run.go | 2 ++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/cmd/run.go b/cmd/run.go index 98ccb8ac..8918d520 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -16,6 +16,7 @@ package cmd import ( "context" + "crypto" "encoding/json" "fmt" @@ -102,12 +103,21 @@ func runRun(ctx context.Context, ro options.RunOptions, args []string, signers . } } + var roHashes []crypto.Hash + for _, hashStr := range ro.Hashes { + hash, err := cryptoutil.HashFromString(hashStr) + if err != nil { + return fmt.Errorf("failed to parse hash: %w", err) + } + roHashes = append(roHashes, hash) + } + defer out.Close() result, err := witness.Run( ro.StepName, signers[0], witness.RunWithAttestors(attestors), - witness.RunWithAttestationOpts(attestation.WithWorkingDir(ro.WorkingDir)), + witness.RunWithAttestationOpts(attestation.WithWorkingDir(ro.WorkingDir), attestation.WithHashes(roHashes)), witness.RunWithTimestampers(timestampers...), ) diff --git a/cmd/run_test.go b/cmd/run_test.go index 7d30214c..ef717b9c 100644 --- a/cmd/run_test.go +++ b/cmd/run_test.go @@ -116,3 +116,58 @@ func Test_runRunRSACA(t *testing.T) { require.NoError(t, err) assert.Equal(t, b, env.Signatures[0].Certificate) } + +func TestRunHashesOptions(t *testing.T) { + tests := []struct { + name string + hashesOption []string + expectErr bool + }{ + { + name: "Valid RSA key pair", + hashesOption: []string{"sha256"}, + expectErr: false, + }, + { + name: "Invalid hashes option", + hashesOption: []string{"invalidHash"}, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + privatekey, err := rsa.GenerateKey(rand.Reader, keybits) + require.NoError(t, err) + signer := cryptoutil.NewRSASigner(privatekey, crypto.SHA256) + + workingDir := t.TempDir() + attestationPath := filepath.Join(workingDir, "outfile.txt") + runOptions := options.RunOptions{ + WorkingDir: workingDir, + Attestations: []string{}, + Hashes: tt.hashesOption, + OutFilePath: attestationPath, + StepName: "teststep", + Tracing: false, + } + + args := []string{ + "bash", + "-c", + "echo 'test' > test.txt", + } + + err = runRun(context.Background(), runOptions, args, signer) + if tt.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + attestationBytes, err := os.ReadFile(attestationPath) + require.NoError(t, err) + env := dsse.Envelope{} + require.NoError(t, json.Unmarshal(attestationBytes, &env)) + } + }) + } +} diff --git a/docs/witness_run.md b/docs/witness_run.md index 9b9ac361..713056a0 100644 --- a/docs/witness_run.md +++ b/docs/witness_run.md @@ -14,6 +14,7 @@ witness run [cmd] [flags] --attestor-product-exclude-glob string Pattern to use when recording products. Files that match this pattern will be excluded as subjects on the attestation. --attestor-product-include-glob string Pattern to use when recording products. Files that match this pattern will be included as subjects on the attestation. (default "*") --enable-archivista Use Archivista to store or retrieve attestations + --hashes strings Hashes selected for digest calculation. Defaults to SHA256 (default [sha256]) -h, --help help for run -o, --outfile string File to which to write signed data. Defaults to stdout --signer-file-cert-path string Path to the file containing the certificate for the private key diff --git a/options/run.go b/options/run.go index 4b0119e7..ee844d30 100644 --- a/options/run.go +++ b/options/run.go @@ -25,6 +25,7 @@ type RunOptions struct { ArchivistaOptions ArchivistaOptions WorkingDir string Attestations []string + Hashes []string OutFilePath string StepName string Tracing bool @@ -37,6 +38,7 @@ func (ro *RunOptions) AddFlags(cmd *cobra.Command) { ro.ArchivistaOptions.AddFlags(cmd) cmd.Flags().StringVarP(&ro.WorkingDir, "workingdir", "d", "", "Directory from which commands will run") cmd.Flags().StringSliceVarP(&ro.Attestations, "attestations", "a", []string{"environment", "git"}, "Attestations to record") + cmd.Flags().StringSliceVar(&ro.Hashes, "hashes", []string{"sha256"}, "Hashes selected for digest calculation. Defaults to SHA256") cmd.Flags().StringVarP(&ro.OutFilePath, "outfile", "o", "", "File to which to write signed data. Defaults to stdout") cmd.Flags().StringVarP(&ro.StepName, "step", "s", "", "Name of the step being run") cmd.Flags().BoolVar(&ro.Tracing, "trace", false, "Enable tracing for the command")