Esempio n. 1
0
// downloadMeta downloads top-level metadata from remote storage and verifies
// it using the given file metadata.
func (c *Client) downloadMeta(name string, m data.FileMeta) ([]byte, error) {
	r, size, err := c.download(name, c.remote.GetMeta, m.Hashes)
	if err != nil {
		if IsNotFound(err) {
			return nil, ErrMissingRemoteMetadata{name}
		}
		return nil, err
	}
	defer r.Close()

	// return ErrWrongSize if the reported size is known and incorrect
	if size >= 0 && size != m.Length {
		return nil, ErrWrongSize{name, size, m.Length}
	}

	// wrap the data in a LimitReader so we download at most m.Length bytes
	stream := io.LimitReader(r, m.Length)

	// read the data, simultaneously writing it to buf and generating metadata
	var buf bytes.Buffer
	meta, err := util.GenerateFileMeta(io.TeeReader(stream, &buf), m.HashAlgorithms()...)
	if err != nil {
		return nil, err
	}
	if err := util.FileMetaEqual(meta, m); err != nil {
		return nil, ErrDownloadFailed{name, err}
	}
	return buf.Bytes(), nil
}
Esempio n. 2
0
// Download downloads the given target file from remote storage into dest.
//
// dest will be deleted and an error returned in the following situations:
//
//   * The target does not exist in the local targets.json
//   * The target does not exist in remote storage
//   * Metadata cannot be generated for the downloaded data
//   * Generated metadata does not match local metadata for the given file
func (c *Client) Download(name string, dest Destination) (err error) {
	// delete dest if there is an error
	defer func() {
		if err != nil {
			dest.Delete()
		}
	}()

	// populate c.targets from local storage if not set
	if c.targets == nil {
		if err := c.getLocalMeta(); err != nil {
			return err
		}
	}

	// return ErrUnknownTarget if the file is not in the local targets.json
	normalizedName := util.NormalizeTarget(name)
	localMeta, ok := c.targets[normalizedName]
	if !ok {
		return ErrUnknownTarget{name}
	}

	// get the data from remote storage
	r, size, err := c.download(normalizedName, c.remote.GetTarget, localMeta.Hashes)
	if err != nil {
		return err
	}
	defer r.Close()

	// return ErrWrongSize if the reported size is known and incorrect
	if size >= 0 && size != localMeta.Length {
		return ErrWrongSize{name, size, localMeta.Length}
	}

	// wrap the data in a LimitReader so we download at most localMeta.Length bytes
	stream := io.LimitReader(r, localMeta.Length)

	// read the data, simultaneously writing it to dest and generating metadata
	actual, err := util.GenerateFileMeta(io.TeeReader(stream, dest), localMeta.HashAlgorithms()...)
	if err != nil {
		return ErrDownloadFailed{name, err}
	}

	// check the data has the correct length and hashes
	if err := util.FileMetaEqual(actual, localMeta); err != nil {
		if err == util.ErrWrongLength {
			return ErrWrongSize{name, actual.Length, localMeta.Length}
		}
		return ErrDownloadFailed{name, err}
	}

	return nil
}
Esempio n. 3
0
// hasMeta checks whether local metadata has the given file meta
func (c *Client) hasMeta(name string, m data.FileMeta) bool {
	b, ok := c.localMeta[name]
	if !ok {
		return false
	}
	meta, err := util.GenerateFileMeta(bytes.NewReader(b), m.HashAlgorithms()...)
	if err != nil {
		return false
	}
	err = util.FileMetaEqual(meta, m)
	return err == nil
}
Esempio n. 4
0
func (InteropSuite) TestGoClientPythonGenerated(c *C) {
	// start file server
	cwd, err := os.Getwd()
	c.Assert(err, IsNil)
	testDataDir := filepath.Join(cwd, "testdata")
	addr, cleanup := startFileServer(c, testDataDir)
	defer cleanup()

	for _, dir := range []string{"with-consistent-snapshot", "without-consistent-snapshot"} {
		remote, err := HTTPRemoteStore(
			fmt.Sprintf("http://%s/%s/repository", addr, dir),
			&HTTPRemoteOptions{MetadataPath: "metadata", TargetsPath: "targets"},
		)
		c.Assert(err, IsNil)

		// initiate a client with the root keys
		f, err := os.Open(filepath.Join("testdata", dir, "keystore", "root_key.pub"))
		c.Assert(err, IsNil)
		key := &data.Key{}
		c.Assert(json.NewDecoder(f).Decode(key), IsNil)
		c.Assert(key.Type, Equals, "ed25519")
		c.Assert(key.Value.Public, HasLen, ed25519.PublicKeySize)
		client := NewClient(MemoryLocalStore(), remote)
		c.Assert(client.Init([]*data.Key{key}, 1), IsNil)

		// check update returns the correct updated targets
		files, err := client.Update()
		c.Assert(err, IsNil)
		c.Assert(files, HasLen, len(pythonTargets))
		for name, data := range pythonTargets {
			file, ok := files[name]
			if !ok {
				c.Fatalf("expected updated targets to contain %s", name)
			}
			meta, err := util.GenerateFileMeta(bytes.NewReader(data), file.HashAlgorithms()...)
			c.Assert(err, IsNil)
			c.Assert(util.FileMetaEqual(file, meta), IsNil)
		}

		// download the files and check they have the correct content
		for name, data := range pythonTargets {
			var dest testDestination
			c.Assert(client.Download(name, &dest), IsNil)
			c.Assert(dest.deleted, Equals, false)
			c.Assert(dest.String(), Equals, string(data))
		}
	}
}
Esempio n. 5
0
func assertFiles(c *C, files data.Files, names []string) {
	c.Assert(files, HasLen, len(names))
	for _, name := range names {
		target, ok := targetFiles[name]
		if !ok {
			c.Fatalf("unknown target %s", name)
		}
		file, ok := files[name]
		if !ok {
			c.Fatalf("expected files to contain %s", name)
		}
		meta, err := util.GenerateFileMeta(bytes.NewReader(target), file.HashAlgorithms()...)
		c.Assert(err, IsNil)
		c.Assert(util.FileMetaEqual(file, meta), IsNil)
	}
}
Esempio n. 6
0
// decodeTargets decodes and verifies targets metadata, sets c.targets and
// returns updated targets.
func (c *Client) decodeTargets(b json.RawMessage) (data.Files, error) {
	targets := &data.Targets{}
	if err := signed.Unmarshal(b, targets, "targets", c.targetsVer, c.db); err != nil {
		return nil, ErrDecodeFailed{"targets.json", err}
	}
	updatedTargets := make(data.Files)
	for path, meta := range targets.Targets {
		if local, ok := c.targets[path]; ok {
			if err := util.FileMetaEqual(local, meta); err == nil {
				continue
			}
		}
		updatedTargets[path] = meta
	}
	c.targetsVer = targets.Version
	c.targets = targets.Targets
	return updatedTargets, nil
}