From 2313823271306b2daea63f9755aefc4b63e88cdf Mon Sep 17 00:00:00 2001 From: Trishank Karthik Kuppusamy Date: Tue, 9 Jun 2020 16:55:30 -0400 Subject: [PATCH 1/8] delegate all paths to targets/releases Signed-off-by: Trishank Karthik Kuppusamy --- pkg/tuf/common.go | 4 +- pkg/tuf/delegations.go | 25 +++++++++++++ pkg/tuf/keys.go | 84 +++++++++++++++++++++++++----------------- pkg/tuf/sign.go | 73 ++++++++++++++++++++++-------------- 4 files changed, 123 insertions(+), 63 deletions(-) create mode 100644 pkg/tuf/delegations.go diff --git a/pkg/tuf/common.go b/pkg/tuf/common.go index 1f4b17a2..872869da 100644 --- a/pkg/tuf/common.go +++ b/pkg/tuf/common.go @@ -13,10 +13,12 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/registry" + "github.com/theupdateframework/notary/tuf/data" ) const ( - dockerConfigDir = ".docker" + dockerConfigDir = ".docker" + releasesRoleName = data.RoleName("targets/releases") ) func DefaultTrustDir() string { diff --git a/pkg/tuf/delegations.go b/pkg/tuf/delegations.go new file mode 100644 index 00000000..473fc13e --- /dev/null +++ b/pkg/tuf/delegations.go @@ -0,0 +1,25 @@ +package tuf + +import ( + "fmt" + + "github.com/theupdateframework/notary/client" + "github.com/theupdateframework/notary/tuf/data" +) + +// Delegate all paths ("*") to targets/releases. +// https://github.com/theupdateframework/notary/blob/f255ae779066dc28ae4aee196061e58bb38a2b49/cmd/notary/delegations.go +func delegateToReleases(repo client.Repository, publicKey data.PublicKey) error { + // How Notary v1 denotes "*"" + // https://github.com/theupdateframework/notary/blob/f255ae779066dc28ae4aee196061e58bb38a2b49/cmd/notary/delegations.go#L367 + allPaths := []string{""} + publicKeys := []data.PublicKey{publicKey} + + // Add the delegation to the repository + err := repo.AddDelegation(releasesRoleName, publicKeys, allPaths) + if err != nil { + return fmt.Errorf("failed to create delegation: %v", err) + } + + return nil +} diff --git a/pkg/tuf/keys.go b/pkg/tuf/keys.go index 2edcc43d..c19b0397 100644 --- a/pkg/tuf/keys.go +++ b/pkg/tuf/keys.go @@ -24,9 +24,9 @@ import ( func getPassphraseRetriever() notary.PassRetriever { baseRetriever := passphrase.PromptRetriever() env := map[string]string{ - "root": os.Getenv("SIGNY_ROOT_PASSPHRASE"), - "targets": os.Getenv("SIGNY_TARGETS_PASSPHRASE"), - "releases": os.Getenv("SIGNY_RELEASES_PASSPHRASE"), + "root": os.Getenv("SIGNY_ROOT_PASSPHRASE"), + "targets": os.Getenv("SIGNY_TARGETS_PASSPHRASE"), + "targets/releases": os.Getenv("SIGNY_RELEASES_PASSPHRASE"), } return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) { @@ -39,11 +39,12 @@ func getPassphraseRetriever() notary.PassRetriever { // Attempt to read a role key from a file, and return it as a data.PrivateKey // If key is for the Root role, it must be encrypted -func readKey(role data.RoleName, keyFilename string, retriever notary.PassRetriever) (data.PrivateKey, error) { +func readPrivateKey(role data.RoleName, keyFilename string, retriever notary.PassRetriever) (data.PrivateKey, error) { pemBytes, err := ioutil.ReadFile(keyFilename) if err != nil { return nil, fmt.Errorf("Error reading input root key file: %v", err) } + isEncrypted := true if err = cryptoservice.CheckRootKeyIsEncrypted(pemBytes); err != nil { if role == data.CanonicalRootRole { @@ -51,6 +52,7 @@ func readKey(role data.RoleName, keyFilename string, retriever notary.PassRetrie } isEncrypted = false } + var privKey data.PrivateKey if isEncrypted { privKey, _, err = trustmanager.GetPasswdDecryptBytes(retriever, pemBytes, "", data.CanonicalRootRole.String()) @@ -71,7 +73,7 @@ func importRootKey(rootKey string, nRepo client.Repository, retriever notary.Pas var rootKeyList []string if rootKey != "" { - privKey, err := readKey(data.CanonicalRootRole, rootKey, retriever) + privKey, err := readPrivateKey(data.CanonicalRootRole, rootKey, retriever) if err != nil { return nil, err } @@ -89,57 +91,71 @@ func importRootKey(rootKey string, nRepo client.Repository, retriever notary.Pas // Chooses the first root key available, which is initialization specific // but should return the HW one first. rootKeyID := rootKeyList[0] - log.Debugf("Signy found root key, using: %s\n", rootKeyID) - + log.Debugf("found root key: %s\n", rootKeyID) return []string{rootKeyID}, nil } return []string{}, nil } +// Try to reuse a single targets/releases key across repositories. +func reuseReleasesKey(r client.Repository) (data.PublicKey, error) { + // Get all known targets keys. + cryptoService := r.GetCryptoService() + keyList := cryptoService.ListKeys(releasesRoleName) + + // Try to extract a single targets/releases key we can reuse. + switch len(keyList) { + case 0: + log.Debugf("No %s key available, need to make one", releasesRoleName) + return cryptoService.Create(releasesRoleName, r.GetGUN(), data.ECDSAKey) + case 1: + log.Debugf("Nothing to do, only one %s key available", releasesRoleName) + return cryptoService.GetKey(keyList[0]), nil + default: + return nil, fmt.Errorf("there is more than one %s keys", releasesRoleName) + } +} + // Try to reuse a single targets key across repositories. // FIXME: Unfortunately, short of forking Notary or sending a PR upstream, there isn't an easy way to prevent it // from automagically creating a new, local targets key per TUF metadata repository. We fix this here by undoing // more than one new, local targets key, and reusing any existing local targets key, just like the way Notary // reuses the root key. func reuseTargetsKey(r client.Repository) error { - var ( - err error - thisTargetsKeyID, thatTargetsKeyID string - ) - // Get all known targets keys. - targetsKeyList := r.GetCryptoService().ListKeys(data.CanonicalTargetsRole) + keyList := r.GetCryptoService().ListKeys(data.CanonicalTargetsRole) + // Try to extract a single targets key we can reuse. - switch len(targetsKeyList) { + switch len(keyList) { case 0: - err = fmt.Errorf("no targets key despite having initialized a repo") + return fmt.Errorf("no targets key despite having initialized a repo") case 1: log.Debug("Nothing to do, only one targets key available") + return nil case 2: // First, we publish current changes to repository in order to list roles. // FIXME: Find a find better way to list roles w/o publishing changes first. - publishErr := r.Publish() - if publishErr != nil { - err = publishErr - break + err := r.Publish() + if err != nil { + return err } // Get the current top-level roles. - roleWithSigs, listRolesErr := r.ListRoles() - if listRolesErr != nil { - err = listRolesErr - break + roleWithSigs, err := r.ListRoles() + if err != nil { + return err } // Get the current targets key. // NOTE: We do not delete it, in case the user wants to keep it. + var thisKeyID string for _, roleWithSig := range roleWithSigs { role := roleWithSig.Role if role.Name == data.CanonicalTargetsRole { if len(role.KeyIDs) == 1 { - thisTargetsKeyID = role.KeyIDs[0] - log.Debugf("This targets keyid: %s", thisTargetsKeyID) + thisKeyID = role.KeyIDs[0] + log.Debugf("This targets keyid: %s", thisKeyID) } else { return fmt.Errorf("this targets role has more than 1 key") } @@ -147,19 +163,19 @@ func reuseTargetsKey(r client.Repository) error { } // Get and reuse the other targets key. - for _, keyID := range targetsKeyList { - if keyID != thisTargetsKeyID { - thatTargetsKeyID = keyID + var thatKeyID string + for _, keyID := range keyList { + if keyID != thisKeyID { + thatKeyID = keyID break } } - log.Debugf("That targets keyID: %s", thatTargetsKeyID) - log.Debugf("Before rotating targets key from %s to %s", thisTargetsKeyID, thatTargetsKeyID) - err = r.RotateKey(data.CanonicalTargetsRole, false, []string{thatTargetsKeyID}) + log.Debugf("That targets keyID: %s", thatKeyID) + log.Debugf("Before rotating targets key from %s to %s", thisKeyID, thatKeyID) + err = r.RotateKey(data.CanonicalTargetsRole, false, []string{thatKeyID}) log.Debugf("After targets key rotation") + return err default: - err = fmt.Errorf("there are more than 2 targets keys") + return fmt.Errorf("there are more than two targets keys") } - - return err } diff --git a/pkg/tuf/sign.go b/pkg/tuf/sign.go index 9fce8292..0680492c 100644 --- a/pkg/tuf/sign.go +++ b/pkg/tuf/sign.go @@ -9,15 +9,6 @@ import ( "github.com/theupdateframework/notary/tuf/data" ) -// clearChangelist clears the notary staging changelist -func clearChangeList(notaryRepo client.Repository) error { - cl, err := notaryRepo.GetChangelist() - if err != nil { - return err - } - return cl.Clear("") -} - // SignAndPublish signs an artifact, then publishes the metadata to a trust server func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeout string, custom *canonicaljson.RawMessage) (*client.Target, error) { if err := EnsureTrustDir(trustDir); err != nil { @@ -50,48 +41,74 @@ func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeou if err != nil { return nil, fmt.Errorf("cannot clear change list: %v", err) } - defer clearChangeList(repo) - if _, err = repo.ListTargets(); err != nil { + err = reuseKeys(repo, rootKey) + if err != nil { + return nil, fmt.Errorf("cannot reuse keys: %v", err) + } + + target, err := client.NewTarget(tag, file, custom) + if err != nil { + return nil, err + } + + // If roles is empty, we default to adding to targets + if err = repo.AddTarget(target, data.NewRoleList([]string{})...); err != nil { + return nil, err + } + + err = repo.Publish() + return target, err +} + +// reuse root and top-level targets keys +func reuseKeys(repo client.Repository, rootKey string) error { + if _, err := repo.ListTargets(); err != nil { switch err.(type) { case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist: // Reuse root key. rootKeyIDs, err := importRootKey(rootKey, repo, getPassphraseRetriever()) if err != nil { - return nil, err + return err } // NOTE: 2nd variadic argument is to indicate that snapshot is managed remotely. // The impact of a timestamp + snapshot key compromise is not terrible: // https://docs.docker.com/notary/service_architecture/#threat-model if err = repo.Initialize(rootKeyIDs, data.CanonicalSnapshotRole); err != nil { - return nil, fmt.Errorf("cannot initialize repo: %v", err) + return fmt.Errorf("cannot initialize repo: %v", err) } // Reuse targets key. if err = reuseTargetsKey(repo); err != nil { - return nil, fmt.Errorf("cannot reuse targets keys: %v", err) + return fmt.Errorf("cannot reuse %s keys: %v", data.CanonicalTargetsRole, err) + } + + // Reuse targets/releases key. + releasesPublicKey, err := reuseReleasesKey(repo) + if err != nil { + return fmt.Errorf("cannot reuse %s keys: %v", releasesRoleName, err) + } + + // Delegate to targets/releases. + err = delegateToReleases(repo, releasesPublicKey) + if err != nil { + return fmt.Errorf("cannot delegate to %s: %v", releasesRoleName, err) } default: - return nil, fmt.Errorf("cannot list targets: %v", err) + return fmt.Errorf("cannot list targets: %v", err) } } + return nil +} - target, err := client.NewTarget(tag, file, custom) +// clearChangelist clears the notary staging changelist +func clearChangeList(notaryRepo client.Repository) error { + cl, err := notaryRepo.GetChangelist() if err != nil { - return nil, err - } - - // TODO - Radu M - // decide whether to allow actually passing roles as flags - - // If roles is empty, we default to adding to targets - if err = repo.AddTarget(target, data.NewRoleList([]string{})...); err != nil { - return nil, err + return err } - - err = repo.Publish() - return target, err + return cl.Clear("") } From 9dd82d04c3f356c6b9710bd078ac5853aeb80cf2 Mon Sep 17 00:00:00 2001 From: Trishank Karthik Kuppusamy Date: Fri, 12 Jun 2020 17:13:42 -0400 Subject: [PATCH 2/8] sign and verify bundles as delegated targets Signed-off-by: Trishank Karthik Kuppusamy --- pkg/tuf/common.go | 16 +++++++++++++++- pkg/tuf/delegations.go | 12 ++++++++---- pkg/tuf/list.go | 32 ++++++++++++++++---------------- pkg/tuf/sign.go | 33 +++++++++++++++++---------------- pkg/tuf/verify.go | 36 +++++++++++++++++++----------------- scripts/live-reload.sh | 1 + 6 files changed, 76 insertions(+), 54 deletions(-) diff --git a/pkg/tuf/common.go b/pkg/tuf/common.go index 872869da..443c6424 100644 --- a/pkg/tuf/common.go +++ b/pkg/tuf/common.go @@ -21,6 +21,7 @@ const ( releasesRoleName = data.RoleName("targets/releases") ) +// DefaultTrustDir returns where the Signy trust data lives func DefaultTrustDir() string { homeEnvPath := os.Getenv("HOME") if homeEnvPath == "" && runtime.GOOS == "windows" { @@ -30,6 +31,7 @@ func DefaultTrustDir() string { return filepath.Join(homeEnvPath, ".signy") } +// DefaultDockerCfgDir returns where the Docker config directory lives func DefaultDockerCfgDir() string { homeEnvPath := os.Getenv("HOME") if homeEnvPath == "" && runtime.GOOS == "windows" { @@ -39,11 +41,23 @@ func DefaultDockerCfgDir() string { return filepath.Join(homeEnvPath, dockerConfigDir) } -// ensures the trust directory exists +// EnsureTrustDir ensures the trust directory exists func EnsureTrustDir(trustDir string) error { return os.MkdirAll(trustDir, 0700) } +func getGUN(name string) (string, error) { + r, err := reference.ParseNormalizedNamed(name) + if err != nil { + return "", err + } + repo, err := registry.ParseRepositoryInfo(r) + if err != nil { + return "", err + } + return repo.Name.Name(), nil +} + func getRepoAndTag(name string) (*registry.RepositoryInfo, string, error) { r, err := reference.ParseNormalizedNamed(name) if err != nil { diff --git a/pkg/tuf/delegations.go b/pkg/tuf/delegations.go index 473fc13e..77c57fb5 100644 --- a/pkg/tuf/delegations.go +++ b/pkg/tuf/delegations.go @@ -10,13 +10,17 @@ import ( // Delegate all paths ("*") to targets/releases. // https://github.com/theupdateframework/notary/blob/f255ae779066dc28ae4aee196061e58bb38a2b49/cmd/notary/delegations.go func delegateToReleases(repo client.Repository, publicKey data.PublicKey) error { - // How Notary v1 denotes "*"" - // https://github.com/theupdateframework/notary/blob/f255ae779066dc28ae4aee196061e58bb38a2b49/cmd/notary/delegations.go#L367 - allPaths := []string{""} + // the public keys used to verify the delegatee publicKeys := []data.PublicKey{publicKey} + // the target paths entrusted to the delegatee + paths := make([]string, 2) + gun := repo.GetGUN().String() + tags := gun + ":" + links := gun + "/in-toto-links/" + paths = append(paths, tags, links) // Add the delegation to the repository - err := repo.AddDelegation(releasesRoleName, publicKeys, allPaths) + err := repo.AddDelegation(releasesRoleName, publicKeys, paths) if err != nil { return fmt.Errorf("failed to create delegation: %v", err) } diff --git a/pkg/tuf/list.go b/pkg/tuf/list.go index 3f4f5d53..e310c8ec 100644 --- a/pkg/tuf/list.go +++ b/pkg/tuf/list.go @@ -9,33 +9,20 @@ import ( "github.com/theupdateframework/notary/tuf/data" ) -// PrintTargets prints all the targets for a specific GUN from a trust server -func PrintTargets(gun, trustServer, tlscacert, trustDir, timeout string) error { - targets, err := GetTargets(gun, trustServer, tlscacert, trustDir, timeout) - if err != nil { - return fmt.Errorf("cannot list targets:%v", err) - } - - for _, tgt := range targets { - fmt.Printf("%s\t%s\n", tgt.Name, hex.EncodeToString(tgt.Hashes["sha256"])) - } - return nil -} - // GetTargetWithRole returns a single target by name from the trusted collection -func GetTargetWithRole(gun, name, trustServer, tlscacert, trustDir, timeout string) (*client.TargetWithRole, error) { +func GetTargetWithRole(gun, targetName, trustServer, tlscacert, trustDir, timeout string) (*client.TargetWithRole, error) { targets, err := GetTargets(gun, trustServer, tlscacert, trustDir, timeout) if err != nil { return nil, fmt.Errorf("cannot list targets:%v", err) } for _, target := range targets { - if target.Name == name { + if target.Name == targetName { return target, nil } } - return nil, fmt.Errorf("cannot find target %v in trusted collection %v", name, gun) + return nil, fmt.Errorf("cannot find target %v in trusted collection %v", targetName, gun) } // GetTargets returns all targets for a given gun from the trusted collection @@ -63,3 +50,16 @@ func GetTargets(gun, trustServer, tlscacert, trustDir, timeout string) ([]*clien return repo.ListTargets() } + +// PrintTargets prints all the targets for a specific GUN from a trust server +func PrintTargets(gun, trustServer, tlscacert, trustDir, timeout string) error { + targets, err := GetTargets(gun, trustServer, tlscacert, trustDir, timeout) + if err != nil { + return fmt.Errorf("cannot list targets:%v", err) + } + + for _, tgt := range targets { + fmt.Printf("%s\t%s\n", tgt.Name, hex.EncodeToString(tgt.Hashes["sha256"])) + } + return nil +} diff --git a/pkg/tuf/sign.go b/pkg/tuf/sign.go index 0680492c..8e2757d4 100644 --- a/pkg/tuf/sign.go +++ b/pkg/tuf/sign.go @@ -15,19 +15,19 @@ func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeou return nil, fmt.Errorf("cannot ensure trust directory: %v", err) } - repoInfo, tag, err := getRepoAndTag(ref) + gun, err := getGUN(ref) if err != nil { - return nil, fmt.Errorf("cannot get repo and tag from reference: %v", err) + return nil, fmt.Errorf("cannot get GUN reference: %v", err) } - transport, err := makeTransport(trustServer, repoInfo.Name.Name(), tlscacert, timeout) + transport, err := makeTransport(trustServer, gun, tlscacert, timeout) if err != nil { return nil, fmt.Errorf("cannot make transport: %v", err) } repo, err := client.NewFileCachedRepository( trustDir, - data.GUN(repoInfo.Name.Name()), + data.GUN(gun), trustServer, transport, getPassphraseRetriever(), @@ -48,13 +48,14 @@ func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeou return nil, fmt.Errorf("cannot reuse keys: %v", err) } - target, err := client.NewTarget(tag, file, custom) + // NOTE: We use the full reference for the target filename of the bundle. + target, err := client.NewTarget(ref, file, custom) if err != nil { return nil, err } - // If roles is empty, we default to adding to targets - if err = repo.AddTarget(target, data.NewRoleList([]string{})...); err != nil { + // NOTE: And we add the bundle to the "targets/releases" instead of the top-level "targets" role. + if err = repo.AddTarget(target, releasesRoleName); err != nil { return nil, err } @@ -62,6 +63,15 @@ func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeou return target, err } +// clearChangelist clears the notary staging changelist +func clearChangeList(notaryRepo client.Repository) error { + cl, err := notaryRepo.GetChangelist() + if err != nil { + return err + } + return cl.Clear("") +} + // reuse root and top-level targets keys func reuseKeys(repo client.Repository, rootKey string) error { if _, err := repo.ListTargets(); err != nil { @@ -103,12 +113,3 @@ func reuseKeys(repo client.Repository, rootKey string) error { } return nil } - -// clearChangelist clears the notary staging changelist -func clearChangeList(notaryRepo client.Repository) error { - cl, err := notaryRepo.GetChangelist() - if err != nil { - return err - } - return cl.Clear("") -} diff --git a/pkg/tuf/verify.go b/pkg/tuf/verify.go index 28a933dd..de6c76e2 100644 --- a/pkg/tuf/verify.go +++ b/pkg/tuf/verify.go @@ -13,11 +13,30 @@ import ( "github.com/cnabio/signy/pkg/cnab" ) +// GetTargetAndSHA returns the target with roles and the SHA256 of the target file +func GetTargetAndSHA(targetName, trustServer, tlscacert, trustDir, timeout string) (*client.TargetWithRole, string, error) { + gun, err := getGUN(targetName) + if err != nil { + return nil, "", fmt.Errorf("cannot get repo and tag from reference: %v", err) + } + + target, err := GetTargetWithRole(gun, targetName, trustServer, tlscacert, trustDir, timeout) + if err != nil { + return nil, "", err + } + + trustedSHA := hex.EncodeToString(target.Hashes["sha256"]) + log.Infof("Pulled trust data for %v, with role %v - SHA256: %v", targetName, target.Role, trustedSHA) + return target, trustedSHA, nil +} + +// GetThickBundle reads the thick bundle from disk func GetThickBundle(localFile string) ([]byte, error) { log.Infof("Reading thick bundle on disk: %v", localFile) return ioutil.ReadFile(localFile) } +// GetThinBundle reads the thin bundle from the OCI registry func GetThinBundle(ref string) ([]byte, error) { log.Infof("Pulling thin bundle from registry: %v", ref) bun, err := cnab.Pull(ref) @@ -51,20 +70,3 @@ func verifyTargetSHAFromBytes(buf []byte, trustedSHA string) error { } return nil } - -// GetTargetAndSHA returns the target with roles and the SHA256 of the target file -func GetTargetAndSHA(ref, trustServer, tlscacert, trustDir, timeout string) (*client.TargetWithRole, string, error) { - repoInfo, tag, err := getRepoAndTag(ref) - if err != nil { - return nil, "", fmt.Errorf("cannot get repo and tag from reference: %v", err) - } - - target, err := GetTargetWithRole(repoInfo.Name.Name(), tag, trustServer, tlscacert, trustDir, timeout) - if err != nil { - return nil, "", err - } - - trustedSHA := hex.EncodeToString(target.Hashes["sha256"]) - log.Infof("Pulled trust data for %v, with role %v - SHA256: %v", ref, target.Role, trustedSHA) - return target, trustedSHA, nil -} diff --git a/scripts/live-reload.sh b/scripts/live-reload.sh index fbe80c4e..a3f8070e 100755 --- a/scripts/live-reload.sh +++ b/scripts/live-reload.sh @@ -6,6 +6,7 @@ build () { echo "Building..." # Build an image containing python-in-toto to verify bundles/images with. docker build --rm -t cnabio/signy-in-toto-verifier:$TAG -f in-toto-container.Dockerfile . + make lint make TAG=$TAG install echo "...done." echo From 5616526fb2b569a75eb84cc4d2ea2eabfee8ecf6 Mon Sep 17 00:00:00 2001 From: Trishank Karthik Kuppusamy Date: Wed, 17 Jun 2020 17:00:57 -0400 Subject: [PATCH 3/8] signing in-toto metadata ala MVP Signed-off-by: Trishank Karthik Kuppusamy --- README.md | 58 +--- cmd/sign.go | 40 ++- pkg/docker/docker.go | 13 + pkg/docker/docker_test.go | 31 ++ pkg/intoto/metadata.go | 169 +++++++---- pkg/intoto/verify.go | 8 +- pkg/tuf/common.go | 38 --- pkg/tuf/common_test.go | 49 ---- pkg/tuf/sign.go | 107 ++++++- pkg/tuf/verify.go | 3 +- testdata/intoto/foo.tar.gz | Bin 148 -> 0 bytes ...lformed.template => malformed.root.layout} | 0 ...db1e57fa773e4e31d70698b588f3e9cc48b35.pub} | 0 testdata/intoto/minimal/bundle.json | 91 ++++++ .../developer.2d2e5437.link | 141 +++++++++ .../machine.dd035ca2.link | 271 ++++++++++++++++++ testdata/intoto/minimal/root.layout | 118 ++++++++ testdata/intoto/minimal/root.layout.pub | 11 + testdata/intoto/package.2f89b927.link | 34 --- testdata/intoto/root.layout | 136 --------- testdata/intoto/root.layout.pub | 11 - testdata/intoto/write-code.776a00e2.link | 21 -- 22 files changed, 922 insertions(+), 428 deletions(-) delete mode 100644 testdata/intoto/foo.tar.gz rename testdata/intoto/{malformed.template => malformed.root.layout} (100%) rename testdata/intoto/{alice.pub => minimal/556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub} (100%) create mode 100644 testdata/intoto/minimal/bundle.json create mode 100644 testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/developer.2d2e5437.link create mode 100644 testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/machine.dd035ca2.link create mode 100644 testdata/intoto/minimal/root.layout create mode 100644 testdata/intoto/minimal/root.layout.pub delete mode 100644 testdata/intoto/package.2f89b927.link delete mode 100644 testdata/intoto/root.layout delete mode 100644 testdata/intoto/root.layout.pub delete mode 100644 testdata/intoto/write-code.776a00e2.link diff --git a/README.md b/README.md index 8415882a..87c0ef5e 100644 --- a/README.md +++ b/README.md @@ -101,67 +101,13 @@ INFO[0000] The SHA sums are equal: 540cc4dc213548ebbdffb2ab0ef58729e089d1887edbc - Add in-toto metadata when signing a thin bundle: ``` -$ ./scripts/signy-sign.sh testdata/cnab/bundle.json localhost:5000/thin-intoto:v2 --in-toto --layout testdata/intoto/root.layout --links testdata/intoto --layout-key testdata/intoto/alice.pub -INFO[0000] Adding In-Toto layout and links metadata to TUF -INFO[0000] Pushed trust data for localhost:5000/thin-intoto:v2: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 -INFO[0000] Starting to copy image cnab/helloworld:0.1.1 -INFO[0001] Completed image cnab/helloworld:0.1.1 copy -INFO[0001] Generated relocation map: relocation.ImageRelocationMap{"cnab/helloworld:0.1.1":"localhost:5000/thin-intoto@sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6"} -INFO[0001] Pushed successfully, with digest "sha256:b4936e42304c184bafc9b06dde9ea1f979129e09a021a8f40abc07f736de9268" +$ ./scripts/signy-sign.sh testdata/intoto/minimal/bundle.json localhost:5000/minimal:latest --in-toto --layout testdata/intoto/minimal/root.layout --links testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/ --layout-key testdata/intoto/minimal/root.layout.pub ``` - verifying the signature of a thin bundle and running the in-toto verifications in a container: ``` -$ ./scripts/signy-verify.sh localhost:5000/thin-intoto:v2 --in-toto -INFO[0000] Pulled trust data for localhost:5000/thin-intoto:v2, with role targets - SHA256: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 -INFO[0000] Pulling bundle from registry: localhost:5000/thin-intoto:v2 -INFO[0000] Computed SHA: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 -INFO[0000] The SHA sums are equal: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 -INFO[0000] Writing In-Toto metadata files into /tmp/intoto-verification169227773 -INFO[0000] copying file /in-toto/layout.template in container for verification... -INFO[0000] copying file /in-toto/key.pub in container for verification... -INFO[0000] copying file in-toto/package.2f89b927.link in container for verification... -INFO[0000] copying file in-toto/write-code.776a00e2.link in container for verification... -INFO[0000] copying file in-toto/foo.tar.gz in container for verification... -INFO[0000] Loading layout... -INFO[0000] Loading layout key(s)... -INFO[0000] Verifying layout signatures... -INFO[0001] Verifying layout expiration... -INFO[0001] Reading link metadata files... -INFO[0001] Verifying link metadata signatures... -INFO[0001] Verifying sublayouts... -INFO[0001] Verifying alignment of reported commands... -INFO[0001] Verifying command alignment for 'write-code.776a00e2.link'... -INFO[0001] Verifying command alignment for 'package.2f89b927.link'... -INFO[0001] Verifying threshold constraints... -INFO[0001] Skipping threshold verification for step 'write-code' with threshold '1'... -INFO[0001] Skipping threshold verification for step 'package' with threshold '1'... -INFO[0001] Verifying Step rules... -INFO[0001] Verifying material rules for 'write-code'... -INFO[0001] Verifying product rules for 'write-code'... -INFO[0001] Verifying 'ALLOW foo.py'... -INFO[0001] Verifying material rules for 'package'... -INFO[0001] Verifying 'MATCH foo.py WITH PRODUCTS FROM write-code'... -INFO[0001] Verifying 'DISALLOW *'... -INFO[0001] Verifying product rules for 'package'... -INFO[0001] Verifying 'ALLOW foo.tar.gz'... -INFO[0001] Verifying 'ALLOW foo.py'... -INFO[0001] Executing Inspection commands... -INFO[0001] Executing command for inspection 'untar'... -INFO[0001] Running 'untar'... -INFO[0001] Recording materials '.'... -INFO[0001] Running command 'tar xfz foo.tar.gz'... -INFO[0001] Recording products '.'... -INFO[0001] Creating link metadata... -INFO[0001] Verifying Inspection rules... -INFO[0001] Verifying material rules for 'untar'... -INFO[0001] Verifying 'MATCH foo.tar.gz WITH PRODUCTS FROM package'... -INFO[0001] Verifying 'DISALLOW foo.tar.gz'... -INFO[0001] Verifying product rules for 'untar'... -INFO[0001] Verifying 'MATCH foo.py WITH PRODUCTS FROM write-code'... -INFO[0001] Verifying 'DISALLOW foo.py'... -INFO[0001] The software product passed all verification. +$ ./scripts/signy-verify.sh localhost:5000/minimal:latest --in-toto ``` - similarly for a thick bundle: diff --git a/cmd/sign.go b/cmd/sign.go index b2ad06cd..4d9c5469 100644 --- a/cmd/sign.go +++ b/cmd/sign.go @@ -1,14 +1,13 @@ package main import ( - "encoding/hex" "fmt" - canonicaljson "github.com/docker/go/canonical/json" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/cnabio/signy/pkg/cnab" + "github.com/cnabio/signy/pkg/docker" "github.com/cnabio/signy/pkg/intoto" "github.com/cnabio/signy/pkg/tuf" ) @@ -89,28 +88,47 @@ INFO[0001] Pushed successfully, with digest "sha256:b4936e42304c184bafc9b06dde9e } func (s *signCmd) run() error { - var cm *canonicaljson.RawMessage + var publicKeys intoto.PublicKeys + var rootLayout intoto.RootLayout + var links intoto.Links + var bundleCustom intoto.Custom + + gun, err := docker.GetGUN(s.ref) + if err != nil { + return fmt.Errorf("cannot initialize GUN from %s: %v", s.ref, err) + } + if s.intoto { if s.layout == "" || s.layoutKey == "" || s.linkDir == "" { return fmt.Errorf("required in-toto metadata not found") } log.Infof("Adding In-Toto layout and links metadata to TUF") + err := intoto.ValidateFromPath(s.layout) if err != nil { return fmt.Errorf("validation for in-toto metadata failed: %v", err) } - custom, err := intoto.GetMetadataRawMessage(s.layout, s.linkDir, s.layoutKey) + + publicKeys, err = intoto.GetPublicKeys(gun, s.layoutKey) + if err != nil { + return fmt.Errorf("cannot read public keys: %v", err) + } + + rootLayout, err = intoto.GetRootLayout(gun, s.layout, publicKeys) if err != nil { - return fmt.Errorf("cannot get metadata message: %v", err) + return fmt.Errorf("cannot read root layout: %v", err) } - // TODO: Radu M - // Refactor GetMatedataRawMessage to return a pointer to a raw message - cm = &custom + + links, err = intoto.GetLinks(gun, s.linkDir) + if err != nil { + return fmt.Errorf("cannot read links: %v", err) + } + + bundleCustom = intoto.GetBundleCustom(rootLayout, links) } // NOTE: We first push to the Registry, and then Notary. This is so that if we modify the bundle locally, // we will not invalidate its signature by first pushing to Notary, and then the Registry. - // We push only thin bundles to the Registry. if !s.thick { if err := cnab.Push(s.file, s.ref); err != nil { @@ -118,11 +136,11 @@ func (s *signCmd) run() error { } } - target, err := tuf.SignAndPublish(trustDir, trustServer, s.ref, s.file, tlscacert, s.rootKey, timeout, cm) + bundleDigest, err := tuf.SignAndPublish(trustDir, trustServer, s.ref, s.file, tlscacert, s.rootKey, timeout, rootLayout, publicKeys, links, bundleCustom) if err != nil { return fmt.Errorf("cannot sign and publish trust data: %v", err) } - log.Infof("Pushed trust data for %v: %v\n", s.ref, hex.EncodeToString(target.Hashes["sha256"])) + log.Infof("Pushed trust data for %v: %v\n", s.ref, bundleDigest) return nil } diff --git a/pkg/docker/docker.go b/pkg/docker/docker.go index 004f52ce..675f3d8f 100644 --- a/pkg/docker/docker.go +++ b/pkg/docker/docker.go @@ -32,6 +32,19 @@ const ( workingDir = "/in-toto" // Where we expect to copy in-toto artifacts to ) +// GetGUN returns the Globally Unique Name for a reference image name +func GetGUN(name string) (string, error) { + r, err := reference.ParseNormalizedNamed(name) + if err != nil { + return "", err + } + repo, err := registry.ParseRepositoryInfo(r) + if err != nil { + return "", err + } + return repo.Name.Name(), nil +} + // Run will start a container, copy all In-Toto metadata in /in-toto // then run in-toto-verification func Run(verificationImage, verificationDir, logLevel string) error { diff --git a/pkg/docker/docker_test.go b/pkg/docker/docker_test.go index 2f028dd7..a6312d6c 100644 --- a/pkg/docker/docker_test.go +++ b/pkg/docker/docker_test.go @@ -15,3 +15,34 @@ func TestRun(t *testing.T) { err := Run(VerificationImage+"latest", testDir, log.InfoLevel.String()) assert.NoError(t, err) } + +func TestParseReference(t *testing.T) { + tests := []struct { + input string + gun string + }{ + { + input: "localhost:5000/local-test-simple:v1", + gun: "localhost:5000/local-test-simple", + }, + { + input: "localhost:5000/multi-path/some/bundle:v1", + gun: "localhost:5000/multi-path/some/bundle", + }, + { + input: "dockerhubusername/bundle:v3", + gun: "docker.io/dockerhubusername/bundle", + }, + { + input: "mycnabregistry.azurecr.io/org/sub-org/bundle:latest", + gun: "mycnabregistry.azurecr.io/org/sub-org/bundle", + }, + } + + is := assert.New(t) + for _, test := range tests { + gun, err := GetGUN(test.input) + is.NoError(err) + is.Equal(test.gun, gun) + } +} diff --git a/pkg/intoto/metadata.go b/pkg/intoto/metadata.go index 70f67227..d14e5f83 100644 --- a/pkg/intoto/metadata.go +++ b/pkg/intoto/metadata.go @@ -9,89 +9,148 @@ import ( canonicaljson "github.com/docker/go/canonical/json" ) -// Metadata represents the In-Toto metadata stored in TUF. -// All fields are represented as []byte in order to be stored in the Custom field for TUF metadata. +// Metadata points to root layout, its public keys, and/or links type Metadata struct { - // TODO: remove this once the TUF targets key is used to sign the root layout - Key []byte `json:"key"` - Layout []byte `json:"layout"` - Links map[string][]byte `json:"links"` + Data []byte `json:"data"` + PublicKeys []string `json:"public-keys"` // filenames + Links []string `json:"links"` // filenames } -// WriteMetadataFiles writes the content of a metadata object into files in a directory -func WriteMetadataFiles(m *Metadata, dir string) error { - abs, err := filepath.Abs(dir) - if err != nil { - return err - } +// Custom is a generic structure that contains in-toto Metadata +type Custom struct { + InToto Metadata `json:"in-toto"` +} - //FIXME: no need to actually write filename. - err = ioutil.WriteFile(filepath.Join(abs, "root.layout"), m.Layout, ReadOnlyMask) - if err != nil { - return err - } +// PublicKeys is a map from the GUN-qualified filename +// (e.g., "example.com/example-org/example-bundle/in-toto-pubkeys/keyid.pub") +// to a Custom struct +type PublicKeys map[string]Custom + +// RootLayout maps a GUN-qualified filename +// (e.g., "example.com/example-org/example-bundle/in-toto-metadata/root.layout") +// to a Custom struct +type RootLayout struct { + Filename string + Custom Custom +} + +// Links is a map from the GUN-qualified filename +// (e.g., "example.com/example-org/example-bundle/in-toto-metadata/DIGEST/step.link") +// to a Custom struct +type Links map[string]Custom - //FIXME: no need to actually write filenames. - err = ioutil.WriteFile(filepath.Join(abs, "root.layout.pub"), m.Key, ReadOnlyMask) +// GetRawMessage transforms a Custom struct into a raw Canonical JSON message +func (custom *Custom) GetRawMessage() (canonicaljson.RawMessage, error) { + marshalled, err := canonicaljson.MarshalCanonical(custom) if err != nil { - return err + return nil, fmt.Errorf("cannot encode custom metadata into canonical json: %v", err) } + return canonicaljson.RawMessage(marshalled), nil +} + +// GetPublicKeys reads public keys off disk into a PublicKeys map +func GetPublicKeys(gun string, filenames ...string) (PublicKeys, error) { + publicKeys := make(PublicKeys) - for n, c := range m.Links { - err = ioutil.WriteFile(filepath.Join(abs, n), c, ReadOnlyMask) + for _, filename := range filenames { + if !strings.HasSuffix(filename, ".pub") { + return nil, fmt.Errorf("%s does not have a .pub suffix", filename) + } + + data, err := readFile(filename) if err != nil { - return err + return nil, err } + + metadata := Metadata{Data: data} + custom := Custom{InToto: metadata} + filename = gun + "/in-toto-pubkeys/" + filename + publicKeys[filename] = custom } - return nil + return publicKeys, nil } -// GetMetadataRawMessage takes In-Toto metadata and returns a canonical RawMessage -// that can be stored in the TUF targets custom field. -// -// TODO: layout signing key should not be passed by the library. -// Layouts should be signed with the targets key used to sign the TUF collection. -func GetMetadataRawMessage(layout string, linkDir string, layoutKey string) (canonicaljson.RawMessage, error) { - k, err := ioutil.ReadFile(layoutKey) - if err != nil { - return nil, fmt.Errorf("cannot get canonical JSON from file %v: %v", layoutKey, err) +// GetRootLayout reads root layout off disk into a RootLayout struct +func GetRootLayout(gun string, filename string, publicKeys PublicKeys) (RootLayout, error) { + var rootLayout RootLayout + + if !strings.HasSuffix(filename, ".layout") { + return rootLayout, fmt.Errorf("%s does not have a .layout suffix", filename) } - l, err := ioutil.ReadFile(layout) + data, err := readFile(filename) if err != nil { - return nil, fmt.Errorf("cannot get canonical JSON from file %v: %v", layout, err) + return rootLayout, err + } + + filenames := make([]string, len(publicKeys)) + for filename := range publicKeys { + filenames = append(filenames, filename) } - links := make(map[string][]byte) - files, err := ioutil.ReadDir(linkDir) + metadata := Metadata{Data: data, PublicKeys: filenames} + custom := Custom{InToto: metadata} + filename = gun + "/in-toto-metadata/" + filename + rootLayout.Filename = filename + rootLayout.Custom = custom + return rootLayout, nil +} + +// GetLinks reads link metadata off disk into a Links struct +func GetLinks(gun string, dir string) (Links, error) { + fileinfos, err := ioutil.ReadDir(dir) if err != nil { - return nil, fmt.Errorf("cannot read links directory %v: %v", linkDir, err) + return nil, fmt.Errorf("cannot read links directory %v: %v", dir, err) } - for _, f := range files { - // TODO - Radu M - // - // robust check if file is actually a link - if !strings.Contains(f.Name(), ".link") { - continue + + // NOTE: We assume that the base directory used to hold the links are unique + // (e.g., identified by the digest of the first link metadata file corresponding to the first step). + // This is so that different versions of links corresponding to the same root layout can be safely isolated from each other. + // TODO: If they are not unique, should we raise an error later when adding them to the TUF targets metadata? + digest := filepath.Base(dir) + links := make(Links) + for _, fileinfo := range fileinfos { + filename := fileinfo.Name() + if !strings.Contains(filename, ".link") { + return nil, fmt.Errorf("%s does not have a .link suffix", filename) } - b, err := ioutil.ReadFile(filepath.Join(linkDir, f.Name())) + + data, err := readFile(filepath.Join(dir, filename)) if err != nil { - return nil, fmt.Errorf("cannot get canonical JSON from file %v: %v", f.Name(), err) + return nil, err } - links[f.Name()] = b + + metadata := Metadata{Data: data} + custom := Custom{InToto: metadata} + filename = gun + "/in-toto-metadata/" + digest + "/" + filename + links[filename] = custom } - m := &Metadata{ - Key: k, - Layout: l, - Links: links, + return links, nil +} + +// GetBundleCustom returns a Custom struct pointing to a list of root layout and links +func GetBundleCustom(rootLayout RootLayout, links Links) Custom { + filenames := make([]string, len(links)+1) + filenames = append(filenames, rootLayout.Filename) + for filename := range links { + filenames = append(filenames, filename) } + metadata := Metadata{Links: filenames} + return Custom{InToto: metadata} +} + +// WriteMetadataFiles writes the content of a metadata object into files in a directory +// TODO +func WriteMetadataFiles(m *Metadata, dir string) error { + return fmt.Errorf("not implemented") +} - raw, err := canonicaljson.MarshalCanonical(m) +func readFile(filename string) ([]byte, error) { + bytes, err := ioutil.ReadFile(filename) if err != nil { - return nil, fmt.Errorf("cannot encode in-toto metadata into canonical json %v: %v", m, err) + return nil, err } - - return canonicaljson.RawMessage(raw), nil + return bytes, nil } diff --git a/pkg/intoto/verify.go b/pkg/intoto/verify.go index 1500745c..463a5c97 100644 --- a/pkg/intoto/verify.go +++ b/pkg/intoto/verify.go @@ -13,10 +13,13 @@ import ( ) const ( + // BundleFilename is the name written to disk during verification BundleFilename = "bundle.json" - ReadOnlyMask = 0400 + // ReadOnlyMask is applied to files written to disk for verification + ReadOnlyMask = 0400 ) +// VerifyOnOS verifies a bundle directly on the OS func VerifyOnOS(target *client.TargetWithRole, bundle []byte) error { verificationDir, err := getVerificationDir(target, bundle) if err != nil { @@ -29,6 +32,7 @@ func VerifyOnOS(target *client.TargetWithRole, bundle []byte) error { return verifyOnOS(verificationDir) } +// VerifyInContainer verifies a bundle within a container func VerifyInContainer(target *client.TargetWithRole, bundle []byte, verificationImage string, logLevel string) error { verificationDir, err := getVerificationDir(target, bundle) if err != nil { @@ -42,6 +46,7 @@ func VerifyInContainer(target *client.TargetWithRole, bundle []byte, verificatio } func getVerificationDir(target *client.TargetWithRole, bundle []byte) (string, error) { + // FIXME m := &Metadata{} err := json.Unmarshal(*target.Custom, m) if err != nil { @@ -53,6 +58,7 @@ func getVerificationDir(target *client.TargetWithRole, bundle []byte) (string, e return "", err } + // FIXME log.Infof("Writing in-toto metadata files into %v", verificationDir) err = WriteMetadataFiles(m, verificationDir) if err != nil { diff --git a/pkg/tuf/common.go b/pkg/tuf/common.go index 443c6424..1fefdfc1 100644 --- a/pkg/tuf/common.go +++ b/pkg/tuf/common.go @@ -11,8 +11,6 @@ import ( "path/filepath" "runtime" - "github.com/docker/distribution/reference" - "github.com/docker/docker/registry" "github.com/theupdateframework/notary/tuf/data" ) @@ -45,39 +43,3 @@ func DefaultDockerCfgDir() string { func EnsureTrustDir(trustDir string) error { return os.MkdirAll(trustDir, 0700) } - -func getGUN(name string) (string, error) { - r, err := reference.ParseNormalizedNamed(name) - if err != nil { - return "", err - } - repo, err := registry.ParseRepositoryInfo(r) - if err != nil { - return "", err - } - return repo.Name.Name(), nil -} - -func getRepoAndTag(name string) (*registry.RepositoryInfo, string, error) { - r, err := reference.ParseNormalizedNamed(name) - if err != nil { - return nil, "", err - } - repo, err := registry.ParseRepositoryInfo(r) - if err != nil { - return nil, "", err - } - - return repo, getTag(r), nil -} - -func getTag(ref reference.Named) string { - switch x := ref.(type) { - case reference.Canonical, reference.Digested: - return "" - case reference.NamedTagged: - return x.Tag() - default: - return "" - } -} diff --git a/pkg/tuf/common_test.go b/pkg/tuf/common_test.go index 0007702f..cbd1b4a0 100644 --- a/pkg/tuf/common_test.go +++ b/pkg/tuf/common_test.go @@ -1,50 +1 @@ package tuf - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestParseReference(t *testing.T) { - tests := []struct { - input string - repository string - registry string - tag string - }{ - { - input: "localhost:5000/local-test-simple:v1", - repository: "localhost:5000/local-test-simple", - registry: "localhost:5000", - tag: "v1", - }, - { - input: "localhost:5000/multi-path/some/bundle:v1", - repository: "localhost:5000/multi-path/some/bundle", - registry: "localhost:5000", - tag: "v1", - }, - { - input: "dockerhubusername/bundle:v3", - repository: "docker.io/dockerhubusername/bundle", - registry: "docker.io", - tag: "v3", - }, - { - input: "mycnabregistry.azurecr.io/org/sub-org/bundle:latest", - repository: "mycnabregistry.azurecr.io/org/sub-org/bundle", - registry: "mycnabregistry.azurecr.io", - tag: "latest", - }, - } - - is := assert.New(t) - for _, test := range tests { - repo, tag, err := getRepoAndTag(test.input) - is.NoError(err) - is.Equal(test.repository, repo.Name.Name()) - is.Equal(test.tag, tag) - is.Equal(test.registry, repo.Index.Name) - } -} diff --git a/pkg/tuf/sign.go b/pkg/tuf/sign.go index 8e2757d4..4152543b 100644 --- a/pkg/tuf/sign.go +++ b/pkg/tuf/sign.go @@ -1,28 +1,32 @@ package tuf import ( + "bytes" + "encoding/hex" "fmt" - canonicaljson "github.com/docker/go/canonical/json" + "github.com/cnabio/signy/pkg/docker" + "github.com/cnabio/signy/pkg/intoto" + "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/trustpinning" "github.com/theupdateframework/notary/tuf/data" ) // SignAndPublish signs an artifact, then publishes the metadata to a trust server -func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeout string, custom *canonicaljson.RawMessage) (*client.Target, error) { +func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeout string, rootLayout intoto.RootLayout, publicKeys intoto.PublicKeys, links intoto.Links, bundleCustom intoto.Custom) (string, error) { if err := EnsureTrustDir(trustDir); err != nil { - return nil, fmt.Errorf("cannot ensure trust directory: %v", err) + return "", fmt.Errorf("cannot ensure trust directory: %v", err) } - gun, err := getGUN(ref) + gun, err := docker.GetGUN(ref) if err != nil { - return nil, fmt.Errorf("cannot get GUN reference: %v", err) + return "", fmt.Errorf("cannot get GUN reference: %v", err) } transport, err := makeTransport(trustServer, gun, tlscacert, timeout) if err != nil { - return nil, fmt.Errorf("cannot make transport: %v", err) + return "", fmt.Errorf("cannot make transport: %v", err) } repo, err := client.NewFileCachedRepository( @@ -34,33 +38,106 @@ func SignAndPublish(trustDir, trustServer, ref, file, tlscacert, rootKey, timeou trustpinning.TrustPinConfig{}, ) if err != nil { - return nil, fmt.Errorf("cannot create new file cached repository: %v", err) + return "", fmt.Errorf("cannot create new file cached repository: %v", err) } err = clearChangeList(repo) if err != nil { - return nil, fmt.Errorf("cannot clear change list: %v", err) + return "", fmt.Errorf("cannot clear change list: %v", err) } defer clearChangeList(repo) err = reuseKeys(repo, rootKey) if err != nil { - return nil, fmt.Errorf("cannot reuse keys: %v", err) + return "", fmt.Errorf("cannot reuse keys: %v", err) + } + + err = addRootLayout(repo, rootLayout, publicKeys) + if err != nil { + return "", fmt.Errorf("cannot add root layout: %v", err) + } + + bundleDigest, err := addBundle(repo, ref, file, links, bundleCustom) + if err != nil { + return "", fmt.Errorf("cannot add bundle: %v", err) + } + + err = repo.Publish() + return bundleDigest, err +} + +func addBundle(repo client.Repository, ref, file string, links intoto.Links, bundleCustom intoto.Custom) (string, error) { + for linkFilename, linkCustom := range links { + linkMeta, err := data.NewFileMeta(bytes.NewBuffer(linkCustom.InToto.Data), data.NotaryDefaultHashes...) + if err != nil { + return "", fmt.Errorf("cannot get link meta: %v", err) + } + + linkCustomRawMessage, err := linkCustom.GetRawMessage() + if err != nil { + return "", fmt.Errorf("cannot get raw message for link custom: %v", err) + } + + linkTarget := client.Target{Name: linkFilename, Hashes: linkMeta.Hashes, Length: linkMeta.Length, Custom: &linkCustomRawMessage} + if err = repo.AddTarget(&linkTarget, releasesRoleName); err != nil { + return "", fmt.Errorf("cannot add link target: %v", err) + } + + } + + bundleCustomRawMessage, err := bundleCustom.GetRawMessage() + if err != nil { + return "", fmt.Errorf("cannot get raw message for bundle custom: %v", err) } // NOTE: We use the full reference for the target filename of the bundle. - target, err := client.NewTarget(ref, file, custom) + bundleTarget, err := client.NewTarget(ref, file, &bundleCustomRawMessage) if err != nil { - return nil, err + return "", fmt.Errorf("cannot get bundle target: %v", err) } // NOTE: And we add the bundle to the "targets/releases" instead of the top-level "targets" role. - if err = repo.AddTarget(target, releasesRoleName); err != nil { - return nil, err + if err = repo.AddTarget(bundleTarget, releasesRoleName); err != nil { + return "", fmt.Errorf("cannot add bundle target: %v", err) } - err = repo.Publish() - return target, err + return hex.EncodeToString(bundleTarget.Hashes["sha256"]), nil +} + +func addRootLayout(repo client.Repository, rootLayout intoto.RootLayout, publicKeys intoto.PublicKeys) error { + for publicKeyFilename, publicKeyCustom := range publicKeys { + meta, err := data.NewFileMeta(bytes.NewBuffer(publicKeyCustom.InToto.Data), data.NotaryDefaultHashes...) + if err != nil { + return fmt.Errorf("cannot get public key meta: %v", err) + } + + publicKeyCustomRawMessage, err := publicKeyCustom.GetRawMessage() + if err != nil { + return fmt.Errorf("cannot get raw message for public key custom: %v", err) + } + + linkTarget := client.Target{Name: publicKeyFilename, Hashes: meta.Hashes, Length: meta.Length, Custom: &publicKeyCustomRawMessage} + if err = repo.AddTarget(&linkTarget, data.CanonicalTargetsRole); err != nil { + return fmt.Errorf("cannot add public key target: %v", err) + } + } + + rootLayoutMeta, err := data.NewFileMeta(bytes.NewBuffer(rootLayout.Custom.InToto.Data), data.NotaryDefaultHashes...) + if err != nil { + return fmt.Errorf("cannot get root layout meta: %v", err) + } + + rootLayoutCustomRawMessage, err := rootLayout.Custom.GetRawMessage() + if err != nil { + return fmt.Errorf("cannot get raw message for root layout custom: %v", err) + } + + linkTarget := client.Target{Name: rootLayout.Filename, Hashes: rootLayoutMeta.Hashes, Length: rootLayoutMeta.Length, Custom: &rootLayoutCustomRawMessage} + if err = repo.AddTarget(&linkTarget, data.CanonicalTargetsRole); err != nil { + return fmt.Errorf("cannot add root layout target: %v", err) + } + + return nil } // clearChangelist clears the notary staging changelist diff --git a/pkg/tuf/verify.go b/pkg/tuf/verify.go index de6c76e2..95c1c707 100644 --- a/pkg/tuf/verify.go +++ b/pkg/tuf/verify.go @@ -11,11 +11,12 @@ import ( "github.com/theupdateframework/notary/client" "github.com/cnabio/signy/pkg/cnab" + "github.com/cnabio/signy/pkg/docker" ) // GetTargetAndSHA returns the target with roles and the SHA256 of the target file func GetTargetAndSHA(targetName, trustServer, tlscacert, trustDir, timeout string) (*client.TargetWithRole, string, error) { - gun, err := getGUN(targetName) + gun, err := docker.GetGUN(targetName) if err != nil { return nil, "", fmt.Errorf("cannot get repo and tag from reference: %v", err) } diff --git a/testdata/intoto/foo.tar.gz b/testdata/intoto/foo.tar.gz deleted file mode 100644 index 5de7b881306435ec0cef766267d07c4f684ae76c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmb2|=3v+>QWC+y{Pw&p*C7Xiwue{dB^`*G`S;{AuDHWW+>aiy-4(fa?<(u*32Gf5 z?9LuH2(A$>w(~om7raVp&-8M>h5wh_GBXc5YEimZDw|#3bPng-7u&YZ`Wblcdz9+g w>zOat%{#hdL;m~|=Kqhx`6YWr>TP#EsrtU|E;|D<*l>=!W1jmo1`P%V08`IFd;kCd diff --git a/testdata/intoto/malformed.template b/testdata/intoto/malformed.root.layout similarity index 100% rename from testdata/intoto/malformed.template rename to testdata/intoto/malformed.root.layout diff --git a/testdata/intoto/alice.pub b/testdata/intoto/minimal/556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub similarity index 100% rename from testdata/intoto/alice.pub rename to testdata/intoto/minimal/556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub diff --git a/testdata/intoto/minimal/bundle.json b/testdata/intoto/minimal/bundle.json new file mode 100644 index 00000000..a9d0aa7a --- /dev/null +++ b/testdata/intoto/minimal/bundle.json @@ -0,0 +1,91 @@ +{ + "credentials": { + "hostkey": { + "env": "HOST_KEY", + "path": "/etc/hostkey.txt" + } + }, + "custom": { + "com.example.backup-preferences": { + "frequency": "daily" + }, + "com.example.duffle-bag": { + "icon": "https://example.com/icon.png", + "iconType": "PNG" + } + }, + "definitions": { + "http_port": { + "default": 80, + "maximum": 10240, + "minimum": 10, + "type": "integer" + }, + "port": { + "maximum": 65535, + "minimum": 1024, + "type": "integer" + }, + "string": { + "type": "string" + }, + "x509Certificate": { + "contentEncoding": "base64", + "contentMediaType": "application/x-x509-user-cert", + "type": "string", + "writeOnly": true + } + }, + "description": "An example 'thin' helloworld Cloud-Native Application Bundle", + "images": { + "my-microservice": { + "contentDigest": "sha256:aaaaaaaaaaaa...", + "description": "my microservice", + "image": "example/microservice:1.2.3" + } + }, + "invocationImages": [ + { + "contentDigest": "sha256:aaaaaaa...", + "image": "example/helloworld:0.1.0", + "imageType": "docker" + } + ], + "maintainers": [ + { + "email": "matt.butcher@microsoft.com", + "name": "Matt Butcher", + "url": "https://example.com" + } + ], + "name": "helloworld", + "outputs": { + "clientCert": { + "definition": "x509Certificate", + "path": "/cnab/app/outputs/clientCert" + }, + "hostName": { + "applyTo": [ + "install" + ], + "definition": "string", + "description": "the hostname produced installing the bundle", + "path": "/cnab/app/outputs/hostname" + }, + "port": { + "definition": "port", + "path": "/cnab/app/outputs/port" + } + }, + "parameters": { + "backend_port": { + "definition": "http_port", + "description": "The port that the back-end will listen on", + "destination": { + "env": "BACKEND_PORT" + } + } + }, + "schemaVersion": "v1.0.0", + "version": "0.1.2" +} \ No newline at end of file diff --git a/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/developer.2d2e5437.link b/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/developer.2d2e5437.link new file mode 100644 index 00000000..2348595f --- /dev/null +++ b/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/developer.2d2e5437.link @@ -0,0 +1,141 @@ +{ + "signatures": [ + { + "keyid": "2d2e5437de978b123ed8a50692cdb38c198ced0f1f356398abe9dd5206e890e2", + "sig": "6e477535c7df002e09a7badbb3159c4265e8d209a026d0676677a4a002189ebf0b19f0dbaa1e9a4bbcef70e3e2fe636d46f4ef6e5265c515797b9a77a9c241ef876b90954a8145cbf569f2fef8756de9e251936ad270b56646b4e1344f9200bb0ee8bb5c237969cc597249f89aa90680671433543852b63741000fe3e8b28c8542c573f661fe8f2c977ecccccec808b194c94b5468de7036de3517258996f7769ad16ce5a9190578a9465e37717d7262b4055c72986a13018855bf2bb27eb0877d2f44f151e83a8ce89724e501a0fa18c1bfa965122fc7bf231844c753f66f0718ab20e086a5966ed55b7f2080e0b89fcc70f52960c702cd31a53395ebc31edcbf5ca2ad8ec0758759a7d5cfad40e903a52ab6f44ac2ca508ce90da10270811aeee9648d41fc7ec72603214a3bf963c1e690a9cd30fde5e9119ee831e2f39ea56bf3922149e051a878baa5794d12e6a52a21db88eaa87ac4c2979cf5f123a2cffff8891b45ac093ad58bb37aa58f1a4e4afd86271badfe021b05c3c8736ea4a6" + } + ], + "signed": { + "_type": "link", + "byproducts": {}, + "command": [], + "environment": {}, + "materials": {}, + "name": "developer", + "products": { + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/credentials/hostkey/env": { + "sha256": "205c6e161cd52c8a5e28ac1a14a70ece07886438418ef1ed7ff431a3c59c55c2" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/credentials/hostkey/path": { + "sha256": "ea4278fd99a3cc839efd8d8675c2b754a6e556e0cd79e2dc10df03e9be071a56" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.backup-preferences/frequency": { + "sha256": "fa6a92cf80cf218ed717b3cc3a405ffda3fdf6a983d72fed1bdd113622fea03d" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.duffle-bag/icon": { + "sha256": "317d5a9514203bd1e19b80d39c846106513c7d20bd63271e8dbdb4821bff63b1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.duffle-bag/iconType": { + "sha256": "2775d1b05dce8419e4bab67f17fb5ed87eb0ca5b30232a04de1d574b99558200" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/default": { + "sha256": "48449a14a4ff7d79bb7a1b6f3d488eba397c36ef25634c111b49baf362511afc" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/maximum": { + "sha256": "205e7b780de08f42eda9fbf1715507758afd1f2eb499ffc6d0a352f904ee72f1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/minimum": { + "sha256": "4a44dc15364204a80fe80e9039455cc1608281820fe2b24f1e5233ade6af1dd5" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/type": { + "sha256": "bb894e256c101d183cfd51432eebb3feb183cf155da5994b19a45406a3e24aac" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/maximum": { + "sha256": "f2f89ede8e7d4b3d2243dea1ca96b8ece56f793811d9708b4a0181bf81a50011" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/minimum": { + "sha256": "e39eef82f61b21e2e7f762fcc4307358f165757f2e77ec855d6992f7e0191932" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/type": { + "sha256": "bb894e256c101d183cfd51432eebb3feb183cf155da5994b19a45406a3e24aac" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/string/type": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/contentEncoding": { + "sha256": "cb65d001ead1184d17391b55d1e96be555a573d9254cac717104f5d9d97e817c" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/contentMediaType": { + "sha256": "fc395c0b2477371951c95b71625d3919c73980dbb24aeca496578fc86a904add" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/type": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/writeOnly": { + "sha256": "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/description": { + "sha256": "12ccf5980e26a71d58ac3b877b24f82fdaede3e01446038585b5b201d4b83c55" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/contentDigest": { + "sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/description": { + "sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/image": { + "sha256": "be56793296fdb27458fd80666e5dcd1af52dd298760097ed7e73944e70e05118" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/contentDigest": { + "sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/image": { + "sha256": "d33274172ff3405c8cbf2ebc6cbe2f3d5904a0d53d5521d0aa6a390124e51703" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/imageType": { + "sha256": "f320b810ea61ce6405c478a1a67befdcc117a307e62f17deb2417be2f98b1864" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/email": { + "sha256": "ea6b0d7bd10c06cbda2212bed77c5b2f9c0ef7b218afaf8e013f4466ba9f2ad1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/name": { + "sha256": "6682c39fd22bf39169f435916402584970df9a4d6ed72fc7ab7db46dca57d3e4" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/url": { + "sha256": "5775c078c67ebad469f37e1d888f3084804a19ce7511c28c74c54e8232a51bdb" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/name": { + "sha256": "7b91cf1e312a84544a3d5f82ce965daf5620579a4a5e7d37b90829238a2f1055" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/clientCert/definition": { + "sha256": "157c4a9d3e97adcdd2028934f7c21db802f1c90d2193ba902fb9cc17fd713402" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/clientCert/path": { + "sha256": "2c66367f304e04df32081f665b5e106d4835b8544b7999fbe5bc73cd7d16b94c" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/applyTo": { + "sha256": "2594d48ff5c9c3577770805617147fe8b06d206b22baa806c3fd21f352acd183" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/definition": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/description": { + "sha256": "da192c9f51c6dc131efe1418a1a408bbef863fd264cc488db48f947bb955efb4" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/path": { + "sha256": "fcc9b2b4e2eb66597d77def766e3ade74c37ff8f830bd0808303345a5ec80761" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/port/definition": { + "sha256": "1915cd692d3b19d814007446d9ab8c0a2cff60dc516035caba9cd00434802e94" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/port/path": { + "sha256": "5b6a3c0e9e60e2829fd2dce7c7f00d6db98ed9e84f43dbd407e9ca67de3e2413" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/definition": { + "sha256": "b70797c6a59d071c0171317fc593cfdda53d35cf1cd7d9fffaca351c84468b05" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/description": { + "sha256": "06b809960a94debf9253cbe7ac86bc220454a50ce25d27ad65dd319eb111dbe3" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/destination/env": { + "sha256": "7ad5e1a1cccb631d1248434e7a4a4e0bd2b7a74369c3c3cf6724a6ff65423331" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/schemaVersion": { + "sha256": "f6d482dd2cad1932e8f6ed9f3a5514a5e20cd68d02237463125baf78013bd182" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/version": { + "sha256": "d05b37e57bc92debe8b4e38039876ab538a95a66caa835796dd5fbd439f0ba33" + } + } + } +} \ No newline at end of file diff --git a/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/machine.dd035ca2.link b/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/machine.dd035ca2.link new file mode 100644 index 00000000..15845c5d --- /dev/null +++ b/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/machine.dd035ca2.link @@ -0,0 +1,271 @@ +{ + "signatures": [ + { + "keyid": "dd035ca2388fffa593a9f0cb76f3ece27fe45560da0a5f93f5f404da2faac745", + "sig": "20657c441605117736e843ab4ea51a0d32e07404fea0300c1259a3a6756ebe21c17dcc8aa442ad525c46a520c4e0568e0e920fcf0f2a943248451e7d216d2f62c3668f461e814951ecab7e68fcdec050744bdd4fbc20fd5b3e211e8f535ae58e344375b0a44290c640ba83ca6bfe404acf12c3945650a551411076f18c567d190900452e3191f4a8bfe7203237db9b349b82ad0dc007770a61dbf20a657d62d4549da015f060ab6c875e980720b0dc26d49d17f488e1b5b946f2cf008d0c9328161821368c5935ced367add4fbcea2a22a8ec6495153ee186bffbb45ffeec6e6ef14de2af1686e4c8b759af883abbe0651bdbd43cd08d37903af98a702fc1a2ef85c5dd5f51572f84cd120ff6d6b91b740bd8888e9365ebf6c3e883f9fa3cb5635ec1ee6f1ac34896b79a41bbc96b4a689b43b22fc81ca6ed9e8386251d696dd1dfc50d52494034b74016e6517c5a3e5e3f435b5d878f31f383e75f8253ad69e87f51fd37e5f039e88a9a22456c48a0d92dbc28e02dabf581b0a6146efd93601" + } + ], + "signed": { + "_type": "link", + "byproducts": { + "return-value": 0, + "stderr": "", + "stdout": "" + }, + "command": [ + "./do-machine-stuff" + ], + "environment": {}, + "materials": { + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/credentials/hostkey/env": { + "sha256": "205c6e161cd52c8a5e28ac1a14a70ece07886438418ef1ed7ff431a3c59c55c2" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/credentials/hostkey/path": { + "sha256": "ea4278fd99a3cc839efd8d8675c2b754a6e556e0cd79e2dc10df03e9be071a56" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.backup-preferences/frequency": { + "sha256": "fa6a92cf80cf218ed717b3cc3a405ffda3fdf6a983d72fed1bdd113622fea03d" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.duffle-bag/icon": { + "sha256": "317d5a9514203bd1e19b80d39c846106513c7d20bd63271e8dbdb4821bff63b1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.duffle-bag/iconType": { + "sha256": "2775d1b05dce8419e4bab67f17fb5ed87eb0ca5b30232a04de1d574b99558200" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/default": { + "sha256": "48449a14a4ff7d79bb7a1b6f3d488eba397c36ef25634c111b49baf362511afc" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/maximum": { + "sha256": "205e7b780de08f42eda9fbf1715507758afd1f2eb499ffc6d0a352f904ee72f1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/minimum": { + "sha256": "4a44dc15364204a80fe80e9039455cc1608281820fe2b24f1e5233ade6af1dd5" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/type": { + "sha256": "bb894e256c101d183cfd51432eebb3feb183cf155da5994b19a45406a3e24aac" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/maximum": { + "sha256": "f2f89ede8e7d4b3d2243dea1ca96b8ece56f793811d9708b4a0181bf81a50011" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/minimum": { + "sha256": "e39eef82f61b21e2e7f762fcc4307358f165757f2e77ec855d6992f7e0191932" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/type": { + "sha256": "bb894e256c101d183cfd51432eebb3feb183cf155da5994b19a45406a3e24aac" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/string/type": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/contentEncoding": { + "sha256": "cb65d001ead1184d17391b55d1e96be555a573d9254cac717104f5d9d97e817c" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/contentMediaType": { + "sha256": "fc395c0b2477371951c95b71625d3919c73980dbb24aeca496578fc86a904add" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/type": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/writeOnly": { + "sha256": "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/description": { + "sha256": "12ccf5980e26a71d58ac3b877b24f82fdaede3e01446038585b5b201d4b83c55" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/contentDigest": { + "sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/description": { + "sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/image": { + "sha256": "be56793296fdb27458fd80666e5dcd1af52dd298760097ed7e73944e70e05118" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/contentDigest": { + "sha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/image": { + "sha256": "d33274172ff3405c8cbf2ebc6cbe2f3d5904a0d53d5521d0aa6a390124e51703" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/imageType": { + "sha256": "f320b810ea61ce6405c478a1a67befdcc117a307e62f17deb2417be2f98b1864" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/email": { + "sha256": "ea6b0d7bd10c06cbda2212bed77c5b2f9c0ef7b218afaf8e013f4466ba9f2ad1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/name": { + "sha256": "6682c39fd22bf39169f435916402584970df9a4d6ed72fc7ab7db46dca57d3e4" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/url": { + "sha256": "5775c078c67ebad469f37e1d888f3084804a19ce7511c28c74c54e8232a51bdb" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/name": { + "sha256": "7b91cf1e312a84544a3d5f82ce965daf5620579a4a5e7d37b90829238a2f1055" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/clientCert/definition": { + "sha256": "157c4a9d3e97adcdd2028934f7c21db802f1c90d2193ba902fb9cc17fd713402" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/clientCert/path": { + "sha256": "2c66367f304e04df32081f665b5e106d4835b8544b7999fbe5bc73cd7d16b94c" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/applyTo": { + "sha256": "2594d48ff5c9c3577770805617147fe8b06d206b22baa806c3fd21f352acd183" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/definition": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/description": { + "sha256": "da192c9f51c6dc131efe1418a1a408bbef863fd264cc488db48f947bb955efb4" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/path": { + "sha256": "fcc9b2b4e2eb66597d77def766e3ade74c37ff8f830bd0808303345a5ec80761" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/port/definition": { + "sha256": "1915cd692d3b19d814007446d9ab8c0a2cff60dc516035caba9cd00434802e94" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/port/path": { + "sha256": "5b6a3c0e9e60e2829fd2dce7c7f00d6db98ed9e84f43dbd407e9ca67de3e2413" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/definition": { + "sha256": "b70797c6a59d071c0171317fc593cfdda53d35cf1cd7d9fffaca351c84468b05" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/description": { + "sha256": "06b809960a94debf9253cbe7ac86bc220454a50ce25d27ad65dd319eb111dbe3" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/destination/env": { + "sha256": "7ad5e1a1cccb631d1248434e7a4a4e0bd2b7a74369c3c3cf6724a6ff65423331" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/schemaVersion": { + "sha256": "f6d482dd2cad1932e8f6ed9f3a5514a5e20cd68d02237463125baf78013bd182" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/version": { + "sha256": "d05b37e57bc92debe8b4e38039876ab538a95a66caa835796dd5fbd439f0ba33" + } + }, + "name": "machine", + "products": { + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/credentials/hostkey/env": { + "sha256": "205c6e161cd52c8a5e28ac1a14a70ece07886438418ef1ed7ff431a3c59c55c2" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/credentials/hostkey/path": { + "sha256": "ea4278fd99a3cc839efd8d8675c2b754a6e556e0cd79e2dc10df03e9be071a56" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.backup-preferences/frequency": { + "sha256": "fa6a92cf80cf218ed717b3cc3a405ffda3fdf6a983d72fed1bdd113622fea03d" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.duffle-bag/icon": { + "sha256": "317d5a9514203bd1e19b80d39c846106513c7d20bd63271e8dbdb4821bff63b1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/custom/com.example.duffle-bag/iconType": { + "sha256": "2775d1b05dce8419e4bab67f17fb5ed87eb0ca5b30232a04de1d574b99558200" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/default": { + "sha256": "48449a14a4ff7d79bb7a1b6f3d488eba397c36ef25634c111b49baf362511afc" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/maximum": { + "sha256": "205e7b780de08f42eda9fbf1715507758afd1f2eb499ffc6d0a352f904ee72f1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/minimum": { + "sha256": "4a44dc15364204a80fe80e9039455cc1608281820fe2b24f1e5233ade6af1dd5" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/http_port/type": { + "sha256": "bb894e256c101d183cfd51432eebb3feb183cf155da5994b19a45406a3e24aac" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/maximum": { + "sha256": "f2f89ede8e7d4b3d2243dea1ca96b8ece56f793811d9708b4a0181bf81a50011" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/minimum": { + "sha256": "e39eef82f61b21e2e7f762fcc4307358f165757f2e77ec855d6992f7e0191932" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/port/type": { + "sha256": "bb894e256c101d183cfd51432eebb3feb183cf155da5994b19a45406a3e24aac" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/string/type": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/contentEncoding": { + "sha256": "cb65d001ead1184d17391b55d1e96be555a573d9254cac717104f5d9d97e817c" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/contentMediaType": { + "sha256": "fc395c0b2477371951c95b71625d3919c73980dbb24aeca496578fc86a904add" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/type": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/definitions/x509Certificate/writeOnly": { + "sha256": "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/description": { + "sha256": "12ccf5980e26a71d58ac3b877b24f82fdaede3e01446038585b5b201d4b83c55" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/contentDigest": { + "sha256": "a9b0c315f1bc108882e4488d1d9320264259585a2af6e9ebdfae4a3191262b51" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/description": { + "sha256": "a3ff9793ffc0d9ae43064176db5add9cd33c579001cdf90f792b7ca9a0e004a7" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/images/my-microservice/image": { + "sha256": "be56793296fdb27458fd80666e5dcd1af52dd298760097ed7e73944e70e05118" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/contentDigest": { + "sha256": "8aca5133332d02975e86c282acc4d55b1afdab3b25f8fd8b3ec5cf2ff8362f99" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/image": { + "sha256": "d33274172ff3405c8cbf2ebc6cbe2f3d5904a0d53d5521d0aa6a390124e51703" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/invocationImages/[0]/imageType": { + "sha256": "f320b810ea61ce6405c478a1a67befdcc117a307e62f17deb2417be2f98b1864" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/email": { + "sha256": "ea6b0d7bd10c06cbda2212bed77c5b2f9c0ef7b218afaf8e013f4466ba9f2ad1" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/name": { + "sha256": "6682c39fd22bf39169f435916402584970df9a4d6ed72fc7ab7db46dca57d3e4" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/maintainers/[0]/url": { + "sha256": "5775c078c67ebad469f37e1d888f3084804a19ce7511c28c74c54e8232a51bdb" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/name": { + "sha256": "7b91cf1e312a84544a3d5f82ce965daf5620579a4a5e7d37b90829238a2f1055" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/clientCert/definition": { + "sha256": "157c4a9d3e97adcdd2028934f7c21db802f1c90d2193ba902fb9cc17fd713402" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/clientCert/path": { + "sha256": "2c66367f304e04df32081f665b5e106d4835b8544b7999fbe5bc73cd7d16b94c" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/applyTo": { + "sha256": "2594d48ff5c9c3577770805617147fe8b06d206b22baa806c3fd21f352acd183" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/definition": { + "sha256": "e9e5c1c9e4f6277339d1bcde0733a59bd42f8731f449da6dc13010a916930d48" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/description": { + "sha256": "da192c9f51c6dc131efe1418a1a408bbef863fd264cc488db48f947bb955efb4" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/hostName/path": { + "sha256": "fcc9b2b4e2eb66597d77def766e3ade74c37ff8f830bd0808303345a5ec80761" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/port/definition": { + "sha256": "1915cd692d3b19d814007446d9ab8c0a2cff60dc516035caba9cd00434802e94" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/outputs/port/path": { + "sha256": "5b6a3c0e9e60e2829fd2dce7c7f00d6db98ed9e84f43dbd407e9ca67de3e2413" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/definition": { + "sha256": "b70797c6a59d071c0171317fc593cfdda53d35cf1cd7d9fffaca351c84468b05" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/description": { + "sha256": "06b809960a94debf9253cbe7ac86bc220454a50ce25d27ad65dd319eb111dbe3" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/parameters/backend_port/destination/env": { + "sha256": "7ad5e1a1cccb631d1248434e7a4a4e0bd2b7a74369c3c3cf6724a6ff65423331" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/schemaVersion": { + "sha256": "f6d482dd2cad1932e8f6ed9f3a5514a5e20cd68d02237463125baf78013bd182" + }, + "cnab+json:/home/saky/Work/secure-systems-lab/cnab-ite-4/bundle.json$/version": { + "sha256": "d05b37e57bc92debe8b4e38039876ab538a95a66caa835796dd5fbd439f0ba33" + } + } + } +} diff --git a/testdata/intoto/minimal/root.layout b/testdata/intoto/minimal/root.layout new file mode 100644 index 00000000..91f9f4b1 --- /dev/null +++ b/testdata/intoto/minimal/root.layout @@ -0,0 +1,118 @@ +{ + "signatures": [ + { + "keyid": "737c25cc5ed977df2979fe029909d2d36b96adb4a65b9687fb8d150eaa29a4d8", + "sig": "27d2565297b39f1d8a95463c4b55d3e7c710737bd075244504e3eeaad2cc8b0bd2064ce7d346064eefbbd1fe8444274972a8010043f03bcbd9c6a03b85047e4cd3dce40452482e6568fb8c1b6e40bc4dd627ee82e852aac87efba195b07ba3adabd05ba992864ab1decfd3b77f77fd9207b3a504b481ba99e09de72ae3df8a20755cc2597312823aab99582bb438012843d3dcd809ed739ad885733271c121a4cb7614306a4c9486443af6c8430b6a0602f915cc33fcd0f634266ab0f49e7d2c27d1d64f8052202a323e0018ff9920de471d0b55759d3ac0b13cf5665ff0b19ac240b1af2214520e0fa0ff237930f3037fb840ad80d99b8f99a47ae529443749ea2b430021cfedaa4654274701b20e5bc6fa9ed943e4e46b24a197195ed01d4395b08ee3739fc4e0456cb48502696e9d5aa2b4d5d6b3fef61a8872e6a13041a9c2d266a8486432533c28cb3aff453b2ada9cc0ddf5762dc252fbbdab0e71e3fb52e41e0daba4fcb04ec90013f2579572974ac36f50659de6ad6d0bc17141e420" + } + ], + "signed": { + "_type": "layout", + "expires": "2021-05-12T11:15:05Z", + "inspect": [], + "keys": { + "2d2e5437de978b123ed8a50692cdb38c198ced0f1f356398abe9dd5206e890e2": { + "keyid": "2d2e5437de978b123ed8a50692cdb38c198ced0f1f356398abe9dd5206e890e2", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA4X062OOJPtd//g8acQ07\n+3xnCMCsQkMkOhJlaCWkxCFqSI8bUQrzRg1jfpCRCqzhEscTy/Vw/xWNvhgGOzcc\nYyCpnwDcr3CK3G58lVz2mnSyAW9PCOGvwgLfKMC7Z99q0tLRa3YYqzrajgYsL9D2\nPh+4DrlbAADfQIok7MkkjBqvKsFptr6N+KsB3pkQyGZUYAMdUUiQU8BV2oWNfJQo\n0PHW5fi5hfBCUGzDQB9U22fhD3I6sgKVfA+948mo+Y9QRp53WvmjR47xb5KjNJR8\niIq8htwXIe7JCJxcyEh7hjKE5E6Qm9OhN2e99uta9XiIX1hIqSdh+BcRuBb1SbJD\nX3OBqqZHtUJ9hiNLz+PmlMwLS+02sqIQud4Vhjfy83KnfOoZ0PRU9sUSQDDe/wnK\nC7PWwCfyBto7wf9EWOrRjlG/DwIPiRsxrZ2XhHJu3x4jjXjrkT6MIPWZESJvonZM\n5XIekFjaYt/mnPybuwMZCulAwQZ7ucJqf9zJUFhRYgZRAgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + }, + "dd035ca2388fffa593a9f0cb76f3ece27fe45560da0a5f93f5f404da2faac745": { + "keyid": "dd035ca2388fffa593a9f0cb76f3ece27fe45560da0a5f93f5f404da2faac745", + "keyid_hash_algorithms": [ + "sha256", + "sha512" + ], + "keytype": "rsa", + "keyval": { + "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAqxqOAKWUWeiEBNtH1Nx2\n/8IKsSpbxy9NoTT31uTVK57oX0EomrBuCY4Ec6kcBWDPcqp6Z+2Z9baVzRB08SzO\nIJ3tGsTTdmJaFXFXjrDix/AioNmpCFh0ditOYoxUFu+skYV7PoHQ3BH7A5m2YxdH\n+KiLh2ccCvNnL/Wdio/6hwyVp4lZMFgQdhi7ba5EzuzwLt6dkB+9Fle2PMgeF+od\n/Ly7oXXaUi5lbPNLzf4Dkhr02qeKLFap35hBqXjqcx68vOfjuYFWwFaHmkCPjDpf\nTaLbPnDtXVWM8HzHdb4lVrZCpwpj859/QuDGb+uJyto3BsSHMli2v5e9QKcxtOzn\n6c5YtQkfL7u/n/vM3WuMsq78mFLoBNzHWrEsKp1ZKtBQDXfKDaW2io8BiWlREseh\nbk9ekBEvh8BmjBPS7A62z389kjUKZYhfaRSsaLGkUv0CnRCnuEQxCHpIUztHGiRx\nNpWz3dgp3t6+f/ZNC+ro8+FoHDGzEHX42oxD25E2H16BAgMBAAE=\n-----END PUBLIC KEY-----" + }, + "scheme": "rsassa-pss-sha256" + } + }, + "readme": "", + "steps": [ + { + "_type": "step", + "expected_command": [], + "expected_materials": [ + [ + "DISALLOW", + "*" + ] + ], + "expected_products": [ + [ + "CREATE", + "cnab+json:*/bundle.json$*" + ], + [ + "DISALLOW", + "*" + ] + ], + "name": "developer", + "pubkeys": [ + "2d2e5437de978b123ed8a50692cdb38c198ced0f1f356398abe9dd5206e890e2" + ], + "threshold": 1 + }, + { + "_type": "step", + "expected_command": [ + "./do-machine-stuff" + ], + "expected_materials": [ + [ + "MATCH", + "cnab+json:*/bundle.json$*", + "WITH", + "PRODUCTS", + "FROM", + "developer" + ], + [ + "DISALLOW", + "*" + ] + ], + "expected_products": [ + [ + "MODIFY", + "cnab+json:*/bundle.json$/images/*/contentDigest" + ], + [ + "MODIFY", + "cnab+json:*/bundle.json$/images/*/description" + ], + [ + "MODIFY", + "cnab+json:*/bundle.json$/invocationImages/*/contentDigest" + ], + [ + "MATCH", + "cnab+json:*/bundle.json$*", + "WITH", + "MATERIALS", + "FROM", + "machine" + ], + [ + "DISALLOW", + "*" + ] + ], + "name": "machine", + "pubkeys": [ + "dd035ca2388fffa593a9f0cb76f3ece27fe45560da0a5f93f5f404da2faac745" + ], + "threshold": 1 + } + ] + } +} \ No newline at end of file diff --git a/testdata/intoto/minimal/root.layout.pub b/testdata/intoto/minimal/root.layout.pub new file mode 100644 index 00000000..34d7cf65 --- /dev/null +++ b/testdata/intoto/minimal/root.layout.pub @@ -0,0 +1,11 @@ +-----BEGIN PUBLIC KEY----- +MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA1uHXuCIQKFHzPv8nM/QJ +qROdJz9Fx+wmPMaTfXxHXVt93CLdsn1JYPuLQEdhUA4s1AgOnfNYr4mEYNlCJ/lV +j4Ilt6QBloqKMqWzxi1KAEEnX/o6WQhMPZdvR3lZADwh/57fJMRyFpKQT4adv4kx +FAFVn0nUSrZj8PtSZw84Jt0ghSh1heEv9Cjza8Tf1a6XiGhsrhvU/ADWFN6ZmLdW +mscnouwtro03ic9DkyrhZYj3LnbOEvQ7zkt6tzrdgkzh925BvU8whxItrNrThX6n +4WaIJ2BUy2ZAxSrNXIWs2mW4mXmKuUs0q2477+Bh0Wa8RDmIKqW28ITxCarLrdfX +DDkm7WpDNZLfWBmQwyZaDUCpZwuQ/vsEDyNFfAk1f57drp91ACoZO68lB0f01Qu0 +LFOXOonXBmcdgC2spNTQPP7GZz861aa4nFK35JpWaT3N/9D861qjJ9XVYCEe0hK6 +rR9yKZfjBfosDH02KwCcNLvHElDip6U6yPMZKogs5w8/AgMBAAE= +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/testdata/intoto/package.2f89b927.link b/testdata/intoto/package.2f89b927.link deleted file mode 100644 index e7bde586..00000000 --- a/testdata/intoto/package.2f89b927.link +++ /dev/null @@ -1,34 +0,0 @@ -{ - "signatures": [ - { - "keyid": "2f89b9272acfc8f4a0a0f094d789fdb0ba798b0fe41f2f5f417c12f0085ff498", - "sig": "66365d379d66a2e76d39a1f048847826393127572ba43bead96419499b02561a08e1cb06cf91f2addd87c30a01f776a8ccc599574bc9a2bd519558351f56cffa61ac4f994d0d491204ff54707937e15f9abfa97c5bda1ec1ae2a2afea63f808613f4fb343b85a5a455b668b95fa3a11cb9b34219d4d6af2dd4e80a9af01023954a8813b510a6ff6041c3af52056d021fabbc975211b0d8ee7a429a6c22efde583d8ac0719fd657b398a3e02cc711897acbe8cadf32d54f47012aa44621728ede42c3bc95c662f9c1211df4e18da8e0f6b2de358700cea5db1e76fc61ef5a90bcebcc883eed2272e5ca1c8cbb09b868613b839266cd3ae346ce88439bdb5bb4c69dcb7398f4373f2b051adb3d44d11ef1b70c7189aa5c0e6906bf7be1228dc553390024c9c796316067fda7d63cf60bfac86ef2e13bbd8e4c3575683673f7cdf4639c3a5dc225fc0c040dbd9962a6ff51913b240544939ce2d32a5e84792c0acfa94ee07e88e474bf4937558d107c6ecdef5b5b3a7f3a44a657662bbc1046df3a" - } - ], - "signed": { - "_type": "link", - "byproducts": { - "return-value": 0, - "stderr": "a foo.py\n", - "stdout": "" - }, - "command": [ - "tar", - "zcvf", - "foo.tar.gz", - "foo.py" - ], - "environment": {}, - "materials": { - "foo.py": { - "sha256": "74dc3727c6e89308b39e4dfedf787e37841198b1fa165a27c013544a60502549" - } - }, - "name": "package", - "products": { - "foo.tar.gz": { - "sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355" - } - } - } -} \ No newline at end of file diff --git a/testdata/intoto/root.layout b/testdata/intoto/root.layout deleted file mode 100644 index 6812818d..00000000 --- a/testdata/intoto/root.layout +++ /dev/null @@ -1,136 +0,0 @@ -{ - "signatures": [ - { - "keyid": "556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35", - "sig": "02813858670c66647c17802d84f06453589f41850013a544609e9d33ba21fa19280e8371701f8274fb0c56bd95ff4f34c418456b002af9836ca218b584f51eb0eaacbb1c9bb57448101b07d058dec04d525551d157f6ae5e3679701735b1b8f52430f9b771d5476db1a2053cd93e2354f20061178a01705f2fa9ac82c7aeca4dd830e2672eb227127178d52328747ac819e50ec8ff52c662d7a4c58f5040d8f655fe595804f3e47c4fc98434c44e914445f7cb773439ebf813de8849dd1b533958f99f671d4e023d34c110d4b169cc02c12a3755ebe537147ff2479d244daaf719e24cf6b2fa6f47d0410d52d67217bcf4d4d4c2c7c0b92cd2bcd321edc69bc1430f78a188e712b8cb1fff0c14550cd01c41dae377256f31211fd249c5031bfee86e638bce6aa36aca349b787cef48255b0ef04bd0a21adb37b2a3da888d1530ca6ddeae5261e6fd65aa626d5caebbfae2986f842bd2ce94bcefe5dd0ae9c5b2028a15bd63bbea61be732207f0f5b58d056f118c830981747cb2b245d1377e17" - } - ], - "signed": { - "_type": "layout", - "expires": "2020-11-18T16:06:36Z", - "inspect": [ - { - "_type": "inspection", - "expected_materials": [ - [ - "MATCH", - "foo.tar.gz", - "WITH", - "PRODUCTS", - "FROM", - "package" - ], - [ - "DISALLOW", - "foo.tar.gz" - ] - ], - "expected_products": [ - [ - "MATCH", - "foo.py", - "WITH", - "PRODUCTS", - "FROM", - "write-code" - ], - [ - "DISALLOW", - "foo.py" - ] - ], - "name": "untar", - "run": [ - "tar", - "xfz", - "foo.tar.gz" - ] - } - ], - "keys": { - "2f89b9272acfc8f4a0a0f094d789fdb0ba798b0fe41f2f5f417c12f0085ff498": { - "keyid": "2f89b9272acfc8f4a0a0f094d789fdb0ba798b0fe41f2f5f417c12f0085ff498", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "rsa", - "keyval": { - "private": "", - "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAzgLBsMFSgwBiWTBmVsyW\n5KbJwLFSodAzdUhU2Bq6SdRz/W6UOBGdojZXibxupjRtAaEQW/eXDe+1CbKg6ENZ\nGt2D9HGFCQZgQS8ONgNDQGiNxgApMA0T21AaUhru0vEofzdN1DfEF4CAGv5AkcgK\nsalhTyONervFIjFEdXGelFZ7dVMV3Pp5WkZPG0jFQWjnmDZhUrtSxEtqbVghc3kK\nAUj9Ll/3jyi2wS92Z1j5ueN8X62hWX2xBqQ6nViOMzdujkoiYCRSwuMLRqzW2CbT\nL8hF1+S5KWKFzxl5sCVfpPe7V5HkgEHjwCILXTbCn2fCMKlaSbJ/MG2lW7qSY2Ro\nwVXWkp1wDrsJ6Ii9f2dErv9vJeOVZeO9DsooQ5EuzLCfQLEU5mn7ul7bU7rFsb8J\nxYOeudkNBatnNCgVMAkmDPiNA7E33bmL5ARRwU0iZicsqLQR32pmwdap8PjofxqQ\nk7Gtvz/iYzaLrZv33cFWWTsEOqK1gKqigSqgW9T26wO9AgMBAAE=\n-----END PUBLIC KEY-----" - }, - "scheme": "rsassa-pss-sha256" - }, - "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5": { - "keyid": "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5", - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "rsa", - "keyval": { - "private": "", - "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0Zfzonp3/FScaIP+KKuz\nB+OZNFpjbVGWjm3leqnFqHYLqrLcCw5KhlXpycJqoSvZBpO+PFCksUx8U/ryklHG\nVoDiB84pRkvZtBoVaA4b4IHDIhz1K5NqkJgieya4fwReTxmCW0a9gH7AnDicHBCX\nlzMxqEdt6OKMV5g4yjKaxf8lW72O1gSI46GSIToo+Z7UUgs3ofaM5UFIcczgCpUa\n5kEKocB6cSZ9U8PKRLSs0xO0ROjrcOTsfxMs8eV4bsRCWY5mAq1WM9EHDSV9WO8g\nqrRmanC4enNqa8jU4O3zhgJVegP9A01r9AwNt6AqgPSikwhXN/P4v1FMYV+R6N3b\nS1lsVWRAnwBq5RFz5zVvcY88JEkHbrcBqP/A4909NXae1VMXmnoJb4EzGAkyUySB\na+fHXAVJgzwyv3I48d/OIjH8NWcVmM/DQL7FtcJk3tp0YUjY5wNpcbQTnLzURtlU\nsd+MtGuvdlDxUUvtUYCIVKRdS8UzYnTPjI2xzeoSHZ2ZAgMBAAE=\n-----END PUBLIC KEY-----" - }, - "scheme": "rsassa-pss-sha256" - } - }, - "readme": "", - "steps": [ - { - "_type": "step", - "expected_command": [], - "expected_materials": [], - "expected_products": [ - [ - "ALLOW", - "foo.py" - ] - ], - "name": "write-code", - "pubkeys": [ - "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5" - ], - "threshold": 1 - }, - { - "_type": "step", - "expected_command": [ - "tar", - "zcvf", - "foo.tar.gz", - "foo.py" - ], - "expected_materials": [ - [ - "MATCH", - "foo.py", - "WITH", - "PRODUCTS", - "FROM", - "write-code" - ], - [ - "DISALLOW", - "*" - ] - ], - "expected_products": [ - [ - "ALLOW", - "foo.tar.gz" - ], - [ - "ALLOW", - "foo.py" - ] - ], - "name": "package", - "pubkeys": [ - "2f89b9272acfc8f4a0a0f094d789fdb0ba798b0fe41f2f5f417c12f0085ff498" - ], - "threshold": 1 - } - ] - } -} \ No newline at end of file diff --git a/testdata/intoto/root.layout.pub b/testdata/intoto/root.layout.pub deleted file mode 100644 index 0e200eac..00000000 --- a/testdata/intoto/root.layout.pub +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxPX3kFs/z645x4UOC3KF -Y3V80YQtKrp6YS3qU+Jlvx/XzK53lb4sCDRU9jqBBx3We45TmFUibroMd8tQXCUS -e8gYCBUBqBmmz0dEHJYbW0tYF7IoapMIxhRYn76YqNdl1JoRTcmzIaOJ7QrHxQrS -GpivvTm6kQ9WLeApG1GLYJ3C3Wl4bnsI1bKSv55Zi45/JawHzTzYUAIXX9qCd3Io -HzDucz9IAj9Ookw0va/q9FjoPGrRB80IReVxLVnbo6pYJfu/O37jvEobHFa8ckHd -YxUIg8wvkIOy1O3M74lBDm6CVI0ZO25xPlDB/4nHAE1PbA3aF3lw8JGuxLDsetxm -fzgAleVt4vXLQiCrZaLf+0cM97JcT7wdHcbIvRLsij9LNP+2tWZgeZ/hIAOEdaDq -cYANPDIAxfTvbe9I0sXrCtrLer1SS7GqUmdFCdkdun8erXdNF0ls9Rp4cbYhjdf3 -yMxdI/24LUOOQ71cHW3ITIDImm6I8KmrXFM2NewTARKfAgMBAAE= ------END PUBLIC KEY----- diff --git a/testdata/intoto/write-code.776a00e2.link b/testdata/intoto/write-code.776a00e2.link deleted file mode 100644 index 1baf159c..00000000 --- a/testdata/intoto/write-code.776a00e2.link +++ /dev/null @@ -1,21 +0,0 @@ -{ - "signatures": [ - { - "keyid": "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5", - "sig": "ba2899895d769db034b898e27104a3f8d6cfca4f482c55fc68283cf34feede07966f9d6a32ea283215d373d8b3f2d5b7c7b02546f2cb55751eb347069cd4225da7d5829491aef18405380e21a74b04859c4db41e33133b2aae323c84e95d081a544d58940396f94d49fb718637025a3b7d0b9d4fb3d0ae2a604cb4f45c4cb001e20c411a9832851916b3a2e21d2e29a4d33300a848cd968b588bce7483f6f751ee95c2eb6c18c4e2978be6bdd9eb63b9436e434b38c7f37a7c9f444744a250470262e9ea408e65b96608a6b7748ead175d5b3894a20f258046c769e94e50da4145052dc9bb736196edcb86d79857437d4994a7ec9f022fae3c16645605ae03e4f3408f29818dcaec0ff3e513a5fab6906877ea5e4c92bfa0967913ce70ddeb598e73cb8799ce80d1bf2b88e53d1264f0a36d3549be86b2dff39592a59d4982176d04f5f2d7170a13f5f37ee471493be1eb9a30e002e77e9046b29aa1c1363ca46e4a2ae2f96dd780d0132469815e3fe1ab6db11b570e9411d0968482ac02d2cc" - } - ], - "signed": { - "_type": "link", - "byproducts": {}, - "command": [], - "environment": {}, - "materials": {}, - "name": "write-code", - "products": { - "foo.py": { - "sha256": "74dc3727c6e89308b39e4dfedf787e37841198b1fa165a27c013544a60502549" - } - } - } -} \ No newline at end of file From 15ad9356a7dade5f9bfeba62a82b01c6df3a97bf Mon Sep 17 00:00:00 2001 From: Radu M Date: Mon, 29 Jun 2020 15:53:35 -0700 Subject: [PATCH 4/8] Fix verification tests This commit updates the minimal in-toto root layout, removes an unused signature, as well as updates the directory structure, putting the links at the same level as the layout and public key. (the directory structure should be updated in the future, but for now, getting the tests to pass should suffice.) Finally, it updates `docker_test.go` and `os_test.go` accordingly. Signed-off-by: Radu M --- pkg/docker/docker_test.go | 2 +- pkg/intoto/os_test.go | 7 ++++--- ...d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub | 11 ----------- .../developer.2d2e5437.link | 0 .../machine.dd035ca2.link | 0 testdata/intoto/minimal/root.layout | 6 ++++-- 6 files changed, 9 insertions(+), 17 deletions(-) delete mode 100644 testdata/intoto/minimal/556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub rename testdata/intoto/minimal/{d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7 => }/developer.2d2e5437.link (100%) rename testdata/intoto/minimal/{d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7 => }/machine.dd035ca2.link (100%) diff --git a/pkg/docker/docker_test.go b/pkg/docker/docker_test.go index a6312d6c..2936d30e 100644 --- a/pkg/docker/docker_test.go +++ b/pkg/docker/docker_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" ) -var testDir = "../../testdata/intoto" +var testDir = "../../testdata/intoto/minimal" func TestRun(t *testing.T) { // NOTE: Tag will be empty since we cannot inject build-time variables during testing. diff --git a/pkg/intoto/os_test.go b/pkg/intoto/os_test.go index 8bdd6241..ef7ff5d6 100644 --- a/pkg/intoto/os_test.go +++ b/pkg/intoto/os_test.go @@ -2,6 +2,7 @@ package intoto import ( "os" + "path" "path/filepath" "testing" @@ -11,7 +12,7 @@ import ( var testDir = "../../testdata/intoto" func TestVerify(t *testing.T) { - err := verifyOnOS(testDir) + err := verifyOnOS(path.Join(testDir, "minimal")) assert.NoError(t, err) // the verification step generates a file called untar.link @@ -19,7 +20,7 @@ func TestVerify(t *testing.T) { } func TestValidate(t *testing.T) { - layoutPath := filepath.Join(testDir, "root.layout") + layoutPath := filepath.Join(testDir, "minimal", "root.layout") l, err := getLayout(layoutPath) assert.NoError(t, err) @@ -29,7 +30,7 @@ func TestValidate(t *testing.T) { } func TestValidateMalformed(t *testing.T) { - layoutPath := filepath.Join(testDir, "malformed.template") + layoutPath := filepath.Join(testDir, "malformed.root.layout") // we can load the file and unmarshal a layout l, err := getLayout(layoutPath) diff --git a/testdata/intoto/minimal/556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub b/testdata/intoto/minimal/556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub deleted file mode 100644 index 0e200eac..00000000 --- a/testdata/intoto/minimal/556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b588f3e9cc48b35.pub +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxPX3kFs/z645x4UOC3KF -Y3V80YQtKrp6YS3qU+Jlvx/XzK53lb4sCDRU9jqBBx3We45TmFUibroMd8tQXCUS -e8gYCBUBqBmmz0dEHJYbW0tYF7IoapMIxhRYn76YqNdl1JoRTcmzIaOJ7QrHxQrS -GpivvTm6kQ9WLeApG1GLYJ3C3Wl4bnsI1bKSv55Zi45/JawHzTzYUAIXX9qCd3Io -HzDucz9IAj9Ookw0va/q9FjoPGrRB80IReVxLVnbo6pYJfu/O37jvEobHFa8ckHd -YxUIg8wvkIOy1O3M74lBDm6CVI0ZO25xPlDB/4nHAE1PbA3aF3lw8JGuxLDsetxm -fzgAleVt4vXLQiCrZaLf+0cM97JcT7wdHcbIvRLsij9LNP+2tWZgeZ/hIAOEdaDq -cYANPDIAxfTvbe9I0sXrCtrLer1SS7GqUmdFCdkdun8erXdNF0ls9Rp4cbYhjdf3 -yMxdI/24LUOOQ71cHW3ITIDImm6I8KmrXFM2NewTARKfAgMBAAE= ------END PUBLIC KEY----- diff --git a/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/developer.2d2e5437.link b/testdata/intoto/minimal/developer.2d2e5437.link similarity index 100% rename from testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/developer.2d2e5437.link rename to testdata/intoto/minimal/developer.2d2e5437.link diff --git a/testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/machine.dd035ca2.link b/testdata/intoto/minimal/machine.dd035ca2.link similarity index 100% rename from testdata/intoto/minimal/d374df2f6946233546bb4ca97dcee3a01fe07aaef11be1fb09abd37ceb4ecfb7/machine.dd035ca2.link rename to testdata/intoto/minimal/machine.dd035ca2.link diff --git a/testdata/intoto/minimal/root.layout b/testdata/intoto/minimal/root.layout index 91f9f4b1..3268392f 100644 --- a/testdata/intoto/minimal/root.layout +++ b/testdata/intoto/minimal/root.layout @@ -2,7 +2,7 @@ "signatures": [ { "keyid": "737c25cc5ed977df2979fe029909d2d36b96adb4a65b9687fb8d150eaa29a4d8", - "sig": "27d2565297b39f1d8a95463c4b55d3e7c710737bd075244504e3eeaad2cc8b0bd2064ce7d346064eefbbd1fe8444274972a8010043f03bcbd9c6a03b85047e4cd3dce40452482e6568fb8c1b6e40bc4dd627ee82e852aac87efba195b07ba3adabd05ba992864ab1decfd3b77f77fd9207b3a504b481ba99e09de72ae3df8a20755cc2597312823aab99582bb438012843d3dcd809ed739ad885733271c121a4cb7614306a4c9486443af6c8430b6a0602f915cc33fcd0f634266ab0f49e7d2c27d1d64f8052202a323e0018ff9920de471d0b55759d3ac0b13cf5665ff0b19ac240b1af2214520e0fa0ff237930f3037fb840ad80d99b8f99a47ae529443749ea2b430021cfedaa4654274701b20e5bc6fa9ed943e4e46b24a197195ed01d4395b08ee3739fc4e0456cb48502696e9d5aa2b4d5d6b3fef61a8872e6a13041a9c2d266a8486432533c28cb3aff453b2ada9cc0ddf5762dc252fbbdab0e71e3fb52e41e0daba4fcb04ec90013f2579572974ac36f50659de6ad6d0bc17141e420" + "sig": "bc4a54ac4e7b52d6dfd36e8e0ca1b78e4f4e7d0c2b388ddd6cc738f599fc6d32b52fdbf2e2109e17debfa1a6d37969f217fb9e6f9622f980d33ca67b722ab7ef804497e8f9f070e1f4d338fb34adeb009f966f3aa42ba95d94ec8852c3b7c0399469552a99e5ddbc97368f587600468aa321887a92d7b45eba06a5a4bb932918987cbfe2ee41f88eeb614d35fbc84111c82d58faa2574a2f7b0861ab2652ce23f2a013b06e487fdf29d17ce50d1e6daace0d36657c1b292a6c3065efe6071fd24267137859e49456d69dc6cd344fd4f44a98e6a796c2ac49dbde38458d7f04ddfde1a6c64df634aece3620768828433d3f2f0b381e2956226036cdb68d94c6bdd044ca4732bb136addc037ceb43b7a2d8fa2c536a326ae8304067daa3e59e979ac0f1a079b14c66de5afb6f8d50a241c366d4fccd07dc792713e5b3e2a6f3870971c8aace1b2137341168693135c75e3d341eaa39d33cee4b83336c732fd3c305654b9c9f794d5839464455a3e9cc3a6ca4dc2ca0064d668ef392184f5933ed1" } ], "signed": { @@ -18,6 +18,7 @@ ], "keytype": "rsa", "keyval": { + "private": "", "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA4X062OOJPtd//g8acQ07\n+3xnCMCsQkMkOhJlaCWkxCFqSI8bUQrzRg1jfpCRCqzhEscTy/Vw/xWNvhgGOzcc\nYyCpnwDcr3CK3G58lVz2mnSyAW9PCOGvwgLfKMC7Z99q0tLRa3YYqzrajgYsL9D2\nPh+4DrlbAADfQIok7MkkjBqvKsFptr6N+KsB3pkQyGZUYAMdUUiQU8BV2oWNfJQo\n0PHW5fi5hfBCUGzDQB9U22fhD3I6sgKVfA+948mo+Y9QRp53WvmjR47xb5KjNJR8\niIq8htwXIe7JCJxcyEh7hjKE5E6Qm9OhN2e99uta9XiIX1hIqSdh+BcRuBb1SbJD\nX3OBqqZHtUJ9hiNLz+PmlMwLS+02sqIQud4Vhjfy83KnfOoZ0PRU9sUSQDDe/wnK\nC7PWwCfyBto7wf9EWOrRjlG/DwIPiRsxrZ2XhHJu3x4jjXjrkT6MIPWZESJvonZM\n5XIekFjaYt/mnPybuwMZCulAwQZ7ucJqf9zJUFhRYgZRAgMBAAE=\n-----END PUBLIC KEY-----" }, "scheme": "rsassa-pss-sha256" @@ -30,6 +31,7 @@ ], "keytype": "rsa", "keyval": { + "private": "", "public": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAqxqOAKWUWeiEBNtH1Nx2\n/8IKsSpbxy9NoTT31uTVK57oX0EomrBuCY4Ec6kcBWDPcqp6Z+2Z9baVzRB08SzO\nIJ3tGsTTdmJaFXFXjrDix/AioNmpCFh0ditOYoxUFu+skYV7PoHQ3BH7A5m2YxdH\n+KiLh2ccCvNnL/Wdio/6hwyVp4lZMFgQdhi7ba5EzuzwLt6dkB+9Fle2PMgeF+od\n/Ly7oXXaUi5lbPNLzf4Dkhr02qeKLFap35hBqXjqcx68vOfjuYFWwFaHmkCPjDpf\nTaLbPnDtXVWM8HzHdb4lVrZCpwpj859/QuDGb+uJyto3BsSHMli2v5e9QKcxtOzn\n6c5YtQkfL7u/n/vM3WuMsq78mFLoBNzHWrEsKp1ZKtBQDXfKDaW2io8BiWlREseh\nbk9ekBEvh8BmjBPS7A62z389kjUKZYhfaRSsaLGkUv0CnRCnuEQxCHpIUztHGiRx\nNpWz3dgp3t6+f/ZNC+ro8+FoHDGzEHX42oxD25E2H16BAgMBAAE=\n-----END PUBLIC KEY-----" }, "scheme": "rsassa-pss-sha256" @@ -115,4 +117,4 @@ } ] } -} \ No newline at end of file +} From 0a87d1b11e9d14007c9ce59d7612672647f50674 Mon Sep 17 00:00:00 2001 From: Trishank Karthik Kuppusamy Date: Tue, 30 Jun 2020 17:12:06 -0400 Subject: [PATCH 5/8] address some review comments Signed-off-by: Trishank Karthik Kuppusamy --- pkg/intoto/metadata.go | 6 +++--- pkg/tuf/common.go | 6 ------ pkg/tuf/common_test.go | 1 - pkg/tuf/sign.go | 2 +- 4 files changed, 4 insertions(+), 11 deletions(-) delete mode 100644 pkg/tuf/common_test.go diff --git a/pkg/intoto/metadata.go b/pkg/intoto/metadata.go index d14e5f83..4166e114 100644 --- a/pkg/intoto/metadata.go +++ b/pkg/intoto/metadata.go @@ -12,13 +12,13 @@ import ( // Metadata points to root layout, its public keys, and/or links type Metadata struct { Data []byte `json:"data"` - PublicKeys []string `json:"public-keys"` // filenames - Links []string `json:"links"` // filenames + PublicKeys []string `json:"pubkeys"` // filenames + Links []string `json:"links"` // filenames } // Custom is a generic structure that contains in-toto Metadata type Custom struct { - InToto Metadata `json:"in-toto"` + InToto Metadata `json:"intoto"` } // PublicKeys is a map from the GUN-qualified filename diff --git a/pkg/tuf/common.go b/pkg/tuf/common.go index 1fefdfc1..ebfa5fd7 100644 --- a/pkg/tuf/common.go +++ b/pkg/tuf/common.go @@ -1,9 +1,3 @@ -// Most of the helper functions are adapted from github.com/theupdateframework/notary -// -// Figure out the proper way of making sure we are respecting the licensing from Notary -// While we are also vendoring Notary directly (see LICENSE in vendor/github.com/theupdateframework/notary/LICENSE), -// copying unexported functions could fall under different licensing, so we need to make sure. - package tuf import ( diff --git a/pkg/tuf/common_test.go b/pkg/tuf/common_test.go deleted file mode 100644 index cbd1b4a0..00000000 --- a/pkg/tuf/common_test.go +++ /dev/null @@ -1 +0,0 @@ -package tuf diff --git a/pkg/tuf/sign.go b/pkg/tuf/sign.go index 4152543b..97ccce0f 100644 --- a/pkg/tuf/sign.go +++ b/pkg/tuf/sign.go @@ -185,7 +185,7 @@ func reuseKeys(repo client.Repository, rootKey string) error { } default: - return fmt.Errorf("cannot list targets: %v", err) + return fmt.Errorf("cannot list targets while reusing keys: %v", err) } } return nil From 146e4e4ed3202985fc0e5a1d63b80a7d6d555a4a Mon Sep 17 00:00:00 2001 From: Trishank Karthik Kuppusamy Date: Wed, 14 Jul 2021 15:39:46 -0400 Subject: [PATCH 6/8] WIP on verifying in-toto metadata --- cmd/verify.go | 6 ++++++ pkg/tuf/list.go | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/cmd/verify.go b/cmd/verify.go index ab87ea9d..1928f913 100644 --- a/cmd/verify.go +++ b/cmd/verify.go @@ -110,6 +110,12 @@ func (v *verifyCmd) run() error { } if v.intoto { + // TODO: Put back TUF verification of in-toto metadata. + err = tuf.VerifyInTotoMetadata(v.ref, trustServer, tlscacert, trustDir, timeout) + if err != nil { + return err + } + if v.verifyOnOS { log.Warn("Running in-toto inspections on the OS instead of in container...") return intoto.VerifyOnOS(target, bundle) diff --git a/pkg/tuf/list.go b/pkg/tuf/list.go index e310c8ec..f49d2a17 100644 --- a/pkg/tuf/list.go +++ b/pkg/tuf/list.go @@ -25,8 +25,39 @@ func GetTargetWithRole(gun, targetName, trustServer, tlscacert, trustDir, timeou return nil, fmt.Errorf("cannot find target %v in trusted collection %v", targetName, gun) } +// VerifyInToto ensures that the in-toto root layout, pubkeys, and links match the TUF metadata +func VerifyInTotoMetadata(gun, trustServer, tlscacert, trustDir, timeout string) error { + // TODO: get targets from releases + targets, err := GetTargets(gun, trustServer, tlscacert, trustDir, timeout, releasesRoleName) + if err != nil { + return fmt.Errorf("cannot list %v :%v", releasesRoleName, err) + } + // TODO: verify that releases signs ONLY links + for _, target := range targets { + if target.Role.String() != releasesRoleName.String() { + return fmt.Errorf("expected %v but got :%v", releasesRoleName, target.Role.String()) + } + // TODO: check the target name matches links + // TODO: check the hash and length matches + } + + // TODO: get targets from targets + targets, err = GetTargets(gun, trustServer, tlscacert, trustDir, timeout) + if err != nil { + return fmt.Errorf("cannot list targets: %v", err) + } + // TODO: verify that targets signs ONLY layouts and pubkeys + for _, target := range targets { + if target.Name == targetName { + return target, nil + } + } + + return nil +} + // GetTargets returns all targets for a given gun from the trusted collection -func GetTargets(gun, trustServer, tlscacert, trustDir, timeout string) ([]*client.TargetWithRole, error) { +func GetTargets(gun, trustServer, tlscacert, trustDir, timeout string, roles ...data.RoleName) ([]*client.TargetWithRole, error) { if err := EnsureTrustDir(trustDir); err != nil { return nil, fmt.Errorf("cannot ensure trust directory: %v", err) } @@ -48,7 +79,7 @@ func GetTargets(gun, trustServer, tlscacert, trustDir, timeout string) ([]*clien return nil, fmt.Errorf("cannot create new file cached repository: %v", err) } - return repo.ListTargets() + return repo.ListTargets(roles...) } // PrintTargets prints all the targets for a specific GUN from a trust server From b6f8f56225a5c688a98e7fa8f7707e1750202289 Mon Sep 17 00:00:00 2001 From: Trishank Karthik Kuppusamy Date: Wed, 21 Jul 2021 14:29:52 -0400 Subject: [PATCH 7/8] WIP on verifying TUF metadata about in-toto metadata --- pkg/intoto/metadata.go | 10 ++++++++ pkg/tuf/delegations.go | 14 +++++++---- pkg/tuf/list.go | 57 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/pkg/intoto/metadata.go b/pkg/intoto/metadata.go index 4166e114..9de82477 100644 --- a/pkg/intoto/metadata.go +++ b/pkg/intoto/metadata.go @@ -48,6 +48,16 @@ func (custom *Custom) GetRawMessage() (canonicaljson.RawMessage, error) { return canonicaljson.RawMessage(marshalled), nil } +// ReadRawMessage reads a raw Canonical JSON message into this Custom +func (custom *Custom) ReadRawMessage(rm *canonicaljson.RawMessage) error { + // TODO: check that the types/deferencing are correct + err := canonicaljson.Unmarshal(*rm, custom) + if err != nil { + return fmt.Errorf("cannot decode canonical json into custom metadata: %v", err) + } + return nil +} + // GetPublicKeys reads public keys off disk into a PublicKeys map func GetPublicKeys(gun string, filenames ...string) (PublicKeys, error) { publicKeys := make(PublicKeys) diff --git a/pkg/tuf/delegations.go b/pkg/tuf/delegations.go index 77c57fb5..1fd5984a 100644 --- a/pkg/tuf/delegations.go +++ b/pkg/tuf/delegations.go @@ -7,17 +7,21 @@ import ( "github.com/theupdateframework/notary/tuf/data" ) +func getReleasesPathPattern(gun string) []string { + paths := make([]string, 2) + tags := gun + ":" + links := gun + "/in-toto-links/" + paths = append(paths, tags, links) + return paths +} + // Delegate all paths ("*") to targets/releases. // https://github.com/theupdateframework/notary/blob/f255ae779066dc28ae4aee196061e58bb38a2b49/cmd/notary/delegations.go func delegateToReleases(repo client.Repository, publicKey data.PublicKey) error { // the public keys used to verify the delegatee publicKeys := []data.PublicKey{publicKey} // the target paths entrusted to the delegatee - paths := make([]string, 2) - gun := repo.GetGUN().String() - tags := gun + ":" - links := gun + "/in-toto-links/" - paths = append(paths, tags, links) + paths := getReleasesPathPattern(repo.GetGUN().String()) // Add the delegation to the repository err := repo.AddDelegation(releasesRoleName, publicKeys, paths) diff --git a/pkg/tuf/list.go b/pkg/tuf/list.go index f49d2a17..b4d71891 100644 --- a/pkg/tuf/list.go +++ b/pkg/tuf/list.go @@ -1,9 +1,12 @@ package tuf import ( + "bytes" "encoding/hex" "fmt" + "strings" + "github.com/cnabio/signy/pkg/intoto" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/trustpinning" "github.com/theupdateframework/notary/tuf/data" @@ -27,30 +30,68 @@ func GetTargetWithRole(gun, targetName, trustServer, tlscacert, trustDir, timeou // VerifyInToto ensures that the in-toto root layout, pubkeys, and links match the TUF metadata func VerifyInTotoMetadata(gun, trustServer, tlscacert, trustDir, timeout string) error { - // TODO: get targets from releases + // get targets from releases targets, err := GetTargets(gun, trustServer, tlscacert, trustDir, timeout, releasesRoleName) if err != nil { return fmt.Errorf("cannot list %v :%v", releasesRoleName, err) } - // TODO: verify that releases signs ONLY links + + // verify that releases signs ONLY links + // the target paths entrusted to the delegatee + paths := getReleasesPathPattern(gun) for _, target := range targets { if target.Role.String() != releasesRoleName.String() { return fmt.Errorf("expected %v but got :%v", releasesRoleName, target.Role.String()) } - // TODO: check the target name matches links - // TODO: check the hash and length matches + // check the target name matches links + match := false + for _, path := range paths { + if strings.HasPrefix(target.Name, path) { + match = true + break + } + } + if !match { + return fmt.Errorf("%v does not match %v", target.Name, paths) + } + // check the hash and length matches + custom := intoto.Custom{} + custom.ReadRawMessage(target.Custom) + linkMeta, err := data.NewFileMeta(bytes.NewBuffer(custom.InToto.Data), data.NotaryDefaultHashes...) + if err != nil { + return err + } + if linkMeta.Length != target.Length { + return fmt.Errorf("%v has observed length %v but expected %v", target.Name, linkMeta.Length, target.Length) + } + err = data.CompareMultiHashes(linkMeta.Hashes, target.Hashes) + if err != nil { + return fmt.Errorf("%v has observed hashes %v but expected %v", target.Name, linkMeta.Hashes, target.Hashes) + } } - // TODO: get targets from targets + // get targets from the top-level targets role targets, err = GetTargets(gun, trustServer, tlscacert, trustDir, timeout) if err != nil { - return fmt.Errorf("cannot list targets: %v", err) + return fmt.Errorf("cannot list %v :%v", data.CanonicalTargetsRole, err) } // TODO: verify that targets signs ONLY layouts and pubkeys for _, target := range targets { - if target.Name == targetName { - return target, nil + if target.Role.String() != data.CanonicalTargetsRole.String() { + return fmt.Errorf("expected %v but got :%v", data.CanonicalTargetsRole, target.Role.String()) + } + // check the target name matches links + match := false + for _, path := range paths { + if strings.HasPrefix(target.Name, path) { + match = true + break + } } + if !match { + return fmt.Errorf("%v does not match %v", target.Name, paths) + } + } return nil From 01aebc88f4dfb3ddefa80e8a0f2cd3a695b3a506 Mon Sep 17 00:00:00 2001 From: Trishank Karthik Kuppusamy Date: Fri, 30 Jul 2021 16:16:59 -0400 Subject: [PATCH 8/8] move links around --- testdata/intoto/minimal/{ => links}/developer.2d2e5437.link | 0 testdata/intoto/minimal/{ => links}/machine.dd035ca2.link | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename testdata/intoto/minimal/{ => links}/developer.2d2e5437.link (100%) rename testdata/intoto/minimal/{ => links}/machine.dd035ca2.link (100%) diff --git a/testdata/intoto/minimal/developer.2d2e5437.link b/testdata/intoto/minimal/links/developer.2d2e5437.link similarity index 100% rename from testdata/intoto/minimal/developer.2d2e5437.link rename to testdata/intoto/minimal/links/developer.2d2e5437.link diff --git a/testdata/intoto/minimal/machine.dd035ca2.link b/testdata/intoto/minimal/links/machine.dd035ca2.link similarity index 100% rename from testdata/intoto/minimal/machine.dd035ca2.link rename to testdata/intoto/minimal/links/machine.dd035ca2.link