Skip to content

Commit

Permalink
tests for witness
Browse files Browse the repository at this point in the history
Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
  • Loading branch information
David Lawrence committed Aug 3, 2016
1 parent fbdc159 commit 3b9ffbc
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 30 deletions.
8 changes: 4 additions & 4 deletions client/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func applyChangelist(repo *tuf.Repo, invalid *tuf.Repo, cl changelist.Changelist
case c.Scope() == changelist.ScopeRoot:
err = applyRootChange(repo, c)
default:
logrus.Debug("scope not supported: ", c.Scope())
return fmt.Errorf("scope not supported: %s", c.Scope())
}
if err != nil {
logrus.Debugf("error attempting to apply change #%d: %s, on scope: %s path: %s type: %s", index, c.Action(), c.Scope(), c.Path(), c.Type())
Expand Down Expand Up @@ -157,7 +157,7 @@ func changeTargetMeta(repo *tuf.Repo, c changelist.Change) error {
}

default:
logrus.Debug("action not yet supported: ", c.Action())
err = fmt.Errorf("action not yet supported: %s", c.Action())
}
return err
}
Expand All @@ -168,7 +168,7 @@ func applyRootChange(repo *tuf.Repo, c changelist.Change) error {
case changelist.TypeRootRole:
err = applyRootRoleChange(repo, c)
default:
logrus.Debug("type of root change not yet supported: ", c.Type())
err = fmt.Errorf("type of root change not yet supported: %s", c.Type())
}
return err // might be nil
}
Expand All @@ -187,7 +187,7 @@ func applyRootRoleChange(repo *tuf.Repo, c changelist.Change) error {
return err
}
default:
logrus.Debug("action not yet supported for root: ", c.Action())
return fmt.Errorf("action not yet supported for root: %s", c.Action())
}
return nil
}
Expand Down
11 changes: 1 addition & 10 deletions client/tufclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package client

