// ApplyTargets falls back to role that exists when adding or deleting a change func TestApplyChangelistTargetsFallbackRoles(t *testing.T) { repo, _, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) assert.NoError(t, err) cl := changelist.NewMemChangelist() assert.NoError(t, cl.Add(&changelist.TufChange{ Actn: changelist.ActionCreate, Role: "targets/level1/level2/level3/level4", ChangeType: "target", ChangePath: "latest", Data: fjson, })) assert.NoError(t, applyChangelist(repo, cl)) _, ok := repo.Targets[data.CanonicalTargetsRole].Signed.Targets["latest"] assert.True(t, ok) // now delete and assert it applies to cl = changelist.NewMemChangelist() assert.NoError(t, cl.Add(&changelist.TufChange{ Actn: changelist.ActionDelete, Role: "targets/level1/level2/level3/level4", ChangeType: "target", ChangePath: "latest", Data: nil, })) assert.NoError(t, applyChangelist(repo, cl)) assert.Empty(t, repo.Targets[data.CanonicalTargetsRole].Signed.Targets) }
// ApplyTargets fails when adding or deleting a change to a nonexistent delegation func TestApplyChangelistTargetsFailsNonexistentRole(t *testing.T) { repo, _, err := testutils.EmptyRepo("docker.com/notary") require.NoError(t, err) hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) require.NoError(t, err) cl := changelist.NewMemChangelist() require.NoError(t, cl.Add(&changelist.TUFChange{ Actn: changelist.ActionCreate, Role: "targets/level1/level2/level3/level4", ChangeType: "target", ChangePath: "latest", Data: fjson, })) err = applyChangelist(repo, nil, cl) require.Error(t, err) require.IsType(t, data.ErrInvalidRole{}, err) // now try a delete and assert the same error cl = changelist.NewMemChangelist() require.NoError(t, cl.Add(&changelist.TUFChange{ Actn: changelist.ActionDelete, Role: "targets/level1/level2/level3/level4", ChangeType: "target", ChangePath: "latest", Data: nil, })) err = applyChangelist(repo, nil, cl) require.Error(t, err) require.IsType(t, data.ErrInvalidRole{}, err) }
// RotateKey removes all existing keys associated with the role, and either // creates and adds one new key or delegates managing the key to the server. // These changes are staged in a changelist until publish is called. func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool) error { // We currently support remotely managing timestamp and snapshot keys canBeRemoteKey := role == data.CanonicalTimestampRole || role == data.CanonicalSnapshotRole // And locally managing root, targets, and snapshot keys canBeLocalKey := (role == data.CanonicalSnapshotRole || role == data.CanonicalTargetsRole || role == data.CanonicalRootRole) switch { case !data.ValidRole(role) || data.IsDelegation(role): return fmt.Errorf("notary does not currently permit rotating the %s key", role) case serverManagesKey && !canBeRemoteKey: return ErrInvalidRemoteRole{Role: role} case !serverManagesKey && !canBeLocalKey: return ErrInvalidLocalRole{Role: role} } var ( pubKey data.PublicKey err error errFmtMsg string ) switch serverManagesKey { case true: pubKey, err = getRemoteKey(r.baseURL, r.gun, role, r.roundTrip) errFmtMsg = "unable to rotate remote key: %s" default: pubKey, err = r.CryptoService.Create(role, r.gun, data.ECDSAKey) errFmtMsg = "unable to generate key: %s" } if err != nil { return fmt.Errorf(errFmtMsg, err) } // if this is a root role, generate a root cert for the public key if role == data.CanonicalRootRole { privKey, _, err := r.CryptoService.GetPrivateKey(pubKey.ID()) if err != nil { return err } pubKey, err = rootCertKey(r.gun, privKey) if err != nil { return err } } cl := changelist.NewMemChangelist() if err := r.rootFileKeyChange(cl, role, changelist.ActionCreate, pubKey); err != nil { return err } return r.publish(cl) }
// Each change applies only to the role specified func TestApplyChangelistTargetsToMultipleRoles(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") require.NoError(t, err) newKey, err := cs.Create("targets/level1", "docker.com/notary", data.ED25519Key) require.NoError(t, err) err = repo.UpdateDelegationKeys("targets/level1", []data.PublicKey{newKey}, []string{}, 1) require.NoError(t, err) err = repo.UpdateDelegationPaths("targets/level1", []string{""}, []string{}, false) require.NoError(t, err) err = repo.UpdateDelegationKeys("targets/level2", []data.PublicKey{newKey}, []string{}, 1) require.NoError(t, err) err = repo.UpdateDelegationPaths("targets/level2", []string{""}, []string{}, false) require.NoError(t, err) hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) require.NoError(t, err) cl := changelist.NewMemChangelist() require.NoError(t, cl.Add(&changelist.TUFChange{ Actn: changelist.ActionCreate, Role: "targets/level1", ChangeType: "target", ChangePath: "latest", Data: fjson, })) require.NoError(t, cl.Add(&changelist.TUFChange{ Actn: changelist.ActionDelete, Role: "targets/level2", ChangeType: "target", ChangePath: "latest", Data: nil, })) require.NoError(t, applyChangelist(repo, nil, cl)) _, ok := repo.Targets["targets/level1"].Signed.Targets["latest"] require.True(t, ok) _, ok = repo.Targets["targets/level2"] require.False(t, ok, "no change to targets/level2, so metadata not created") }
func TestApplyChangelist(t *testing.T) { kdb := keys.NewDB() role, err := data.NewRole("targets", 1, nil, nil, nil) assert.NoError(t, err) kdb.AddRole(role) repo := tuf.NewRepo(kdb, nil) err = repo.InitTargets() assert.NoError(t, err) hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) assert.NoError(t, err) cl := changelist.NewMemChangelist() addChange := &changelist.TufChange{ Actn: changelist.ActionCreate, Role: changelist.ScopeTargets, ChangeType: "target", ChangePath: "latest", Data: fjson, } cl.Add(addChange) err = applyChangelist(repo, cl) assert.NoError(t, err) assert.NotNil(t, repo.Targets["targets"].Signed.Targets["latest"]) cl.Clear("") removeChange := &changelist.TufChange{ Actn: changelist.ActionDelete, Role: changelist.ScopeTargets, ChangeType: "target", ChangePath: "latest", Data: nil, } cl.Add(removeChange) err = applyChangelist(repo, cl) assert.NoError(t, err) _, ok := repo.Targets["targets"].Signed.Targets["latest"] assert.False(t, ok) }
// Each change applies only to the role specified func TestApplyChangelistTargetsToMultipleRoles(t *testing.T) { _, repo, cs := testutils.EmptyRepo() newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) r, err := data.NewRole("targets/level1", 1, []string{newKey.ID()}, []string{""}, nil) assert.NoError(t, err) repo.UpdateDelegations(r, []data.PublicKey{newKey}) r, err = data.NewRole("targets/level2", 1, []string{newKey.ID()}, []string{""}, nil) assert.NoError(t, err) repo.UpdateDelegations(r, []data.PublicKey{newKey}) hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) assert.NoError(t, err) cl := changelist.NewMemChangelist() assert.NoError(t, cl.Add(&changelist.TufChange{ Actn: changelist.ActionCreate, Role: "targets/level1", ChangeType: "target", ChangePath: "latest", Data: fjson, })) assert.NoError(t, cl.Add(&changelist.TufChange{ Actn: changelist.ActionDelete, Role: "targets/level2", ChangeType: "target", ChangePath: "latest", Data: nil, })) assert.NoError(t, applyChangelist(repo, cl)) _, ok := repo.Targets["targets/level1"].Signed.Targets["latest"] assert.True(t, ok) _, ok = repo.Targets["targets/level2"] assert.False(t, ok, "no change to targets/level2, so metadata not created") }
// RotateKey removes all existing keys associated with the role, and either // creates and adds one new key or delegates managing the key to the server. // These changes are staged in a changelist until publish is called. func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool) error { switch { // We currently support locally or remotely managing snapshot keys... case role == data.CanonicalSnapshotRole: break // locally managing targets keys only case role == data.CanonicalTargetsRole && !serverManagesKey: break case role == data.CanonicalTargetsRole && serverManagesKey: return ErrInvalidRemoteRole{Role: data.CanonicalTargetsRole} // and remotely managing timestamp keys only case role == data.CanonicalTimestampRole && serverManagesKey: break case role == data.CanonicalTimestampRole && !serverManagesKey: return ErrInvalidLocalRole{Role: data.CanonicalTimestampRole} default: return fmt.Errorf("notary does not currently permit rotating the %s key", role) } var ( pubKey data.PublicKey err error errFmtMsg string ) switch serverManagesKey { case true: pubKey, err = getRemoteKey(r.baseURL, r.gun, role, r.roundTrip) errFmtMsg = "unable to rotate remote key: %s" default: pubKey, err = r.CryptoService.Create(role, r.gun, data.ECDSAKey) errFmtMsg = "unable to generate key: %s" } if err != nil { return fmt.Errorf(errFmtMsg, err) } cl := changelist.NewMemChangelist() if err := r.rootFileKeyChange(cl, role, changelist.ActionCreate, pubKey); err != nil { return err } return r.publish(cl) }
// Adding the same target twice doesn't actually add it. func TestApplyAddTargetTwice(t *testing.T) { repo, _, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) _, err = repo.InitTargets(data.CanonicalTargetsRole) assert.NoError(t, err) hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) assert.NoError(t, err) cl := changelist.NewMemChangelist() assert.NoError(t, cl.Add(&changelist.TufChange{ Actn: changelist.ActionCreate, Role: changelist.ScopeTargets, ChangeType: "target", ChangePath: "latest", Data: fjson, })) assert.NoError(t, cl.Add(&changelist.TufChange{ Actn: changelist.ActionCreate, Role: changelist.ScopeTargets, ChangeType: "target", ChangePath: "latest", Data: fjson, })) assert.NoError(t, applyChangelist(repo, cl)) assert.Len(t, repo.Targets["targets"].Signed.Targets, 1) assert.NotEmpty(t, repo.Targets["targets"].Signed.Targets["latest"]) assert.NoError(t, applyTargetsChange(repo, &changelist.TufChange{ Actn: changelist.ActionCreate, Role: changelist.ScopeTargets, ChangeType: "target", ChangePath: "latest", Data: fjson, })) assert.Len(t, repo.Targets["targets"].Signed.Targets, 1) assert.NotEmpty(t, repo.Targets["targets"].Signed.Targets["latest"]) }
func TestApplyChangelist(t *testing.T) { repo, _, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) _, err = repo.InitTargets(data.CanonicalTargetsRole) assert.NoError(t, err) hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) assert.NoError(t, err) cl := changelist.NewMemChangelist() addChange := &changelist.TufChange{ Actn: changelist.ActionCreate, Role: changelist.ScopeTargets, ChangeType: "target", ChangePath: "latest", Data: fjson, } cl.Add(addChange) err = applyChangelist(repo, cl) assert.NoError(t, err) assert.NotNil(t, repo.Targets["targets"].Signed.Targets["latest"]) cl.Clear("") removeChange := &changelist.TufChange{ Actn: changelist.ActionDelete, Role: changelist.ScopeTargets, ChangeType: "target", ChangePath: "latest", Data: nil, } cl.Add(removeChange) err = applyChangelist(repo, cl) assert.NoError(t, err) _, ok := repo.Targets["targets"].Signed.Targets["latest"] assert.False(t, ok) }
// RotateKey removes all existing keys associated with the role, and either // creates and adds one new key or delegates managing the key to the server. // These changes are staged in a changelist until publish is called. func (r *NotaryRepository) RotateKey(role string, serverManagesKey bool) error { if err := checkRotationInput(role, serverManagesKey); err != nil { return err } var ( pubKey data.PublicKey err error errFmtMsg string ) switch serverManagesKey { case true: pubKey, err = rotateRemoteKey(r.baseURL, r.gun, role, r.roundTrip) errFmtMsg = "unable to rotate remote key: %s" default: pubKey, err = r.CryptoService.Create(role, r.gun, data.ECDSAKey) errFmtMsg = "unable to generate key: %s" } if err != nil { return fmt.Errorf(errFmtMsg, err) } // if this is a root role, generate a root cert for the public key if role == data.CanonicalRootRole { privKey, _, err := r.CryptoService.GetPrivateKey(pubKey.ID()) if err != nil { return err } pubKey, err = rootCertKey(r.gun, privKey) if err != nil { return err } } cl := changelist.NewMemChangelist() if err := r.rootFileKeyChange(cl, role, changelist.ActionCreate, pubKey); err != nil { return err } return r.publish(cl) }
// If there is no delegation target, ApplyTargets creates it func TestApplyChangelistCreatesDelegation(t *testing.T) { repo, cs, err := testutils.EmptyRepo("docker.com/notary") assert.NoError(t, err) newKey, err := cs.Create("targets/level1", data.ED25519Key) assert.NoError(t, err) r, err := data.NewRole("targets/level1", 1, []string{newKey.ID()}, []string{""}) assert.NoError(t, err) repo.UpdateDelegations(r, []data.PublicKey{newKey}) delete(repo.Targets, "targets/level1") hash := sha256.Sum256([]byte{}) f := &data.FileMeta{ Length: 1, Hashes: map[string][]byte{ "sha256": hash[:], }, } fjson, err := json.Marshal(f) assert.NoError(t, err) cl := changelist.NewMemChangelist() assert.NoError(t, cl.Add(&changelist.TufChange{ Actn: changelist.ActionCreate, Role: "targets/level1", ChangeType: "target", ChangePath: "latest", Data: fjson, })) assert.NoError(t, applyChangelist(repo, cl)) _, ok := repo.Targets["targets/level1"] assert.True(t, ok, "Failed to create the delegation target") _, ok = repo.Targets["targets/level1"].Signed.Targets["latest"] assert.True(t, ok, "Failed to write change to delegation target") }