Example #1
0
// Creates metadata in the following manner:
// - the snapshot has bad checksums for itself and for timestamp, to show that those aren't checked
// - snapshot has valid checksums for root, targets, and targets/other
// - snapshot doesn't have a checksum for targets/other/other, but targets/other/other is a valid
//   delegation role in targets/other and there is metadata for targets/other/other that is correctly
//   signed
func setupSnapshotChecksumming(t *testing.T, gun string) map[string][]byte {
	repo, _, err := testutils.EmptyRepo(gun, "targets/other", "targets/other/other")
	require.NoError(t, err)

	// add invalid checkums for all the other roles to timestamp too, and show that
	// cached items aren't checksummed against this
	fakeChecksum, err := data.NewFileMeta(bytes.NewBuffer([]byte("fake")), notary.SHA256, notary.SHA512)
	require.NoError(t, err)
	// fake the snapshot and timestamp checksums
	repo.Snapshot.Signed.Meta[data.CanonicalSnapshotRole] = fakeChecksum
	repo.Snapshot.Signed.Meta[data.CanonicalTimestampRole] = fakeChecksum

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)
	// ensure that the fake metadata for other roles wasn't destroyed by signing
	require.Len(t, repo.Snapshot.Signed.Meta, 5)

	// create delegation metadata that should not be in snapshot, but has a valid role and signature
	_, err = repo.InitTargets("targets/other/other")
	require.NoError(t, err)
	s, err := repo.SignTargets("targets/other/other", data.DefaultExpires(data.CanonicalTargetsRole))
	require.NoError(t, err)
	meta["targets/other/other"], err = json.Marshal(s)
	require.NoError(t, err)

	return meta
}
Example #2
0
func TestBuilderLoadInvalidDelegationsOldVersion(t *testing.T) {
	gun := "docker.com/notary"
	tufRepo, _, err := testutils.EmptyRepo(gun, "targets/a", "targets/a/b", "targets/b")
	require.NoError(t, err)

	meta, err := testutils.SignAndSerialize(tufRepo)
	require.NoError(t, err)

	builder := tuf.NewBuilderFromRepo(gun, tufRepo, trustpinning.TrustPinConfig{})
	delete(tufRepo.Targets, "targets/a")

	// load targets/a with high min-version so this one is too old
	err = builder.Load(
		"targets/a",
		meta["targets/a"],
		10,
		false,
	)
	require.Error(t, err)
	require.IsType(t, signed.ErrLowVersion{}, err)

	_, invalid, err := builder.Finish()
	require.NoError(t, err)
	_, ok := invalid.Targets["targets/a"]
	require.False(t, ok)
}
Example #3
0
// If there is no previous timestamp or the previous timestamp is corrupt, then
// even if everything else is in place, getting the timestamp fails
func TestGetTimestampNoPreviousTimestamp(t *testing.T) {
	repo, crypto, err := testutils.EmptyRepo("gun")
	require.NoError(t, err)

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	for _, timestampJSON := range [][]byte{nil, []byte("invalid JSON")} {
		store := storage.NewMemStorage()

		// so we know it's not a failure in getting root or snapshot
		require.NoError(t,
			store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0,
				Data: meta[data.CanonicalRootRole]}))
		require.NoError(t,
			store.UpdateCurrent("gun", storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0,
				Data: meta[data.CanonicalSnapshotRole]}))

		if timestampJSON != nil {
			require.NoError(t,
				store.UpdateCurrent("gun",
					storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 0, Data: timestampJSON}))
		}

		_, _, err = GetOrCreateTimestamp("gun", store, crypto)
		require.Error(t, err, "GetTimestamp should have failed")
		if timestampJSON == nil {
			require.IsType(t, storage.ErrNotFound{}, err)
		} else {
			require.IsType(t, &json.SyntaxError{}, err)
		}
	}
}
Example #4
0
func TestCreateTimestampNoKeyInCrypto(t *testing.T) {
	store := storage.NewMemStorage()
	repo, _, err := testutils.EmptyRepo("gun")
	require.NoError(t, err)

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	// create an expired timestamp
	_, err = repo.SignTimestamp(time.Now().AddDate(-1, -1, -1))
	require.True(t, repo.Timestamp.Signed.Expires.Before(time.Now()))
	require.NoError(t, err)
	timestampJSON, err := json.Marshal(repo.Timestamp)
	require.NoError(t, err)

	// set all the metadata so we know the failure to sign is just because of the key
	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: meta[data.CanonicalRootRole]}))
	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: meta[data.CanonicalSnapshotRole]}))
	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 1, Data: timestampJSON}))

	// pass it a new cryptoservice without the key
	_, _, err = GetOrCreateTimestamp("gun", store, signed.NewEd25519())
	require.Error(t, err)
	require.IsType(t, signed.ErrInsufficientSignatures{}, err)
}
Example #5
0
func TestGetTimestampOldTimestampExpired(t *testing.T) {
	store := storage.NewMemStorage()
	repo, crypto, err := testutils.EmptyRepo("gun")
	require.NoError(t, err)

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	// create an expired timestamp
	_, err = repo.SignTimestamp(time.Now().AddDate(-1, -1, -1))
	require.True(t, repo.Timestamp.Signed.Expires.Before(time.Now()))
	require.NoError(t, err)
	timestampJSON, err := json.Marshal(repo.Timestamp)
	require.NoError(t, err)

	// set all the metadata
	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalRootRole, Version: 0, Data: meta[data.CanonicalRootRole]}))
	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: meta[data.CanonicalSnapshotRole]}))
	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 1, Data: timestampJSON}))

	_, gottenTimestamp, err := GetOrCreateTimestamp("gun", store, crypto)
	require.NoError(t, err, "GetTimestamp errored")

	require.False(t, bytes.Equal(timestampJSON, gottenTimestamp),
		"Timestamp was not regenerated when old one was expired")

	signedMeta := &data.SignedMeta{}
	require.NoError(t, json.Unmarshal(gottenTimestamp, signedMeta))
	// the new metadata is not expired
	require.True(t, signedMeta.Signed.Expires.After(time.Now()))
}
Example #6
0
// No matter what order timestamp and snapshot is loaded, if the snapshot's checksum doesn't match
// what's in the timestamp, the builder will error and refuse to load the latest piece of metadata
// whether that is snapshot (because it was loaded after timestamp) or timestamp (because builder
// retroactive checks the loaded snapshot's checksum).  Timestamp ONLY checks the snapshot checksum.
func TestTimestampPreAndPostChecksumming(t *testing.T) {
	gun := "docker.com/notary"
	repo, _, err := testutils.EmptyRepo(gun, "targets/other", "targets/other/other")
	require.NoError(t, err)

	// add invalid checkums for all the other roles to timestamp too, and show that
	// cached items aren't checksummed against this
	fakeChecksum, err := data.NewFileMeta(bytes.NewBuffer([]byte("fake")), notary.SHA256, notary.SHA512)
	require.NoError(t, err)
	for _, roleName := range append(data.BaseRoles, "targets/other") {
		// add a wrong checksum for every role, including timestamp itself
		repo.Timestamp.Signed.Meta[roleName] = fakeChecksum
	}
	// this will overwrite the snapshot checksum with the right one
	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)
	// ensure that the fake meta for other roles weren't destroyed by signing the timestamp
	require.Len(t, repo.Timestamp.Signed.Meta, 5)

	snapJSON := append(meta[data.CanonicalSnapshotRole], ' ')

	// --- load timestamp first
	builder := tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{})
	require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false))
	// timestamp doesn't fail, even though its checksum for root is wrong according to timestamp
	require.NoError(t, builder.Load(data.CanonicalTimestampRole, meta[data.CanonicalTimestampRole], 1, false))
	// loading the snapshot in fails, because of the checksum the timestamp has
	err = builder.Load(data.CanonicalSnapshotRole, snapJSON, 1, false)
	require.Error(t, err)
	require.IsType(t, data.ErrMismatchedChecksum{}, err)
	require.True(t, builder.IsLoaded(data.CanonicalTimestampRole))
	require.False(t, builder.IsLoaded(data.CanonicalSnapshotRole))
	// all the other metadata can be loaded in, even though the checksums are wrong according to timestamp
	for _, roleName := range []string{data.CanonicalTargetsRole, "targets/other"} {
		require.NoError(t, builder.Load(roleName, meta[roleName], 1, false))
	}

	// --- load snapshot first
	builder = tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{})
	for _, roleName := range append(data.BaseRoles, "targets/other") {
		switch roleName {
		case data.CanonicalTimestampRole:
			continue
		case data.CanonicalSnapshotRole:
			require.NoError(t, builder.Load(roleName, snapJSON, 1, false))
		default:
			require.NoError(t, builder.Load(roleName, meta[roleName], 1, false))
		}
	}
	// timestamp fails because the snapshot checksum is wrong
	err = builder.Load(data.CanonicalTimestampRole, meta[data.CanonicalTimestampRole], 1, false)
	require.Error(t, err)
	checksumErr, ok := err.(data.ErrMismatchedChecksum)
	require.True(t, ok)
	require.Contains(t, checksumErr.Error(), "checksum for snapshot did not match")
	require.False(t, builder.IsLoaded(data.CanonicalTimestampRole))
	require.True(t, builder.IsLoaded(data.CanonicalSnapshotRole))
}
Example #7
0
// If the root or snapshot is missing or corrupt, no timestamp can be generated
func TestCannotMakeNewTimestampIfNoRootOrSnapshot(t *testing.T) {
	repo, crypto, err := testutils.EmptyRepo("gun")
	require.NoError(t, err)

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	// create an expired timestamp
	_, err = repo.SignTimestamp(time.Now().AddDate(-1, -1, -1))
	require.True(t, repo.Timestamp.Signed.Expires.Before(time.Now()))
	require.NoError(t, err)
	timestampJSON, err := json.Marshal(repo.Timestamp)
	require.NoError(t, err)

	rootJSON := meta[data.CanonicalRootRole]
	snapJSON := meta[data.CanonicalSnapshotRole]

	invalids := []struct {
		test map[string][]byte
		err  error
	}{
		{
			test: map[string][]byte{data.CanonicalRootRole: rootJSON, data.CanonicalSnapshotRole: []byte("invalid JSON")},
			err:  storage.ErrNotFound{},
		},
		{
			test: map[string][]byte{data.CanonicalRootRole: []byte("invalid JSON"), data.CanonicalSnapshotRole: snapJSON},
			err:  &json.SyntaxError{},
		},
		{
			test: map[string][]byte{data.CanonicalRootRole: rootJSON},
			err:  storage.ErrNotFound{},
		},
		{
			test: map[string][]byte{data.CanonicalSnapshotRole: snapJSON},
			err:  storage.ErrNotFound{},
		},
	}

	for _, test := range invalids {
		dataToSet := test.test
		store := storage.NewMemStorage()
		for roleName, jsonBytes := range dataToSet {
			require.NoError(t, store.UpdateCurrent("gun",
				storage.MetaUpdate{Role: roleName, Version: 0, Data: jsonBytes}))
		}
		require.NoError(t, store.UpdateCurrent("gun",
			storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 1, Data: timestampJSON}))

		_, _, err := GetOrCreateTimestamp("gun", store, crypto)
		require.Error(t, err, "GetTimestamp errored")
		require.IsType(t, test.err, err)
	}
}
Example #8
0
func TestBuilderLoadInvalidDelegations(t *testing.T) {
	gun := "docker.com/notary"
	tufRepo, _, err := testutils.EmptyRepo(gun, "targets/a", "targets/a/b", "targets/b")
	require.NoError(t, err)

	meta, err := testutils.SignAndSerialize(tufRepo)
	require.NoError(t, err)

	builder := tuf.NewBuilderFromRepo(gun, tufRepo, trustpinning.TrustPinConfig{})

	// modify targets/a to remove the signature and update the snapshot
	// (we're not going to load the timestamp so no need to modify)
	targetsAJSON := meta["targets/a"]
	targetsA := data.Signed{}
	err = json.Unmarshal(targetsAJSON, &targetsA)
	require.NoError(t, err)
	targetsA.Signatures = make([]data.Signature, 0)
	targetsAJSON, err = json.Marshal(&targetsA)
	require.NoError(t, err)
	meta["targets/a"] = targetsAJSON
	delete(tufRepo.Targets, "targets/a")

	snap := tufRepo.Snapshot
	m, err := data.NewFileMeta(
		bytes.NewReader(targetsAJSON),
		"sha256", "sha512",
	)
	require.NoError(t, err)
	snap.AddMeta("targets/a", m)

	// load snapshot directly into repo to bypass signature check (we've invalidated
	// the signature by modifying it)
	tufRepo.Snapshot = snap

	// load targets/a
	require.Error(
		t,
		builder.Load(
			"targets/a",
			meta["targets/a"],
			1,
			false,
		),
	)

	_, invalid, err := builder.Finish()
	require.NoError(t, err)
	_, ok := invalid.Targets["targets/a"]
	require.True(t, ok)
}
Example #9
0
// If there WAS a pre-existing timestamp, and it is not expired, then just return it (it doesn't
// load any other metadata that it doesn't need, like root)
func TestGetTimestampReturnsPreviousTimestampIfUnexpired(t *testing.T) {
	store := storage.NewMemStorage()
	repo, crypto, err := testutils.EmptyRepo("gun")
	require.NoError(t, err)

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalSnapshotRole, Version: 0, Data: meta[data.CanonicalSnapshotRole]}))
	require.NoError(t, store.UpdateCurrent("gun",
		storage.MetaUpdate{Role: data.CanonicalTimestampRole, Version: 0, Data: meta[data.CanonicalTimestampRole]}))

	_, gottenTimestamp, err := GetOrCreateTimestamp("gun", store, crypto)
	require.NoError(t, err, "GetTimestamp should not have failed")
	require.True(t, bytes.Equal(meta[data.CanonicalTimestampRole], gottenTimestamp))
}
Example #10
0
// Produce a series of tufMeta objects and updates given a TUF repo
func metaFromRepo(t *testing.T, gun string, version int) map[string]StoredTUFMeta {
	tufRepo, _, err := testutils.EmptyRepo(gun, "targets/a", "targets/a/b")
	require.NoError(t, err)

	tufRepo.Root.Signed.Version = version - 1
	tufRepo.Timestamp.Signed.Version = version - 1
	tufRepo.Snapshot.Signed.Version = version - 1
	for _, signedObj := range tufRepo.Targets {
		signedObj.Signed.Version = version - 1
	}

	metaBytes, err := testutils.SignAndSerialize(tufRepo)
	require.NoError(t, err)

	tufMeta := make(map[string]StoredTUFMeta)
	for role, tufdata := range metaBytes {
		tufMeta[role] = SampleCustomTUFObj(gun, role, version, tufdata)
	}

	return tufMeta
}
Example #11
0
// 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)
}
Example #12
0
func TestGetConsistentInfo(t *testing.T) {
	gun := "docker.com/notary"
	repo, _, err := testutils.EmptyRepo(gun)
	require.NoError(t, err)

	// add some hashes for items in the snapshot that don't correspond to real metadata, but that
	// will cause ConsistentInfo to behave differently
	realSha512Sum := sha512.Sum512([]byte("stuff"))
	repo.Snapshot.Signed.Meta["only512"] = data.FileMeta{Hashes: data.Hashes{notary.SHA512: realSha512Sum[:]}}
	repo.Snapshot.Signed.Meta["targets/random"] = data.FileMeta{Hashes: data.Hashes{"randomsha": []byte("12345")}}
	repo.Snapshot.Signed.Meta["targets/nohashes"] = data.FileMeta{Length: 1}

	extraMeta := []string{"only512", "targets/random", "targets/nohashes"}

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	builder := tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{})
	// if neither snapshot nor timestamp are loaded, no matter how much other data is loaded, consistent info
	// is empty except for timestamp: timestamps have no checksums, and the length is always -1
	for _, roleToLoad := range []string{data.CanonicalRootRole, data.CanonicalTargetsRole} {
		require.NoError(t, builder.Load(roleToLoad, meta[roleToLoad], 1, false))
		for _, checkName := range append(data.BaseRoles, extraMeta...) {
			ci := builder.GetConsistentInfo(checkName)
			require.Equal(t, checkName, ci.ConsistentName())

			switch checkName {
			case data.CanonicalTimestampRole:
				// timestamp's size is always the max timestamp size
				require.True(t, ci.ChecksumKnown())
				require.Equal(t, notary.MaxTimestampSize, ci.Length())
			default:
				require.False(t, ci.ChecksumKnown())
				require.Equal(t, int64(-1), ci.Length())
			}
		}
	}

	// once timestamp is loaded, we can get the consistent info for snapshot but nothing else
	require.NoError(t, builder.Load(data.CanonicalTimestampRole, meta[data.CanonicalTimestampRole], 1, false))
	for _, checkName := range append(data.BaseRoles, extraMeta...) {
		ci := builder.GetConsistentInfo(checkName)

		switch checkName {
		case data.CanonicalSnapshotRole:
			cName := utils.ConsistentName(data.CanonicalSnapshotRole,
				repo.Timestamp.Signed.Meta[data.CanonicalSnapshotRole].Hashes[notary.SHA256])
			require.Equal(t, cName, ci.ConsistentName())
			require.True(t, ci.ChecksumKnown())
			require.True(t, ci.Length() > -1)
		case data.CanonicalTimestampRole:
			// timestamp's canonical name is always "timestamp" and its size is always the max
			// timestamp size
			require.Equal(t, data.CanonicalTimestampRole, ci.ConsistentName())
			require.True(t, ci.ChecksumKnown())
			require.Equal(t, notary.MaxTimestampSize, ci.Length())
		default:
			require.Equal(t, checkName, ci.ConsistentName())
			require.False(t, ci.ChecksumKnown())
			require.Equal(t, int64(-1), ci.Length())
		}
	}

	// once the snapshot is loaded, we can get real consistent info for all loaded roles
	require.NoError(t, builder.Load(data.CanonicalSnapshotRole, meta[data.CanonicalSnapshotRole], 1, false))
	for _, checkName := range data.BaseRoles {
		ci := builder.GetConsistentInfo(checkName)
		require.True(t, ci.ChecksumKnown(), "%s's checksum is not known", checkName)

		switch checkName {
		case data.CanonicalTimestampRole:
			// timestamp's canonical name is always "timestamp" and its size is always -1
			require.Equal(t, data.CanonicalTimestampRole, ci.ConsistentName())
			require.Equal(t, notary.MaxTimestampSize, ci.Length())
		default:
			fileInfo := repo.Snapshot.Signed.Meta
			if checkName == data.CanonicalSnapshotRole {
				fileInfo = repo.Timestamp.Signed.Meta
			}

			cName := utils.ConsistentName(checkName, fileInfo[checkName].Hashes[notary.SHA256])
			require.Equal(t, cName, ci.ConsistentName())
			require.True(t, ci.Length() > -1)
		}
	}

	// the fake roles have invalid-ish checksums: the ConsistentInfos for those will return
	// non-consistent names but non -1 sizes
	for _, checkName := range extraMeta {
		ci := builder.GetConsistentInfo(checkName)
		require.Equal(t, checkName, ci.ConsistentName()) // because no sha256 hash
		require.True(t, ci.ChecksumKnown())
		require.True(t, ci.Length() > -1)
	}

	// a non-existent role's ConsistentInfo is empty
	ci := builder.GetConsistentInfo("nonExistent")
	require.Equal(t, "nonExistent", ci.ConsistentName())
	require.False(t, ci.ChecksumKnown())
	require.Equal(t, int64(-1), ci.Length())

	// when we bootstrap a new builder, the root has consistent info because the checksum is provided,
	// but nothing else does
	builder = builder.BootstrapNewBuilder()
	for _, checkName := range append(data.BaseRoles, extraMeta...) {
		ci := builder.GetConsistentInfo(checkName)

		switch checkName {
		case data.CanonicalTimestampRole:
			// timestamp's size is always the max timestamp size
			require.Equal(t, checkName, ci.ConsistentName())
			require.True(t, ci.ChecksumKnown())
			require.Equal(t, notary.MaxTimestampSize, ci.Length())

		case data.CanonicalRootRole:
			cName := utils.ConsistentName(data.CanonicalRootRole,
				repo.Snapshot.Signed.Meta[data.CanonicalRootRole].Hashes[notary.SHA256])

			require.Equal(t, cName, ci.ConsistentName())
			require.True(t, ci.ChecksumKnown())
			require.True(t, ci.Length() > -1)

		default:
			require.Equal(t, checkName, ci.ConsistentName())
			require.False(t, ci.ChecksumKnown())
			require.Equal(t, int64(-1), ci.Length())
		}
	}
}
Example #13
0
// Test the cases in which GenerateTimestamp fails
func TestGenerateTimestampInvalidOperations(t *testing.T) {
	gun := "docker.com/notary"
	repo, cs, err := testutils.EmptyRepo(gun)
	require.NoError(t, err)

	// make timsetamp have 2 keys and a threshold of 2
	tsKeys := make([]data.PublicKey, 2)
	for i := 0; i < 2; i++ {
		tsKeys[i], err = cs.Create(data.CanonicalTimestampRole, gun, data.ECDSAKey)
		require.NoError(t, err)
	}

	require.NoError(t, repo.ReplaceBaseKeys(data.CanonicalTimestampRole, tsKeys...))
	repo.Root.Signed.Roles[data.CanonicalTimestampRole].Threshold = 2

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	for _, prevTimestamp := range []*data.SignedTimestamp{nil, repo.Timestamp} {
		// --- we can't generate a timestamp if the root isn't loaded
		builder := tuf.NewRepoBuilder(gun, cs, trustpinning.TrustPinConfig{})
		_, _, err := builder.GenerateTimestamp(prevTimestamp)
		require.IsType(t, tuf.ErrInvalidBuilderInput{}, err)
		require.Contains(t, err.Error(), "root must be loaded first")
		require.False(t, builder.IsLoaded(data.CanonicalTimestampRole))

		// --- we can't generate a timestamp if the snapshot isn't loaded, no matter if we have a previous
		// --- timestamp or not
		require.NoError(t, builder.Load(data.CanonicalRootRole, meta[data.CanonicalRootRole], 1, false))
		_, _, err = builder.GenerateTimestamp(prevTimestamp)
		require.IsType(t, tuf.ErrInvalidBuilderInput{}, err)
		require.Contains(t, err.Error(), "snapshot must be loaded first")
		require.False(t, builder.IsLoaded(data.CanonicalTimestampRole))

		// --- we can't generate a timestamp if we've loaded the timestamp already
		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.CanonicalSnapshotRole, meta[data.CanonicalSnapshotRole], 1, false))
		require.NoError(t, builder.Load(data.CanonicalTimestampRole, meta[data.CanonicalTimestampRole], 1, false))

		_, _, err = builder.GenerateTimestamp(prevTimestamp)
		require.IsType(t, tuf.ErrInvalidBuilderInput{}, err)
		require.Contains(t, err.Error(), "timestamp has already been loaded")

		// --- we cannot generate a timestamp if we can't satisfy the role threshold
		for i := 0; i < len(tsKeys); i++ {
			require.NoError(t, cs.RemoveKey(tsKeys[i].ID()))
			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.CanonicalSnapshotRole, meta[data.CanonicalSnapshotRole], 1, false))

			_, _, err = builder.GenerateTimestamp(prevTimestamp)
			require.IsType(t, signed.ErrInsufficientSignatures{}, err)
			require.False(t, builder.IsLoaded(data.CanonicalTimestampRole))
		}

		// --- we cannot generate a timestamp 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))
		require.NoError(t, builder.Load(data.CanonicalSnapshotRole, meta[data.CanonicalSnapshotRole], 1, false))

		_, _, err = builder.GenerateTimestamp(prevTimestamp)
		require.IsType(t, tuf.ErrInvalidBuilderInput{}, err)
		require.Contains(t, err.Error(), "cannot generate timestamp without a cryptoservice")
		require.False(t, builder.IsLoaded(data.CanonicalTimestampRole))
	}

	// --- we can't generate a timsetamp if we're given an invalid previous timestamp (for instance, an empty one),
	// --- even if we have a snapshot 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.CanonicalSnapshotRole, meta[data.CanonicalSnapshotRole], 1, false))

	_, _, err = builder.GenerateTimestamp(&data.SignedTimestamp{})
	require.IsType(t, data.ErrInvalidMetadata{}, err)
	require.False(t, builder.IsLoaded(data.CanonicalTimestampRole))
}
Example #14
0
// 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))
}
Example #15
0
func TestGetConsistentInfo(t *testing.T) {
	gun := "docker.com/notary"
	repo, _, err := testutils.EmptyRepo(gun)
	require.NoError(t, err)

	// add some hashes for items in the snapshot that don't correspond to real metadata, but that
	// will cause ConsistentInfo to behave differently
	realSha512Sum := sha512.Sum512([]byte("stuff"))
	repo.Snapshot.Signed.Meta["only512"] = data.FileMeta{Hashes: data.Hashes{notary.SHA512: realSha512Sum[:]}}
	repo.Snapshot.Signed.Meta["targets/random"] = data.FileMeta{Hashes: data.Hashes{"randomsha": []byte("12345")}}
	repo.Snapshot.Signed.Meta["targets/nohashes"] = data.FileMeta{Length: 1}

	extraMeta := []string{"only512", "targets/random", "targets/nohashes"}

	meta, err := testutils.SignAndSerialize(repo)
	require.NoError(t, err)

	builder := tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{})
	checkTimestampSnapshotRequired(t, meta, extraMeta, builder)
	checkOnlySnapshotConsistentAfterTimestamp(t, repo, meta, extraMeta, builder)
	checkOtherRolesConsistentAfterSnapshot(t, repo, meta, builder)

	// the fake roles have invalid-ish checksums: the ConsistentInfos for those will return
	// non-consistent names but non -1 sizes
	for _, checkName := range extraMeta {
		ci := builder.GetConsistentInfo(checkName)
		require.Equal(t, checkName, ci.ConsistentName()) // because no sha256 hash
		require.True(t, ci.ChecksumKnown())
		require.True(t, ci.Length() > -1)
	}

	// a non-existent role's ConsistentInfo is empty
	ci := builder.GetConsistentInfo("nonExistent")
	require.Equal(t, "nonExistent", ci.ConsistentName())
	require.False(t, ci.ChecksumKnown())
	require.Equal(t, int64(-1), ci.Length())

	// when we bootstrap a new builder, the root has consistent info because the checksum is provided,
	// but nothing else does
	builder = builder.BootstrapNewBuilder()
	for _, checkName := range append(data.BaseRoles, extraMeta...) {
		ci := builder.GetConsistentInfo(checkName)

		switch checkName {
		case data.CanonicalTimestampRole:
			// timestamp's size is always the max timestamp size
			require.Equal(t, checkName, ci.ConsistentName())
			require.True(t, ci.ChecksumKnown())
			require.Equal(t, notary.MaxTimestampSize, ci.Length())

		case data.CanonicalRootRole:
			cName := utils.ConsistentName(data.CanonicalRootRole,
				repo.Snapshot.Signed.Meta[data.CanonicalRootRole].Hashes[notary.SHA256])

			require.Equal(t, cName, ci.ConsistentName())
			require.True(t, ci.ChecksumKnown())
			require.True(t, ci.Length() > -1)

		default:
			require.Equal(t, checkName, ci.ConsistentName())
			require.False(t, ci.ChecksumKnown())
			require.Equal(t, int64(-1), ci.Length())
		}
	}
}