Browse code

tarsum: allow for alternate block ciphers

Not breaking the default cipher and algorithm for calculating checksums,
but allow for using alternate block ciphers during the checksum
calculation.

Docker-DCO-1.1-Signed-off-by: Vincent Batts <vbatts@redhat.com> (github: vbatts)

Vincent Batts authored on 2014/05/16 05:50:58
Showing 2 changed files
... ...
@@ -35,6 +35,14 @@ func NewTarSum(r io.Reader, dc bool, v Version) (TarSum, error) {
35 35
 	return &tarSum{Reader: r, DisableCompression: dc, tarSumVersion: v}, nil
36 36
 }
37 37
 
38
+// Create a new TarSum, providing a THash to use rather than the DefaultTHash
39
+func NewTarSumHash(r io.Reader, dc bool, v Version, tHash THash) (TarSum, error) {
40
+	if _, ok := tarSumVersions[v]; !ok {
41
+		return nil, ErrVersionNotImplemented
42
+	}
43
+	return &tarSum{Reader: r, DisableCompression: dc, tarSumVersion: v, tHash: tHash}, nil
44
+}
45
+
38 46
 // TarSum is the generic interface for calculating fixed time
39 47
 // checksums of a tar archive
40 48
 type TarSum interface {
... ...
@@ -42,6 +50,7 @@ type TarSum interface {
42 42
 	GetSums() FileInfoSums
43 43
 	Sum([]byte) string
44 44
 	Version() Version
45
+	Hash() THash
45 46
 }
46 47
 
47 48
 // tarSum struct is the structure for a Version0 checksum calculation
... ...
@@ -49,11 +58,12 @@ type tarSum struct {
49 49
 	io.Reader
50 50
 	tarR               *tar.Reader
51 51
 	tarW               *tar.Writer
52
-	gz                 writeCloseFlusher
52
+	writer             writeCloseFlusher
53 53
 	bufTar             *bytes.Buffer
54
-	bufGz              *bytes.Buffer
54
+	bufWriter          *bytes.Buffer
55 55
 	bufData            []byte
56 56
 	h                  hash.Hash
57
+	tHash              THash
57 58
 	sums               FileInfoSums
58 59
 	fileCounter        int64
59 60
 	currentFile        string
... ...
@@ -63,10 +73,36 @@ type tarSum struct {
63 63
 	tarSumVersion      Version // this field is not exported so it can not be mutated during use
64 64
 }
65 65
 
66
+func (ts tarSum) Hash() THash {
67
+	return ts.tHash
68
+}
69
+
66 70
 func (ts tarSum) Version() Version {
67 71
 	return ts.tarSumVersion
68 72
 }
69 73
 
74
+// A hash.Hash type generator and its name
75
+type THash interface {
76
+	Hash() hash.Hash
77
+	Name() string
78
+}
79
+
80
+// Convenience method for creating a THash
81
+func NewTHash(name string, h func() hash.Hash) THash {
82
+	return simpleTHash{n: name, h: h}
83
+}
84
+
85
+// TarSum default is "sha256"
86
+var DefaultTHash = NewTHash("sha256", sha256.New)
87
+
88
+type simpleTHash struct {
89
+	n string
90
+	h func() hash.Hash
91
+}
92
+
93
+func (sth simpleTHash) Name() string    { return sth.n }
94
+func (sth simpleTHash) Hash() hash.Hash { return sth.h() }
95
+
70 96
 func (ts tarSum) selectHeaders(h *tar.Header, v Version) (set [][2]string) {
71 97
 	for _, elem := range [][2]string{
72 98
 		{"name", h.Name},
... ...
@@ -113,25 +149,35 @@ func (ts *tarSum) encodeHeader(h *tar.Header) error {
113 113
 	return nil
114 114
 }
115 115
 
116
+func (ts *tarSum) initTarSum() error {
117
+	ts.bufTar = bytes.NewBuffer([]byte{})
118
+	ts.bufWriter = bytes.NewBuffer([]byte{})
119
+	ts.tarR = tar.NewReader(ts.Reader)
120
+	ts.tarW = tar.NewWriter(ts.bufTar)
121
+	if !ts.DisableCompression {
122
+		ts.writer = gzip.NewWriter(ts.bufWriter)
123
+	} else {
124
+		ts.writer = &nopCloseFlusher{Writer: ts.bufWriter}
125
+	}
126
+	if ts.tHash == nil {
127
+		ts.tHash = DefaultTHash
128
+	}
129
+	ts.h = ts.tHash.Hash()
130
+	ts.h.Reset()
131
+	ts.first = true
132
+	ts.sums = FileInfoSums{}
133
+	return nil
134
+}
135
+
116 136
 func (ts *tarSum) Read(buf []byte) (int, error) {
117
-	if ts.gz == nil {
118
-		ts.bufTar = bytes.NewBuffer([]byte{})
119
-		ts.bufGz = bytes.NewBuffer([]byte{})
120
-		ts.tarR = tar.NewReader(ts.Reader)
121
-		ts.tarW = tar.NewWriter(ts.bufTar)
122
-		if !ts.DisableCompression {
123
-			ts.gz = gzip.NewWriter(ts.bufGz)
124
-		} else {
125
-			ts.gz = &nopCloseFlusher{Writer: ts.bufGz}
137
+	if ts.writer == nil {
138
+		if err := ts.initTarSum(); err != nil {
139
+			return 0, err
126 140
 		}
127
-		ts.h = sha256.New()
128
-		ts.h.Reset()
129
-		ts.first = true
130
-		ts.sums = FileInfoSums{}
131 141
 	}
132 142
 
133 143
 	if ts.finished {
134
-		return ts.bufGz.Read(buf)
144
+		return ts.bufWriter.Read(buf)
135 145
 	}
136 146
 	if ts.bufData == nil {
137 147
 		switch {
... ...
@@ -167,10 +213,10 @@ func (ts *tarSum) Read(buf []byte) (int, error) {
167 167
 					if err := ts.tarW.Close(); err != nil {
168 168
 						return 0, err
169 169
 					}
170
-					if _, err := io.Copy(ts.gz, ts.bufTar); err != nil {
170
+					if _, err := io.Copy(ts.writer, ts.bufTar); err != nil {
171 171
 						return 0, err
172 172
 					}
173
-					if err := ts.gz.Close(); err != nil {
173
+					if err := ts.writer.Close(); err != nil {
174 174
 						return 0, err
175 175
 					}
176 176
 					ts.finished = true
... ...
@@ -189,12 +235,12 @@ func (ts *tarSum) Read(buf []byte) (int, error) {
189 189
 				return 0, err
190 190
 			}
191 191
 			ts.tarW.Flush()
192
-			if _, err := io.Copy(ts.gz, ts.bufTar); err != nil {
192
+			if _, err := io.Copy(ts.writer, ts.bufTar); err != nil {
193 193
 				return 0, err
194 194
 			}
195
-			ts.gz.Flush()
195
+			ts.writer.Flush()
196 196
 
197
-			return ts.bufGz.Read(buf)
197
+			return ts.bufWriter.Read(buf)
198 198
 		}
199 199
 		return n, err
200 200
 	}
... ...
@@ -210,18 +256,18 @@ func (ts *tarSum) Read(buf []byte) (int, error) {
210 210
 	}
211 211
 	ts.tarW.Flush()
212 212
 
213
-	// Filling the gz writter
214
-	if _, err = io.Copy(ts.gz, ts.bufTar); err != nil {
213
+	// Filling the output writer
214
+	if _, err = io.Copy(ts.writer, ts.bufTar); err != nil {
215 215
 		return 0, err
216 216
 	}
217
-	ts.gz.Flush()
217
+	ts.writer.Flush()
218 218
 
219
-	return ts.bufGz.Read(buf)
219
+	return ts.bufWriter.Read(buf)
220 220
 }
221 221
 
222 222
 func (ts *tarSum) Sum(extra []byte) string {
223 223
 	ts.sums.SortBySums()
224
-	h := sha256.New()
224
+	h := ts.tHash.Hash()
225 225
 	if extra != nil {
226 226
 		h.Write(extra)
227 227
 	}
... ...
@@ -229,7 +275,7 @@ func (ts *tarSum) Sum(extra []byte) string {
229 229
 		log.Debugf("-->%s<--", fis.Sum())
230 230
 		h.Write([]byte(fis.Sum()))
231 231
 	}
232
-	checksum := ts.Version().String() + "+sha256:" + hex.EncodeToString(h.Sum(nil))
232
+	checksum := ts.Version().String() + "+" + ts.tHash.Name() + ":" + hex.EncodeToString(h.Sum(nil))
233 233
 	log.Debugf("checksum processed: %s", checksum)
234 234
 	return checksum
235 235
 }
... ...
@@ -3,8 +3,11 @@ package tarsum
3 3
 import (
4 4
 	"bytes"
5 5
 	"compress/gzip"
6
+	"crypto/md5"
6 7
 	"crypto/rand"
8
+	"crypto/sha1"
7 9
 	"crypto/sha256"
10
+	"crypto/sha512"
8 11
 	"encoding/hex"
9 12
 	"fmt"
10 13
 	"io"
... ...
@@ -22,6 +25,7 @@ type testLayer struct {
22 22
 	gzip     bool
23 23
 	tarsum   string
24 24
 	version  Version
25
+	hash     THash
25 26
 }
26 27
 
27 28
 var testLayers = []testLayer{
... ...
@@ -75,6 +79,31 @@ var testLayers = []testLayer{
75 75
 		// this tar has newer of collider-1.tar, ensuring is has different hash
76 76
 		filename: "testdata/collision/collision-3.tar",
77 77
 		tarsum:   "tarsum+sha256:f886e431c08143164a676805205979cd8fa535dfcef714db5515650eea5a7c0f"},
78
+	{
79
+		options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory)
80
+		tarsum:  "tarsum+md5:0d7529ec7a8360155b48134b8e599f53",
81
+		hash:    md5THash,
82
+	},
83
+	{
84
+		options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory)
85
+		tarsum:  "tarsum+sha1:f1fee39c5925807ff75ef1925e7a23be444ba4df",
86
+		hash:    sha1Hash,
87
+	},
88
+	{
89
+		options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory)
90
+		tarsum:  "tarsum+sha224:6319390c0b061d639085d8748b14cd55f697cf9313805218b21cf61c",
91
+		hash:    sha224Hash,
92
+	},
93
+	{
94
+		options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory)
95
+		tarsum:  "tarsum+sha384:a578ce3ce29a2ae03b8ed7c26f47d0f75b4fc849557c62454be4b5ffd66ba021e713b48ce71e947b43aab57afd5a7636",
96
+		hash:    sha384Hash,
97
+	},
98
+	{
99
+		options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory)
100
+		tarsum:  "tarsum+sha512:e9bfb90ca5a4dfc93c46ee061a5cf9837de6d2fdf82544d6460d3147290aecfabf7b5e415b9b6e72db9b8941f149d5d69fb17a394cbfaf2eac523bd9eae21855",
101
+		hash:    sha512Hash,
102
+	},
78 103
 }
79 104
 
80 105
 type sizedOptions struct {
... ...
@@ -203,6 +232,14 @@ func TestEmptyTar(t *testing.T) {
203 203
 	}
204 204
 }
205 205
 
206
+var (
207
+	md5THash   = NewTHash("md5", md5.New)
208
+	sha1Hash   = NewTHash("sha1", sha1.New)
209
+	sha224Hash = NewTHash("sha224", sha256.New224)
210
+	sha384Hash = NewTHash("sha384", sha512.New384)
211
+	sha512Hash = NewTHash("sha512", sha512.New)
212
+)
213
+
206 214
 func TestTarSums(t *testing.T) {
207 215
 	for _, layer := range testLayers {
208 216
 		var (
... ...
@@ -226,8 +263,13 @@ func TestTarSums(t *testing.T) {
226 226
 			defer file.Close()
227 227
 		}
228 228
 
229
-		//                                  double negatives!
230
-		ts, err := NewTarSum(fh, !layer.gzip, layer.version)
229
+		var ts TarSum
230
+		if layer.hash == nil {
231
+			//                           double negatives!
232
+			ts, err = NewTarSum(fh, !layer.gzip, layer.version)
233
+		} else {
234
+			ts, err = NewTarSumHash(fh, !layer.gzip, layer.version, layer.hash)
235
+		}
231 236
 		if err != nil {
232 237
 			t.Errorf("%q :: %q", err, layer.filename)
233 238
 			continue