// 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 testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType string) { gun := "docker.com/notary" memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cs := cryptoservice.NewCryptoService(memKeyStore) // TUF key with PEM-encoded x509 certificate origRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg) require.NoError(t, err) origRootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{origRootKey.ID()}, nil) require.NoError(t, err) origTestRoot, err := data.NewRoot( map[string]data.PublicKey{origRootKey.ID(): origRootKey}, map[string]*data.RootRole{ data.CanonicalRootRole: &origRootRole.RootRole, data.CanonicalTargetsRole: &origRootRole.RootRole, data.CanonicalSnapshotRole: &origRootRole.RootRole, data.CanonicalTimestampRole: &origRootRole.RootRole, }, false, ) origTestRoot.Signed.Version = 1 require.NoError(t, err, "Failed to create new root") signedOrigTestRoot, err := origTestRoot.ToSigned() require.NoError(t, err) err = signed.Sign(cs, signedOrigTestRoot, []data.PublicKey{origRootKey}, 1, nil) require.NoError(t, err) prevRoot, err := data.RootFromSigned(signedOrigTestRoot) require.NoError(t, err) // TUF key with PEM-encoded x509 certificate replRootKey, err := testutils.CreateKey(cs, gun, data.CanonicalRootRole, keyAlg) require.NoError(t, err) rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{replRootKey.ID()}, nil) require.NoError(t, err) testRoot, err := data.NewRoot( map[string]data.PublicKey{replRootKey.ID(): replRootKey}, map[string]*data.RootRole{ data.CanonicalRootRole: &rootRole.RootRole, data.CanonicalTargetsRole: &rootRole.RootRole, data.CanonicalSnapshotRole: &rootRole.RootRole, data.CanonicalTimestampRole: &rootRole.RootRole, }, false, ) require.NoError(t, err, "Failed to create new root") signedTestRoot, err := testRoot.ToSigned() require.NoError(t, err) // We only sign with the old key, and not with the new one err = signed.Sign(cs, signedTestRoot, []data.PublicKey{origRootKey}, 1, nil) require.NoError(t, err) // This call to trustpinning.ValidateRoot will succeed since we are using a valid PEM // encoded certificate, and have no other certificates for this CN _, err = trustpinning.ValidateRoot(prevRoot, signedTestRoot, gun, trustpinning.TrustPinConfig{}) require.Error(t, err, "insuficient signatures on root") }
// 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) }
// If the parent, either from the DB or from an update, does not contain the role // of the delegation update, validation fails func TestValidateTargetsRoleNotInParent(t *testing.T) { // no delegation at first gun := "docker.com/notary" repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) meta, err := testutils.SignAndSerialize(repo) require.NoError(t, err) // load the root into the builder, else we can't load anything else builder := tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{}) require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 0, false)) // prepare the original targets file, without a delegation role, as an update origTargetsUpdate := storage.MetaUpdate{ Role: data.CanonicalTargetsRole, Version: 1, Data: meta[data.CanonicalTargetsRole], } emptyStore := storage.NewMemStorage() storeWithParent := storage.NewMemStorage() storeWithParent.UpdateCurrent(gun, origTargetsUpdate) // add a delegation role now delgName := "targets/level1" level1Key, err := testutils.CreateKey(cs, gun, delgName, data.ECDSAKey) require.NoError(t, err) require.NoError(t, repo.UpdateDelegationKeys(delgName, []data.PublicKey{level1Key}, []string{}, 1)) // create the delegation metadata too repo.InitTargets(delgName) // re-serialize meta, err = testutils.SignAndSerialize(repo) require.NoError(t, err) delgMeta, ok := meta[delgName] require.True(t, ok) delgUpdate := storage.MetaUpdate{ Role: delgName, Version: 1, Data: delgMeta, } // parent in update does not have this role, whether or not there's a parent in the store, // so validation fails roles := map[string]storage.MetaUpdate{ delgName: delgUpdate, data.CanonicalTargetsRole: origTargetsUpdate, } for _, metaStore := range []storage.MetaStore{emptyStore, storeWithParent} { updates, err := loadAndValidateTargets(gun, builder, roles, metaStore) require.Error(t, err) require.Empty(t, updates) require.IsType(t, validation.ErrBadTargets{}, err) } // if the update is provided without the parent, then the parent from the // store is loaded - if it doesn't have the role, then the update fails updates, err := loadAndValidateTargets(gun, builder, map[string]storage.MetaUpdate{delgName: delgUpdate}, storeWithParent) require.Error(t, err) require.Empty(t, updates) require.IsType(t, validation.ErrBadTargets{}, err) }