Beispiel #1
0
func checkOnlySnapshotConsistentAfterTimestamp(t *testing.T, repo *tuf.Repo, meta map[string][]byte, extraMeta []string, builder tuf.RepoBuilder) {
	// 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())
		}
	}
}
Beispiel #2
0
// SetMeta sets the metadata value for the given name
func (m *MemoryStore) SetMeta(name string, meta []byte) error {
	m.meta[name] = meta

	checksum := sha256.Sum256(meta)
	path := utils.ConsistentName(name, checksum[:])
	m.consistent[path] = meta
	return nil
}
Beispiel #3
0
// RemoveMeta removes the metadata for a single role - if the metadata doesn't
// exist, no error is returned
func (m *MemoryStore) RemoveMeta(name string) error {
	if meta, ok := m.meta[name]; ok {
		checksum := sha256.Sum256(meta)
		path := utils.ConsistentName(name, checksum[:])
		delete(m.meta, name)
		delete(m.consistent, path)
	}
	return nil
}
Beispiel #4
0
func TestMemoryStoreMetadataOperations(t *testing.T) {
	s := NewMemoryStore(nil)

	// GetMeta of a non-existent metadata fails
	_, err := s.GetMeta("nonexistent", 0)
	require.Error(t, err)
	require.IsType(t, ErrMetaNotFound{}, err)

	// Once SetMeta succeeds, GetMeta with the role name and the consistent name
	// should succeed
	metaContent := []byte("content")
	metaSize := int64(len(metaContent))
	shasum := sha256.Sum256(metaContent)
	invalidShasum := sha256.Sum256([]byte{})

	require.NoError(t, s.SetMeta("exists", metaContent))
	require.NoError(t, s.SetMultiMeta(map[string][]byte{"multi1": metaContent, "multi2": metaContent}))

	for _, metaName := range []string{"exists", "multi1", "multi2"} {
		meta, err := s.GetMeta(metaName, metaSize)
		require.NoError(t, err)
		require.Equal(t, metaContent, meta)

		meta, err = s.GetMeta(utils.ConsistentName(metaName, shasum[:]), metaSize)
		require.NoError(t, err)
		require.Equal(t, metaContent, meta)

		_, err = s.GetMeta(utils.ConsistentName(metaName, invalidShasum[:]), metaSize)
		require.Error(t, err)
		require.IsType(t, ErrMetaNotFound{}, err)
	}

	// Once Metadata is removed, it's no longer accessible
	err = s.RemoveAll()
	require.NoError(t, err)

	_, err = s.GetMeta("exists", 0)
	require.Error(t, err)
	require.IsType(t, ErrMetaNotFound{}, err)
}
Beispiel #5
0
func TestRepoPrefixDoesNotMatch(t *testing.T) {
	gun := "docker.io/notary"
	meta, cs, err := testutils.NewRepoMetadata(gun)
	require.NoError(t, err)
	s := storage.NewMemStorage()

	ctx := context.WithValue(context.Background(), notary.CtxKeyMetaStore, s)
	ctx = context.WithValue(ctx, notary.CtxKeyKeyAlgo, data.ED25519Key)

	snChecksumBytes := sha256.Sum256(meta[data.CanonicalSnapshotRole])

	// successful gets
	handler := RootHandler(ctx, nil, cs, nil, nil, []string{"nope"})
	ts := httptest.NewServer(handler)

	url := fmt.Sprintf("%s/v2/%s/_trust/tuf/", ts.URL, gun)
	uploader, err := store.NewHTTPStore(url, "", "json", "key", http.DefaultTransport)
	require.NoError(t, err)

	require.Error(t, uploader.SetMulti(meta))

	// update the storage so we don't fail just because the metadata is missing
	for _, roleName := range data.BaseRoles {
		require.NoError(t, s.UpdateCurrent(gun, storage.MetaUpdate{
			Role:    roleName,
			Data:    meta[roleName],
			Version: 1,
		}))
	}

	_, err = uploader.GetSized(data.CanonicalSnapshotRole, notary.MaxDownloadSize)
	require.Error(t, err)

	_, err = uploader.GetSized(
		tufutils.ConsistentName(data.CanonicalSnapshotRole, snChecksumBytes[:]), notary.MaxDownloadSize)
	require.Error(t, err)

	_, err = uploader.GetKey(data.CanonicalTimestampRole)
	require.Error(t, err)

	// the httpstore doesn't actually delete all, so we do it manually
	req, err := http.NewRequest("DELETE", url, nil)
	require.NoError(t, err)
	res, err := http.DefaultTransport.RoundTrip(req)
	require.NoError(t, err)
	defer res.Body.Close()
	require.Equal(t, http.StatusNotFound, res.StatusCode)
}
Beispiel #6
0
// NewMemoryStore returns a MetadataStore that operates entirely in memory.
// Very useful for testing
func NewMemoryStore(initial map[string][]byte) *MemoryStore {
	var consistent = make(map[string][]byte)
	if initial == nil {
		initial = make(map[string][]byte)
	} else {
		// add all seed meta to consistent
		for name, data := range initial {
			checksum := sha256.Sum256(data)
			path := utils.ConsistentName(name, checksum[:])
			consistent[path] = data
		}
	}
	return &MemoryStore{
		data:       initial,
		consistent: consistent,
	}
}
Beispiel #7
0
// NewMemoryStore returns a MetadataStore that operates entirely in memory.
// Very useful for testing
func NewMemoryStore(meta map[string][]byte) *MemoryStore {
	var consistent = make(map[string][]byte)
	if meta == nil {
		meta = make(map[string][]byte)
	} else {
		// add all seed meta to consistent
		for name, data := range meta {
			checksum := sha256.Sum256(data)
			path := utils.ConsistentName(name, checksum[:])
			consistent[path] = data
		}
	}
	return &MemoryStore{
		meta:       meta,
		consistent: consistent,
		keys:       make(map[string][]data.PrivateKey),
	}
}
Beispiel #8
0
func (c *Client) downloadSigned(role string, size int64, expectedSha256 []byte) ([]byte, *data.Signed, error) {
	rolePath := utils.ConsistentName(role, expectedSha256)
	raw, err := c.remote.GetMeta(rolePath, size)
	if err != nil {
		return nil, nil, err
	}
	if expectedSha256 != nil {
		genHash := sha256.Sum256(raw)
		if !bytes.Equal(genHash[:], expectedSha256) {
			return nil, nil, ErrChecksumMismatch{role: role}
		}
	}
	s := &data.Signed{}
	err = json.Unmarshal(raw, s)
	if err != nil {
		return nil, nil, err
	}
	return raw, s, nil
}
Beispiel #9
0
func (c *Client) downloadSigned(role string, size int64, expectedHashes data.Hashes) ([]byte, *data.Signed, error) {
	rolePath := utils.ConsistentName(role, expectedHashes["sha256"])
	raw, err := c.remote.GetMeta(rolePath, size)
	if err != nil {
		return nil, nil, err
	}

	if expectedHashes != nil {
		if err := data.CheckHashes(raw, expectedHashes); err != nil {
			return nil, nil, ErrChecksumMismatch{role: role}
		}
	}

	s := &data.Signed{}
	err = json.Unmarshal(raw, s)
	if err != nil {
		return nil, nil, err
	}
	return raw, s, nil
}
Beispiel #10
0
func TestRepoPrefixMatches(t *testing.T) {
	gun := "docker.io/notary"
	meta, cs, err := testutils.NewRepoMetadata(gun)
	require.NoError(t, err)

	ctx := context.WithValue(context.Background(), notary.CtxKeyMetaStore, storage.NewMemStorage())
	ctx = context.WithValue(ctx, notary.CtxKeyKeyAlgo, data.ED25519Key)

	snChecksumBytes := sha256.Sum256(meta[data.CanonicalSnapshotRole])

	// successful gets
	handler := RootHandler(ctx, nil, cs, nil, nil, []string{"docker.io"})
	ts := httptest.NewServer(handler)

	url := fmt.Sprintf("%s/v2/%s/_trust/tuf/", ts.URL, gun)
	uploader, err := store.NewHTTPStore(url, "", "json", "key", http.DefaultTransport)
	require.NoError(t, err)

	// uploading is cool
	require.NoError(t, uploader.SetMulti(meta))
	// getting is cool
	_, err = uploader.GetSized(data.CanonicalSnapshotRole, notary.MaxDownloadSize)
	require.NoError(t, err)

	_, err = uploader.GetSized(
		tufutils.ConsistentName(data.CanonicalSnapshotRole, snChecksumBytes[:]), notary.MaxDownloadSize)
	require.NoError(t, err)

	_, err = uploader.GetKey(data.CanonicalTimestampRole)
	require.NoError(t, err)

	// the httpstore doesn't actually delete all, so we do it manually
	req, err := http.NewRequest("DELETE", url, nil)
	require.NoError(t, err)
	res, err := http.DefaultTransport.RoundTrip(req)
	require.NoError(t, err)
	defer res.Body.Close()
	require.Equal(t, http.StatusOK, res.StatusCode)
}
Beispiel #11
0
func checkOtherRolesConsistentAfterSnapshot(t *testing.T, repo *tuf.Repo, meta map[string][]byte, builder tuf.RepoBuilder) {
	// 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)
		}
	}
}
Beispiel #12
0
// ConsistentName returns the consistent name (rolename.sha256) for the role
// given this consistent information
func (c ConsistentInfo) ConsistentName() string {
	return utils.ConsistentName(c.RoleName, c.fileMeta.Hashes[notary.SHA256])
}
Beispiel #13
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())
		}
	}
}
Beispiel #14
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())
		}
	}
}