Use different limits for metadata downloads

This uses the same limits that python-tuf uses by default
to protect against large downloads. This should eventually
be controlled by the client.

Change-Id: Ie16b0ebd7cc6936e041a909b5d607c8c26cd9a08
diff --git a/client/client.go b/client/client.go
index 9e49269..dcb146b 100644
--- a/client/client.go
+++ b/client/client.go
@@ -12,6 +12,14 @@
 	"github.com/flynn/go-tuf/verify"
 )
 
+const (
+	// This is the upper limit in bytes we will use to limit the download
+	// size of the root/timestamp roles, since we might not don't know how
+	// big it is.
+	defaultRootDownloadLimit      = 512000
+	defaultTimestampDownloadLimit = 16384
+)
+
 // LocalStore is local storage for downloaded top-level metadata.
 type LocalStore interface {
 	// GetMeta returns top-level metadata from local storage. The keys are
@@ -91,7 +99,7 @@
 	if len(rootKeys) < threshold {
 		return ErrInsufficientKeys
 	}
-	rootJSON, err := c.downloadMetaUnsafe("root.json")
+	rootJSON, err := c.downloadMetaUnsafe("root.json", defaultRootDownloadLimit)
 	if err != nil {
 		return err
 	}
@@ -154,7 +162,7 @@
 
 	// Get timestamp.json, extract snapshot.json file meta and save the
 	// timestamp.json locally
-	timestampJSON, err := c.downloadMetaUnsafe("timestamp.json")
+	timestampJSON, err := c.downloadMetaUnsafe("timestamp.json", defaultTimestampDownloadLimit)
 	if err != nil {
 		return nil, err
 	}
@@ -230,7 +238,7 @@
 	var rootJSON json.RawMessage
 	var err error
 	if m == nil {
-		rootJSON, err = c.downloadMetaUnsafe("root.json")
+		rootJSON, err = c.downloadMetaUnsafe("root.json", defaultRootDownloadLimit)
 	} else {
 		rootJSON, err = c.downloadMetaFromSnapshot("root.json", *m)
 	}
@@ -328,14 +336,10 @@
 	}
 }
 
-// maxMetaSize is the maximum number of bytes that will be downloaded when
-// getting remote metadata without knowing it's length.
-const maxMetaSize = 50 * 1024
-
 // downloadMetaUnsafe downloads top-level metadata from remote storage without
 // verifying it's length and hashes (used for example to download timestamp.json
 // which has unknown size). It will download at most maxMetaSize bytes.
-func (c *Client) downloadMetaUnsafe(name string) ([]byte, error) {
+func (c *Client) downloadMetaUnsafe(name string, maxMetaSize int64) ([]byte, error) {
 	r, size, err := c.remote.GetMeta(name)
 	if err != nil {
 		if IsNotFound(err) {
@@ -347,7 +351,7 @@
 
 	// return ErrMetaTooLarge if the reported size is greater than maxMetaSize
 	if size > maxMetaSize {
-		return nil, ErrMetaTooLarge{name, size}
+		return nil, ErrMetaTooLarge{name, size, maxMetaSize}
 	}
 
 	// although the size has been checked above, use a LimitReader in case
diff --git a/client/client_test.go b/client/client_test.go
index 911923e..1e6569d 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -230,8 +230,8 @@
 
 func (s *ClientSuite) TestInitRootTooLarge(c *C) {
 	client := NewClient(MemoryLocalStore(), s.remote)
-	s.remote.meta["root.json"] = newFakeFile(make([]byte, maxMetaSize+1))
-	c.Assert(client.Init(s.rootKeys(c), 0), Equals, ErrMetaTooLarge{"root.json", maxMetaSize + 1})
+	s.remote.meta["root.json"] = newFakeFile(make([]byte, defaultRootDownloadLimit+1))
+	c.Assert(client.Init(s.rootKeys(c), 0), Equals, ErrMetaTooLarge{"root.json", defaultRootDownloadLimit + 1, defaultRootDownloadLimit})
 }
 
 func (s *ClientSuite) TestInitRootExpired(c *C) {
@@ -526,9 +526,9 @@
 }
 
 func (s *ClientSuite) TestTimestampTooLarge(c *C) {
-	s.remote.meta["timestamp.json"] = newFakeFile(make([]byte, maxMetaSize+1))
+	s.remote.meta["timestamp.json"] = newFakeFile(make([]byte, defaultTimestampDownloadLimit+1))
 	_, err := s.newClient(c).Update()
-	c.Assert(err, Equals, ErrMetaTooLarge{"timestamp.json", maxMetaSize + 1})
+	c.Assert(err, Equals, ErrMetaTooLarge{"timestamp.json", defaultTimestampDownloadLimit + 1, defaultTimestampDownloadLimit})
 }
 
 func (s *ClientSuite) TestUpdateLocalRootExpired(c *C) {
diff --git a/client/errors.go b/client/errors.go
index c769d15..fcc17b8 100644
--- a/client/errors.go
+++ b/client/errors.go
@@ -89,12 +89,13 @@
 }
 
 type ErrMetaTooLarge struct {
-	Name string
-	Size int64
+	Name    string
+	Size    int64
+	MaxSize int64
 }
 
 func (e ErrMetaTooLarge) Error() string {
-	return fmt.Sprintf("tuf: %s size %d bytes greater than maximum %d bytes", e.Name, e.Size, maxMetaSize)
+	return fmt.Sprintf("tuf: %s size %d bytes greater than maximum %d bytes", e.Name, e.Size, e.MaxSize)
 }
 
 type ErrInvalidURL struct {