import (
"encoding/json"
"fmt"

"github.com/Sirupsen/logrus"
"github.com/docker/notary"
Expand All @@ -12,15 +11,6 @@ import (
"github.com/docker/notary/tuf/signed"
)

// ErrCorruptedCache - local data is incorrect
type ErrCorruptedCache struct {
file string
}

func (e ErrCorruptedCache) Error() string {
return fmt.Sprintf("cache is corrupted: %s", e.file)
}

// TUFClient is a usability wrapper around a raw TUF repo
type TUFClient struct {
remote store.RemoteStore
Expand Down Expand Up @@ -158,6 +148,7 @@ func (c *TUFClient) downloadTargets() error {
BaseRole: data.BaseRole{Name: data.CanonicalTargetsRole},
Paths: []string{""},
}}

for len(toDownload) > 0 {
role := toDownload[0]
toDownload = toDownload[1:]
Expand Down
22 changes: 13 additions & 9 deletions client/witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package client
import (
"github.com/docker/notary/client/changelist"
"github.com/docker/notary/tuf"
"github.com/docker/notary/tuf/data"
"path/filepath"
)

// Witness creates change objects to witness (i.e. resign) the given
// Witness creates change objects to witness (i.e. re-sign) the given
// roles on the next publish. One change is created per role
func (r *NotaryRepository) Witness(roles ...string) ([]string, error) {
cl, err := changelist.NewFileChangelist(filepath.Join(r.tufRepoPath, "changelist"))
Expand Down Expand Up @@ -40,14 +41,17 @@ func witnessTargets(repo *tuf.Repo, invalid *tuf.Repo, role string) error {
r.Dirty = true
return nil
}
if invalid == nil {
// role isn't recognized, even as invalid
return ErrInvalidLocalRole{Role: role}
if invalid != nil {
if r, ok := invalid.Targets[role]; ok {
// role is recognized but invalid, move to valid data and mark for re-signing
repo.Targets[role] = r
r.Dirty = true
return nil
}
}
if r, ok := invalid.Targets[role]; ok {
// role is recognized but invalid, move to valid data and mark for re-signing
repo.Targets[role] = r
r.Dirty = true
// role isn't recognized, even as invalid
return data.ErrInvalidRole{
Role: role,
Reason: "this role is not known",
}
return nil
}
160 changes: 160 additions & 0 deletions cmd/notary/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1366,3 +1366,163 @@ func TestPurge(t *testing.T) {
require.NoError(t, err)
require.Contains(t, out, delgName)
}

// Initialize repo and test witnessing. The following steps are performed:
// 1. init a repo
// 2. add a delegation with a key and --all-paths
// 3. add a target to the delegation
// 4. list targets and ensure it really is in the delegation
// 5 witness the valid delegation, make sure everything is successful
// 6. add a new (different) key to the delegation
// 7. remove the key from the delegation
// 8. list targets and ensure the target is no longer visible
// 9. witness the delegation
// 10. list targets and ensure target is visible again
// 11. witness an invalid role and check for error on publish
func TestWitness(t *testing.T) {
setUp(t)

tempDir := tempDirWithConfig(t, "{}")
defer os.RemoveAll(tempDir)

server := setupServer()
defer server.Close()

startTime := time.Now()
endTime := startTime.AddDate(10, 0, 0)

// Setup certificates
tempFile, err := ioutil.TempFile("", "pemfile")
require.NoError(t, err)
privKey, err := utils.GenerateECDSAKey(rand.Reader)
cert, err := cryptoservice.GenerateCertificate(privKey, "gun", startTime, endTime)
require.NoError(t, err)
_, err = tempFile.Write(utils.CertToPEM(cert))
require.NoError(t, err)
tempFile.Close()
defer os.Remove(tempFile.Name())
rawPubBytes, _ := ioutil.ReadFile(tempFile.Name())
parsedPubKey, _ := utils.ParsePEMPublicKey(rawPubBytes)
keyID, err := utils.CanonicalKeyID(parsedPubKey)
require.NoError(t, err)

tempFile2, err := ioutil.TempFile("", "pemfile")
require.NoError(t, err)
privKey2, err := utils.GenerateECDSAKey(rand.Reader)
cert2, err := cryptoservice.GenerateCertificate(privKey2, "gun", startTime, endTime)
require.NoError(t, err)
_, err = tempFile2.Write(utils.CertToPEM(cert2))
require.NoError(t, err)
tempFile2.Close()
defer os.Remove(tempFile2.Name())

delgName := "targets/delegation"
targetName := "test_target"
targetHash := "9d9e890af64dd0f44b8a1538ff5fa0511cc31bf1ab89f3a3522a9a581a70fad8" // sha256 of README.md at time of writing test

keyStore, err := trustmanager.NewKeyFileStore(tempDir, passphrase.ConstantRetriever(testPassphrase))
require.NoError(t, err)
err = keyStore.AddKey(
trustmanager.KeyInfo{
Gun: "gun",
Role: delgName,
},
privKey,
)
require.NoError(t, err)

// 1. init a repo
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
require.NoError(t, err)

_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

// 2. add a delegation with a key and --all-paths
_, err = runCommand(t, tempDir, "delegation", "add", "gun", delgName, tempFile.Name(), "--all-paths")
require.NoError(t, err)

// 3. add a target to the delegation
_, err = runCommand(t, tempDir, "addhash", "gun", targetName, "100", "--sha256", targetHash, "-r", delgName)
require.NoError(t, err)

_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

// 4. list targets and ensure it really is in the delegation
output, err := runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.NoError(t, err)
require.Contains(t, output, targetName)
require.Contains(t, output, targetHash)

// 5. witness the valid delegation, make sure everything is successful
_, err = runCommand(t, tempDir, "witness", "gun", delgName)
require.NoError(t, err)

_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.NoError(t, err)
require.Contains(t, output, targetName)
require.Contains(t, output, targetHash)

// 6. add a new (different) key to the delegation
_, err = runCommand(t, tempDir, "delegation", "add", "gun", delgName, tempFile2.Name(), "--all-paths")
require.NoError(t, err)

// 7. remove the key from the delegation
_, err = runCommand(t, tempDir, "delegation", "remove", "gun", delgName, keyID)
require.NoError(t, err)

_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

// 8. list targets and ensure the target is no longer visible
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.NoError(t, err)
require.NotContains(t, output, targetName)
require.NotContains(t, output, targetHash)

err = keyStore.AddKey(
trustmanager.KeyInfo{
Gun: "gun",
Role: delgName,
},
privKey2,
)
require.NoError(t, err)

// 9. witness the delegation
_, err = runCommand(t, tempDir, "witness", "gun", delgName)
require.NoError(t, err)

_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

// 10. list targets and ensure target is visible again
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.NoError(t, err)
require.Contains(t, output, targetName)
require.Contains(t, output, targetHash)

// 11. witness an invalid role and check for error on publish
_, err = runCommand(t, tempDir, "witness", "gun", "targets/made/up")
require.NoError(t, err)

_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.Error(t, err)

// 12. check non-targets base roles all fail
for _, role := range []string{data.CanonicalRootRole, data.CanonicalSnapshotRole, data.CanonicalTimestampRole} {
// clear any pending changes to ensure errors are only related to the specific role we're trying to witness
_, err = runCommand(t, tempDir, "status", "gun", "--reset")
require.NoError(t, err)

_, err = runCommand(t, tempDir, "witness", "gun", role)
require.NoError(t, err)

_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.Error(t, err)
}
}
1 change: 1 addition & 0 deletions cmd/notary/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ var exampleValidCommands = []string{
"delegation list repo",
"delegation add repo targets/releases path/to/pem/file.pem",
"delegation remove repo targets/releases",
"witness gun targets/releases",
}

// config parsing bugs are propagated in all commands
Expand Down
2 changes: 1 addition & 1 deletion cmd/notary/tuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ var cmdTUFVerifyTemplate = usageTemplate{
var cmdWitnessTemplate = usageTemplate{
Use: "witness [ GUN ] <role> ...",
Short: "Marks roles to be re-signed the next time they're published",
Long: "Marks roles to be re-signed the next time they're published. If the role has no currently valid signatures, or is otherwise invalid, a new version is published. If a role has some valid signatures and is not otherwise invalid, new signatures are added without modifying the signed role data.",
Long: "Marks roles to be re-signed the next time they're published. Currently will always bump version and expiry for role. N.B. behaviour may change when thresholding is introduced.",
}

type tufCommander struct {
Expand Down
18 changes: 13 additions & 5 deletions tuf/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,15 @@ func (f finishedBuilder) GetConsistentInfo(roleName string) ConsistentInfo {

// NewRepoBuilder is the only way to get a pre-built RepoBuilder
func NewRepoBuilder(gun string, cs signed.CryptoService, trustpin trustpinning.TrustPinConfig) RepoBuilder {
return NewBuilderFromRepo(gun, NewRepo(cs), trustpin)
}

// NewBuilderFromRepo allows us to bootstrap a builder given existing repo data.
// YOU PROBABLY SHOULDN'T BE USING THIS OUTSIDE OF TESTING CODE!!!
func NewBuilderFromRepo(gun string, repo *Repo, trustpin trustpinning.TrustPinConfig) RepoBuilder {
return &repoBuilderWrapper{
RepoBuilder: &repoBuilder{
repo: NewRepo(cs),
repo: repo,
invalidRoles: NewRepo(nil),
gun: gun,
trustpin: trustpin,
Expand Down Expand Up @@ -539,6 +545,7 @@ func (rb *repoBuilder) loadDelegation(roleName string, content []byte, minVersio
return err
}

// bytesToSigned checks checksum
signedObj, err := rb.bytesToSigned(content, roleName)
if err != nil {
return err
Expand All @@ -548,13 +555,14 @@ func (rb *repoBuilder) loadDelegation(roleName string, content []byte, minVersio
if err != nil {
return err
}
// verify signature
if err := signed.VerifySignatures(signedObj, delegationRole.BaseRole); err != nil {
rb.invalidRoles.Targets[roleName] = signedTargets

if err := signed.VerifyVersion(&(signedTargets.Signed.SignedCommon), minVersion); err != nil {
// don't capture in invalidRoles because the role we received is a rollback
return err
}

if err := signed.VerifyVersion(&(signedTargets.Signed.SignedCommon), minVersion); err != nil {
// verify signature
if err := signed.VerifySignatures(signedObj, delegationRole.BaseRole); err != nil {
rb.invalidRoles.Targets[roleName] = signedTargets
return err
}
Expand Down
Loading

0 comments on commit 3b9ffbc

Please sign in to comment.