From 8d230cf89cfe3c6c5293d7807c1bffa1cf799d6b Mon Sep 17 00:00:00 2001
From: Sam Alba <sam.alba@gmail.com>
Date: Sun, 23 Feb 2014 18:33:46 -0800
Subject: [PATCH] registry: Added simple checksums (sha256) for layers

Docker-DCO-1.1-Signed-off-by: Sam Alba <sam@docker.com> (github: samalba)
---
 registry/registry.go | 29 ++++++++++++++++++-----------
 server.go            |  3 ++-
 utils/checksum.go    | 24 ++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 12 deletions(-)
 create mode 100644 utils/checksum.go

diff --git a/registry/registry.go b/registry/registry.go
index df94302305..7bdf12c883 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -2,6 +2,7 @@ package registry
 
 import (
 	"bytes"
+	"crypto/sha256"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -388,6 +389,7 @@ func (r *Registry) PushImageChecksumRegistry(imgData *ImgData, registry string,
 	}
 	setTokenAuth(req, token)
 	req.Header.Set("X-Docker-Checksum", imgData.Checksum)
+	req.Header.Set("X-Docker-Checksum-Payload", imgData.ChecksumPayload)
 
 	res, err := doWithCookies(r.client, req)
 	if err != nil {
@@ -446,26 +448,28 @@ func (r *Registry) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regis
 	return nil
 }
 
-func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registry string, token []string, jsonRaw []byte) (checksum string, err error) {
+func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registry string, token []string, jsonRaw []byte) (checksum string, checksumPayload string, err error) {
 
 	utils.Debugf("[registry] Calling PUT %s", registry+"images/"+imgID+"/layer")
 
-	tarsumLayer := &utils.TarSum{Reader: layer}
+	h := sha256.New()
+	checksumLayer := &utils.CheckSum{Reader: layer, Hash: h}
+	tarsumLayer := &utils.TarSum{Reader: checksumLayer}
 
 	req, err := r.reqFactory.NewRequest("PUT", registry+"images/"+imgID+"/layer", tarsumLayer)
 	if err != nil {
-		return "", err
+		return "", "", err
 	}
 	req.ContentLength = -1
 	req.TransferEncoding = []string{"chunked"}
 	setTokenAuth(req, token)
 	res, err := doWithCookies(r.client, req)
 	if err != nil {
-		return "", fmt.Errorf("Failed to upload layer: %s", err)
+		return "", "", fmt.Errorf("Failed to upload layer: %s", err)
 	}
 	if rc, ok := layer.(io.Closer); ok {
 		if err := rc.Close(); err != nil {
-			return "", err
+			return "", "", err
 		}
 	}
 	defer res.Body.Close()
@@ -473,11 +477,13 @@ func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registr
 	if res.StatusCode != 200 {
 		errBody, err := ioutil.ReadAll(res.Body)
 		if err != nil {
-			return "", utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
+			return "", "", utils.NewHTTPRequestError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res)
 		}
-		return "", utils.NewHTTPRequestError(fmt.Sprintf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody), res)
+		return "", "", utils.NewHTTPRequestError(fmt.Sprintf("Received HTTP code %d while uploading layer: %s", res.StatusCode, errBody), res)
 	}
-	return tarsumLayer.Sum(jsonRaw), nil
+
+	checksumPayload = "sha256:" + checksumLayer.Sum()
+	return tarsumLayer.Sum(jsonRaw), checksumPayload, nil
 }
 
 // push a tag on the registry.
@@ -671,9 +677,10 @@ type RepositoryData struct {
 }
 
 type ImgData struct {
-	ID       string `json:"id"`
-	Checksum string `json:"checksum,omitempty"`
-	Tag      string `json:",omitempty"`
+	ID              string `json:"id"`
+	Checksum        string `json:"checksum,omitempty"`
+	ChecksumPayload string `json:"checksum,omitempty"`
+	Tag             string `json:",omitempty"`
 }
 
 type Registry struct {
diff --git a/server.go b/server.go
index 0ed96fee31..b9fd92716a 100644
--- a/server.go
+++ b/server.go
@@ -1504,11 +1504,12 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID,
 	defer os.RemoveAll(layerData.Name())
 
 	// Send the layer
-	checksum, err = r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf, false, utils.TruncateID(imgData.ID), "Pushing"), ep, token, jsonRaw)
+	checksum, checksumPayload, err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf, false, utils.TruncateID(imgData.ID), "Pushing"), ep, token, jsonRaw)
 	if err != nil {
 		return "", err
 	}
 	imgData.Checksum = checksum
+	imgData.ChecksumPayload = checksumPayload
 	// Send the checksum
 	if err := r.PushImageChecksumRegistry(imgData, ep, token); err != nil {
 		return "", err
diff --git a/utils/checksum.go b/utils/checksum.go
new file mode 100644
index 0000000000..1c85aa63a3
--- /dev/null
+++ b/utils/checksum.go
@@ -0,0 +1,24 @@
+package utils
+
+import (
+	"encoding/hex"
+	"hash"
+	"io"
+)
+
+type CheckSum struct {
+	io.Reader
+	Hash hash.Hash
+}
+
+func (cs *CheckSum) Read(buf []byte) (int, error) {
+	n, err := cs.Reader.Read(buf)
+	if err == nil {
+		cs.Hash.Write(buf[:n])
+	}
+	return n, err
+}
+
+func (cs *CheckSum) Sum() string {
+	return hex.EncodeToString(cs.Hash.Sum(nil))
+}