// We cannot validate a new root if the old root is corrupt, because there might // have been a root key rotation. func TestValidateOldRootCorruptRootRole(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) // so a valid root, but missing the root role signedRoot, err := data.RootFromSigned(r) require.NoError(t, err) delete(signedRoot.Signed.Roles, data.CanonicalRootRole) badRootJSON, err := json.Marshal(signedRoot) require.NoError(t, err) badRoot := storage.MetaUpdate{ Version: root.Version, Role: root.Role, Data: badRootJSON, } store.UpdateCurrent(gun, badRoot) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, data.ErrInvalidMetadata{}, err) }
func TestValidateSnapshotGeneratePrevCorrupt(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() snapRole, err := repo.GetBaseRole(data.CanonicalSnapshotRole) require.NoError(t, err) for _, k := range snapRole.Keys { err := store.SetKey(gun, data.CanonicalSnapshotRole, k.Algorithm(), k.Public()) require.NoError(t, err) } r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets} // corrupt the JSON structure of prev snapshot snapshot.Data = snapshot.Data[1:] // set the current snapshot in the store manually so we find it when generating // the next version store.UpdateCurrent(gun, snapshot) serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole, data.CanonicalSnapshotRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, &json.SyntaxError{}, err) }
func TestValidatePrevTimestamp(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot} store := storage.NewMemStorage() store.UpdateCurrent(gun, timestamp) serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) updates, err = validateUpdate(serverCrypto, gun, updates, store) require.NoError(t, err) // we generated our own timestamp, and did not take the other timestamp, // but all other metadata should come from updates var foundTimestamp bool for _, update := range updates { if update.Role == data.CanonicalTimestampRole { foundTimestamp = true oldTimestamp, newTimestamp := &data.SignedTimestamp{}, &data.SignedTimestamp{} require.NoError(t, json.Unmarshal(timestamp.Data, oldTimestamp)) require.NoError(t, json.Unmarshal(update.Data, newTimestamp)) require.Equal(t, oldTimestamp.Signed.Version+1, newTimestamp.Signed.Version) } } require.True(t, foundTimestamp) }
// Store is broken when getting the current snapshot func TestValidateSnapshotGenerateStoreGetCurrentSnapshotBroken(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := getFailStore{ MetaStore: storage.NewMemStorage(), errsToReturn: map[string]error{data.CanonicalSnapshotRole: data.ErrNoSuchRole{}}, } snapRole, err := repo.GetBaseRole(data.CanonicalSnapshotRole) require.NoError(t, err) for _, k := range snapRole.Keys { err := store.SetKey(gun, data.CanonicalSnapshotRole, k.Algorithm(), k.Public()) require.NoError(t, err) } r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, _, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole, data.CanonicalSnapshotRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, data.ErrNoSuchRole{}, err) }
func TestValidateSnapshotGenerate(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() snapRole, err := repo.GetBaseRole(data.CanonicalSnapshotRole) require.NoError(t, err) for _, k := range snapRole.Keys { err := store.SetKey(gun, data.CanonicalSnapshotRole, k.Algorithm(), k.Public()) require.NoError(t, err) } r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, _, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{targets} store.UpdateCurrent(gun, root) serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole, data.CanonicalSnapshotRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.NoError(t, err) }
func TestValidateTargetsModifiedHash(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) snap, err := data.SnapshotFromSigned(sn) require.NoError(t, err) snap.Signed.Meta["targets"].Hashes["sha256"][0] = snap.Signed.Meta["targets"].Hashes["sha256"][0] ^ 0xff sn, err = snap.ToSigned() require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, validation.ErrBadSnapshot{}, err) }
func TestValidateRootCanContainOnlyx509KeysWithRightGun(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo("wrong/gun") require.NoError(t, err) serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) // if the root has the wrong gun, the server will fail to validate r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) _, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, targets, snapshot, timestamp}, storage.NewMemStorage()) require.Error(t, err) require.IsType(t, validation.ErrBadRoot{}, err) // create regular non-x509 keys - change the root keys to one that is not // an x509 key - it should also fail to validate newRootKey, err := cs.Create(data.CanonicalRootRole, gun, data.ECDSAKey) require.NoError(t, err) require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalRootRole, newRootKey)) r, tg, sn, ts, err = testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err = getUpdates(r, tg, sn, ts) require.NoError(t, err) _, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, targets, snapshot, timestamp}, storage.NewMemStorage()) require.Error(t, err) require.IsType(t, validation.ErrBadRoot{}, err) }
// ### Snapshot size mismatch negative tests ### func TestValidateRootModifiedSize(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) // add another copy of the signature so the hash is different r.Signatures = append(r.Signatures, r.Signatures[0]) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) // flip all the bits in the first byte root.Data[0] = root.Data[0] ^ 0xff updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, validation.ErrBadRoot{}, err) }
// If the timestamp role has a threshold > 1, validation fails. func TestValidateRootInvalidTimestampThreshold(t *testing.T) { gun := "docker.com/notary" oldRepo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) tsKey2, err := testutils.CreateKey(cs, gun, "timestamp2", data.ECDSAKey) require.NoError(t, err) oldRepo.AddBaseKeys(data.CanonicalTimestampRole, tsKey2) tsRole, ok := oldRepo.Root.Signed.Roles[data.CanonicalTimestampRole] require.True(t, ok) tsRole.Threshold = 2 r, tg, sn, ts, err := testutils.Sign(oldRepo) require.NoError(t, err) root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) store := storage.NewMemStorage() updates := []storage.MetaUpdate{root, targets, snapshot} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, validation.ErrBadRoot{}, err) }
func TestValidateOldRoot(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) store.UpdateCurrent(gun, root) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.NoError(t, err) }
func TestValidateSnapshotMissingNoSnapshotKey(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, _, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, validation.ErrBadHierarchy{}, err) }
func TestValidateTargetsRoleMissing(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() delete(repo.Root.Signed.Roles, "targets") r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, validation.ErrBadRoot{}, err) }
func TestValidateSnapshotGenerateWithPrev(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() snapRole, err := repo.GetBaseRole(data.CanonicalSnapshotRole) require.NoError(t, err) for _, k := range snapRole.Keys { err := store.SetKey(gun, data.CanonicalSnapshotRole, k.Algorithm(), k.Public()) require.NoError(t, err) } r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets} // set the current snapshot in the store manually so we find it when generating // the next version store.UpdateCurrent(gun, snapshot) prev, err := data.SnapshotFromSigned(sn) require.NoError(t, err) serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole, data.CanonicalSnapshotRole) updates, err = validateUpdate(serverCrypto, gun, updates, store) require.NoError(t, err) for _, u := range updates { if u.Role == data.CanonicalSnapshotRole { curr := &data.SignedSnapshot{} err = json.Unmarshal(u.Data, curr) require.NoError(t, err) require.Equal(t, prev.Signed.Version+1, curr.Signed.Version) require.Equal(t, u.Version, curr.Signed.Version) } } }
// We cannot validate a new root if we cannot get the old root from the DB ( // and cannot detect whether there was an old root or not), because there might // have been an old root and we can't determine if the new root represents a // root key rotation. func TestValidateRootGetCurrentRootBroken(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := getFailStore{ MetaStore: storage.NewMemStorage(), errsToReturn: map[string]error{data.CanonicalRootRole: data.ErrNoSuchRole{}}, } r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, data.ErrNoSuchRole{}, err) }
func TestValidateSnapshotCorrupt(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) // flip all the bits in the first byte snapshot.Data[0] = snapshot.Data[0] ^ 0xff updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, validation.ErrBadSnapshot{}, err) }
func TestValidatePreviousTimestampCorrupt(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot} // have corrupt timestamp data in the storage already store := storage.NewMemStorage() timestamp.Data = timestamp.Data[1:] store.UpdateCurrent(gun, timestamp) serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, &json.SyntaxError{}, err) }
func TestValidateEmptyNew(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) updates, err = validateUpdate(serverCrypto, gun, updates, store) require.NoError(t, err) // we generated our own timestamp, and did not take the other timestamp, // but all other metadata should come from updates founds := make(map[string]bool) for _, update := range updates { switch update.Role { case data.CanonicalRootRole: require.True(t, bytes.Equal(update.Data, root.Data)) founds[data.CanonicalRootRole] = true case data.CanonicalSnapshotRole: require.True(t, bytes.Equal(update.Data, snapshot.Data)) founds[data.CanonicalSnapshotRole] = true case data.CanonicalTargetsRole: require.True(t, bytes.Equal(update.Data, targets.Data)) founds[data.CanonicalTargetsRole] = true case data.CanonicalTimestampRole: require.False(t, bytes.Equal(update.Data, timestamp.Data)) founds[data.CanonicalTimestampRole] = true } } require.Len(t, founds, 4) }
// If any role has a threshold < 1, validation fails func TestValidateRootInvalidZeroThreshold(t *testing.T) { for _, role := range data.BaseRoles { gun := "docker.com/notary" oldRepo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) tsRole, ok := oldRepo.Root.Signed.Roles[role] require.True(t, ok) tsRole.Threshold = 0 r, tg, sn, ts, err := testutils.Sign(oldRepo) require.NoError(t, err) root, targets, snapshot, _, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) store := storage.NewMemStorage() updates := []storage.MetaUpdate{root, targets, snapshot} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.Contains(t, err.Error(), "invalid threshold") } }
func TestValidateOldRootCorrupt(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) badRoot := storage.MetaUpdate{ Version: root.Version, Role: root.Role, Data: root.Data[1:], } store.UpdateCurrent(gun, badRoot) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} serverCrypto := testutils.CopyKeys(t, cs, data.CanonicalTimestampRole) _, err = validateUpdate(serverCrypto, gun, updates, store) require.Error(t, err) require.IsType(t, &json.SyntaxError{}, err) }
// A root rotation must be signed with old and new root keys such that it satisfies // the old and new roles, otherwise the new root fails to validate func TestRootRotationNotSignedWithOldKeysForOldRole(t *testing.T) { gun := "docker.com/notary" repo, crypto, err := testutils.EmptyRepo(gun) require.NoError(t, err) store := storage.NewMemStorage() serverCrypto := testutils.CopyKeys(t, crypto, data.CanonicalTimestampRole) oldRootKeyID := repo.Root.Signed.Roles[data.CanonicalRootRole].KeyIDs[0] // make the original root have 2 keys with a threshold of 2 pairedRootKeys := make([]data.PublicKey, 2) for i := 0; i < len(pairedRootKeys); i++ { pairedRootKeys[i], err = testutils.CreateKey(crypto, gun, data.CanonicalRootRole, data.ECDSAKey) require.NoError(t, err) } require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalRootRole, pairedRootKeys...)) repo.Root.Signed.Roles[data.CanonicalRootRole].Threshold = 2 r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) require.Len(t, r.Signatures, 3) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} require.NoError(t, store.UpdateMany(gun, updates)) finalRootKey, err := testutils.CreateKey(crypto, gun, data.CanonicalRootRole, data.ECDSAKey) require.NoError(t, err) repo.Root.Signed.Roles[data.CanonicalRootRole].Threshold = 1 require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalRootRole, finalRootKey)) r, err = repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) origSigs := r.Signatures // make sure it's signed with only one of the previous keys and the new key sigs := make([]data.Signature, 0, 2) for _, sig := range origSigs { if sig.KeyID == pairedRootKeys[0].ID() || sig.KeyID == finalRootKey.ID() { sigs = append(sigs, sig) } } require.Len(t, sigs, 2) repo.Root.Signatures = sigs r.Signatures = sigs sn, err = repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) require.NoError(t, err) root, targets, snapshot, timestamp, err = getUpdates(r, tg, sn, ts) require.NoError(t, err) _, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, snapshot}, store) require.Error(t, err) require.Contains(t, err.Error(), "could not rotate trust to a new trusted root") // now sign with both of the pair and the new one sigs = make([]data.Signature, 0, 3) for _, sig := range origSigs { if sig.KeyID != oldRootKeyID { sigs = append(sigs, sig) } } require.Len(t, sigs, 3) repo.Root.Signatures = sigs r.Signatures = sigs sn, err = repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) require.NoError(t, err) root, _, snapshot, _, err = getUpdates(r, tg, sn, ts) require.NoError(t, err) _, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, snapshot}, store) require.NoError(t, err) }
// A valid root rotation requires the immediately previous root ROLE be satisfied, // not just that there is a single root signature. So if there were 2 keys, either // of which can sign the root rotation, then either one of those keys can be used // to sign the root rotation - not necessarily the one that signed the previous root. func TestValidateRootRotationMultipleKeysThreshold1(t *testing.T) { gun := "docker.com/notary" repo, crypto, err := testutils.EmptyRepo(gun) require.NoError(t, err) serverCrypto := testutils.CopyKeys(t, crypto, data.CanonicalTimestampRole) store := storage.NewMemStorage() // add a new root key to the root so that either can sign have to sign additionalRootKey, err := testutils.CreateKey(crypto, gun, data.CanonicalRootRole, data.ECDSAKey) require.NoError(t, err) additionalRootID := additionalRootKey.ID() repo.Root.Signed.Keys[additionalRootID] = additionalRootKey repo.Root.Signed.Roles[data.CanonicalRootRole].KeyIDs = append( repo.Root.Signed.Roles[data.CanonicalRootRole].KeyIDs, additionalRootID) r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) require.Len(t, r.Signatures, 2) // make sure the old root was just signed with the first key for _, sig := range r.Signatures { if sig.KeyID != additionalRootID { r.Signatures = []data.Signature{sig} break } } require.Len(t, r.Signatures, 1) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) // set the original root in the store updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} require.NoError(t, store.UpdateMany(gun, updates)) // replace the keys with just 1 key rotatedRootKey, err := testutils.CreateKey(crypto, gun, data.CanonicalRootRole, data.ECDSAKey) require.NoError(t, err) rotatedRootID := rotatedRootKey.ID() require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalRootRole, rotatedRootKey)) r, err = repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) require.Len(t, r.Signatures, 3) // delete all signatures except the additional key (which didn't sign the // previous root) and the new key sigs := make([]data.Signature, 0, 2) for _, sig := range repo.Root.Signatures { if sig.KeyID == additionalRootID || sig.KeyID == rotatedRootID { sigs = append(sigs, sig) } } require.Len(t, sigs, 2) repo.Root.Signatures = sigs r.Signatures = sigs sn, err = repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) require.NoError(t, err) root, _, snapshot, _, err = getUpdates(r, tg, sn, ts) require.NoError(t, err) root.Version = repo.Root.Signed.Version snapshot.Version = repo.Snapshot.Signed.Version _, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, snapshot}, store) require.NoError(t, err) }
// A valid root rotation only cares about the immediately previous old root keys, // whether or not there are old root roles func TestValidateRootRotationWithOldSigs(t *testing.T) { gun := "docker.com/notary" repo, crypto, err := testutils.EmptyRepo(gun) require.NoError(t, err) serverCrypto := testutils.CopyKeys(t, crypto, data.CanonicalTimestampRole) store := storage.NewMemStorage() r, tg, sn, ts, err := testutils.Sign(repo) require.NoError(t, err) root, targets, snapshot, timestamp, err := getUpdates(r, tg, sn, ts) require.NoError(t, err) // set the original root in the store updates := []storage.MetaUpdate{root, targets, snapshot, timestamp} require.NoError(t, store.UpdateMany(gun, updates)) // rotate the root key, sign with both keys, and update - update should succeed newRootKey, err := testutils.CreateKey(crypto, gun, data.CanonicalRootRole, data.ECDSAKey) require.NoError(t, err) newRootID := newRootKey.ID() require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalRootRole, newRootKey)) r, _, sn, _, err = testutils.Sign(repo) require.NoError(t, err) root, _, snapshot, _, err = getUpdates(r, tg, sn, ts) require.NoError(t, err) root.Version = repo.Root.Signed.Version snapshot.Version = repo.Snapshot.Signed.Version updates, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, snapshot}, store) require.NoError(t, err) require.NoError(t, store.UpdateMany(gun, updates)) // the next root does NOT need to be signed by both keys, because we only care // about signing with both keys if the root keys have changed (signRoot again to bump the version) r, err = repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) // delete all signatures except the one with the new key for _, sig := range repo.Root.Signatures { if sig.KeyID == newRootID { r.Signatures = []data.Signature{sig} repo.Root.Signatures = r.Signatures break } } sn, err = repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) require.NoError(t, err) root, _, snapshot, _, err = getUpdates(r, tg, sn, ts) require.NoError(t, err) root.Version = repo.Root.Signed.Version snapshot.Version = repo.Snapshot.Signed.Version updates, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, snapshot}, store) require.NoError(t, err) require.NoError(t, store.UpdateMany(gun, updates)) // another root rotation requires only the previous and new keys, and not the // original root key even though that original role is still in the metadata newRootKey2, err := testutils.CreateKey(crypto, gun, data.CanonicalRootRole, data.ECDSAKey) require.NoError(t, err) newRootID2 := newRootKey2.ID() require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalRootRole, newRootKey2)) r, err = repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole)) require.NoError(t, err) // delete all signatures except the ones with the first and second new keys sigs := make([]data.Signature, 0, 2) for _, sig := range repo.Root.Signatures { if sig.KeyID == newRootID || sig.KeyID == newRootID2 { sigs = append(sigs, sig) } } require.Len(t, sigs, 2) repo.Root.Signatures = sigs r.Signatures = sigs sn, err = repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole)) require.NoError(t, err) root, _, snapshot, _, err = getUpdates(r, tg, sn, ts) require.NoError(t, err) root.Version = repo.Root.Signed.Version snapshot.Version = repo.Snapshot.Signed.Version _, err = validateUpdate(serverCrypto, gun, []storage.MetaUpdate{root, snapshot}, store) require.NoError(t, err) }
// Test the cases in which GenerateSnapshot fails func TestGenerateSnapshotInvalidOperations(t *testing.T) { gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) // make snapshot have 2 keys and a threshold of 2 snapKeys := make([]data.PublicKey, 2) for i := 0; i < 2; i++ { snapKeys[i], err = cs.Create(data.CanonicalSnapshotRole, gun, data.ECDSAKey) require.NoError(t, err) } require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalSnapshotRole, snapKeys...)) repo.Root.Signed.Roles[data.CanonicalSnapshotRole].Threshold = 2 meta, err := testutils.SignAndSerialize(repo) require.NoError(t, err) for _, prevSnapshot := range []*data.SignedSnapshot{nil, repo.Snapshot} { // copy keys, since we expect one of these generation attempts to succeed and we do // some key deletion tests later newCS := testutils.CopyKeys(t, cs, data.CanonicalSnapshotRole) // --- we can't generate a snapshot if the root isn't loaded builder := tuf.NewRepoBuilder(gun, newCS, trustpinning.TrustPinConfig{}) _, _, err := builder.GenerateSnapshot(prevSnapshot) require.IsType(t, tuf.ErrInvalidBuilderInput{}, err) require.Contains(t, err.Error(), "root must be loaded first") require.False(t, builder.IsLoaded(data.CanonicalSnapshotRole)) // --- we can't generate a snapshot if the targets isn't loaded and we have no previous snapshot, // --- but if we have a previous snapshot with a valid targets, we're good even if no snapshot // --- is loaded require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false)) _, _, err = builder.GenerateSnapshot(prevSnapshot) if prevSnapshot == nil { require.IsType(t, tuf.ErrInvalidBuilderInput{}, err) require.Contains(t, err.Error(), "targets must be loaded first") require.False(t, builder.IsLoaded(data.CanonicalSnapshotRole)) } else { require.NoError(t, err) } // --- we can't generate a snapshot if we've loaded the timestamp already builder = tuf.NewRepoBuilder(gun, newCS, trustpinning.TrustPinConfig{}) require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false)) if prevSnapshot == nil { require.NoError(t, builder.Load(data.CanonicalTargetsRole, meta[data.CanonicalTargetsRole], 1, false)) } require.NoError(t, builder.Load(data.CanonicalTimestampRole, meta[data.CanonicalTimestampRole], 1, false)) _, _, err = builder.GenerateSnapshot(prevSnapshot) require.IsType(t, tuf.ErrInvalidBuilderInput{}, err) require.Contains(t, err.Error(), "cannot generate snapshot if timestamp has already been loaded") require.False(t, builder.IsLoaded(data.CanonicalSnapshotRole)) // --- we cannot generate a snapshot if we've already loaded a snapshot builder = tuf.NewRepoBuilder(gun, newCS, trustpinning.TrustPinConfig{}) require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false)) if prevSnapshot == nil { require.NoError(t, builder.Load(data.CanonicalTargetsRole, meta[data.CanonicalTargetsRole], 1, false)) } require.NoError(t, builder.Load(data.CanonicalSnapshotRole, meta[data.CanonicalSnapshotRole], 1, false)) _, _, err = builder.GenerateSnapshot(prevSnapshot) require.IsType(t, tuf.ErrInvalidBuilderInput{}, err) require.Contains(t, err.Error(), "snapshot has already been loaded") // --- we cannot generate a snapshot if we can't satisfy the role threshold for i := 0; i < len(snapKeys); i++ { require.NoError(t, newCS.RemoveKey(snapKeys[i].ID())) builder = tuf.NewRepoBuilder(gun, newCS, trustpinning.TrustPinConfig{}) require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false)) if prevSnapshot == nil { require.NoError(t, builder.Load(data.CanonicalTargetsRole, meta[data.CanonicalTargetsRole], 1, false)) } _, _, err = builder.GenerateSnapshot(prevSnapshot) require.IsType(t, signed.ErrInsufficientSignatures{}, err) require.False(t, builder.IsLoaded(data.CanonicalSnapshotRole)) } // --- we cannot generate a snapshot if we don't have a cryptoservice builder = tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{}) require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false)) if prevSnapshot == nil { require.NoError(t, builder.Load(data.CanonicalTargetsRole, meta[data.CanonicalTargetsRole], 1, false)) } _, _, err = builder.GenerateSnapshot(prevSnapshot) require.IsType(t, tuf.ErrInvalidBuilderInput{}, err) require.Contains(t, err.Error(), "cannot generate snapshot without a cryptoservice") require.False(t, builder.IsLoaded(data.CanonicalSnapshotRole)) } // --- we can't generate a snapshot if we're given an invalid previous snapshot (for instance, an empty one), // --- even if we have a targets loaded builder := tuf.NewRepoBuilder(gun, cs, trustpinning.TrustPinConfig{}) require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false)) require.NoError(t, builder.Load(data.CanonicalTargetsRole, meta[data.CanonicalTargetsRole], 1, false)) _, _, err = builder.GenerateSnapshot(&data.SignedSnapshot{}) require.IsType(t, data.ErrInvalidMetadata{}, err) require.False(t, builder.IsLoaded(data.CanonicalSnapshotRole)) }