This repository was archived by the owner on May 31, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathdigest.go
373 lines (316 loc) · 13.7 KB
/
digest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// Copyright 2018 IBM Corporation
// Licensed under the Apache License, Version 2.0. See LICENSE file.
package hash
import (
"bytes"
"encoding/ascii85"
"encoding/base64"
"encoding/binary"
"encoding/gob"
"encoding/hex"
"errors"
"hash"
"regexp"
"strings"
)
// Digest is a finalized hash.Hash. It conforms to the hash.Hash interface, but
// it does not support operations that modify the finalized checksum
type Digest interface {
// A Digest is a hash.Hash that has already been finalized and is
// essentially a wrapper around the fixed length checksum result
// hash.Hash
Sum(b []byte) []byte
// Reset()
Size() int
// BlockSize() int
// IsZero returns true for Digests that are the zero-value of their type
// (aka, all 0s)
IsZero() bool
// String returns the hex string representing the checksum
String() string
// Base64 returns the checksum as a base64 string
Base64() string
// Bytes returns the finalized checksum bytes, similar to Sum(), but
// simplified for hashes that have already been finalized
Bytes() []byte
}
// digest64 is a finalized 64-bit checksum. In addition to satisfying the Digest
// (and hash.Hash) interface, it also satisfies the hash.Hash64 interface for
// returning the calculated sum as a single uin64
type digest64 [64 / 8]byte
// digest128 is a finalized 128-bit checksum
type digest128 [128 / 8]byte
// digest160 is a finalized 128-bit checksum
type digest160 [160 / 8]byte
// digest256 is a finalized 128-bit checksum
type digest256 [256 / 8]byte
// digest384 is a finalized 128-bit checksum
type digest384 [384 / 8]byte
// digest512 is a finalized 128-bit checksum
type digest512 [512 / 8]byte
// handy for easily implementing IsZero
var zero struct {
digest64
digest128
digest160
digest256
digest384
digest512
}
// let the compiler tell us when any of the digest implementations are
// incomplete even if we don't use them as a Digest instance directly in the
// code anywhere
var _ []Digest = []Digest{digest64{}, digest128{}, digest160{}, digest256{}, digest384{}, digest512{}}
var _ hash.Hash64 = digest64{}
// DigestMatcher is a multi-hash comparison operand allowing Digest objects to be matched against.
type DigestMatcher struct {
P string
T string
B []byte
}
var matcherFormat = regexp.MustCompile("^(\\*|git|gitsha|sha1|sha256|sha384|sha512):([0-9a-fA-F]+)$")
// NewDigestMatcher creates a new DigestMatcher by parsing the provided string
func NewDigestMatcher(pat string) (DigestMatcher, error) {
dm := DigestMatcher{}
parts := matcherFormat.FindStringSubmatch(pat)
if len(parts) < 3 {
return dm, errors.New("Invalid pattern")
}
dm = DigestMatcher{T: parts[1], P: parts[2]}
// if b, err := base64.StdEncoding.DecodeString(dm.s); err != nil {
// dm.b = b
// } else
if b, err := hex.DecodeString(dm.P); err == nil {
dm.B = b
}
return dm, nil
}
// Match compares the DigestMatcher against a given Digest, either by string
// representation or direct byte comparison if the provided string was an even
// number of hex digits.
func (matcher DigestMatcher) Match(hash Digest) bool {
// if matcher.T != "*" && matcher.T != hash.TypeName() {
// return false
// }
if len(matcher.B) > hash.Size() {
return false
}
// Because digests are normally provided in an encoded form that doesn't have
// a 1 character to 1 byte mapping, the byte representation might not have been
// possible to accurately generate
if matcher.B != nil {
return bytes.HasPrefix(hash.Bytes(), matcher.B)
}
// This will only really work with hex encoded digests and even then this case
// will only be run if we are given an odd number length string
return strings.HasPrefix(hash.String(), matcher.P)
}
func fmtSRI(prefix string, bytes []byte) string {
return prefix + "-" + base64.StdEncoding.EncodeToString(bytes)
}
func marshalBinaryArray(d interface{}) ([]byte, error) {
var b bytes.Buffer
encoder := gob.NewEncoder(&b)
err := encoder.Encode(d)
return b.Bytes(), err
}
// UnmarshalBinaryArray modifies the receiver so it must take a pointer receiver.
func unmarshalBinaryArray(d interface{}, data []byte) error {
b := bytes.NewBuffer(data)
decoder := gob.NewDecoder(b)
return decoder.Decode(d)
}
var marshalYAMLBytes = marshalYAMLBytesHex
func marshalYAMLBytesHex(d Digest) (interface{}, error) {
return d.String(), nil
}
func marshalYAMLBytesBase64(d Digest) (interface{}, error) {
return d.Base64(), nil
}
func marshalYAMLBytesBase85(d Digest) (interface{}, error) {
dst := make([]byte, d.Size()+d.Size()/4)
len := ascii85.Encode(dst, d.Bytes())
if len > d.Size() {
return string(dst[:]), nil
}
return nil, errors.New("base85 encoding error")
}
var unmarshalYAMLBytes = unmarshalYAMLBytesHex
func unmarshalYAMLBytesHex(d []byte, unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err != nil {
return err
}
raw, err := hex.DecodeString(str)
if err != nil {
return err
}
copy(d, raw)
return nil
}
func unmarshalYAMLBytesBase64(d []byte, unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err != nil {
return err
}
raw, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return err
}
copy(d, raw)
return nil
}
func unmarshalYAMLBytesBase85(d []byte, unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err != nil {
return err
}
nDst, nSrc, err := ascii85.Decode(d, []byte(str), true)
if err != nil {
return err
}
if nDst <= nSrc {
return errors.New("Error decoding ascii85")
}
return nil
}
func (digest64) Write([]byte) (int, error) { defer panic("Unimplemented"); return 0, nil }
func (digest64) Reset() { panic("Unimplemented") }
func (digest64) BlockSize() int { defer panic("Unimplemented"); return 0 }
func (digest64) Size() int { return 64 / 8 }
func (d digest64) Sum(in []byte) []byte { return append(in, d.Bytes()...) }
// Sum64 satisfies the hash.Hash64 interface
func (d digest64) Sum64() uint64 { return binary.LittleEndian.Uint64(d.Bytes()[0:8]) }
func (d digest64) IsZero() bool { return d == [len(d)]byte{} }
func (d digest64) String() string { return hex.EncodeToString(d[:]) }
func (d digest64) Base64() string { return base64.StdEncoding.EncodeToString(d[:]) }
func (d digest64) Bytes() []byte { return d[:] }
// func (d digest64) Len() int { return len(d) }
func (d digest64) MarshalYAML() (interface{}, error) { return marshalYAMLBytes(d) }
func (d *digest64) UnmarshalYAML(unmarshal func(interface{}) error) error {
return unmarshalYAMLBytes(d[:], unmarshal)
}
func (d *digest64) MarshalBinary() ([]byte, error) {
return marshalBinaryArray((*[len(d)]byte)(d))
}
func (d *digest64) UnmarshalBinary(data []byte) error {
return unmarshalBinaryArray((*[len(d)]byte)(d), data)
}
// Consider removing these from the interface
// func (digest64) TypeName() string { panic("must implement"); return "" }
// func (d digest64) SRI() string { return fmtSRI(d.TypeName(), d[:]) }
// func (digest128) Write([]byte) (int, error) { defer panic("Unimplemented"); return 0, nil }
// func (digest128) Reset() { panic("Unimplemented") }
// func (digest128) BlockSize() int { defer panic("Unimplemented"); return 0 }
func (d digest128) Size() int { return len(d) }
func (d digest128) Sum(in []byte) []byte { return append(in, d.Bytes()...) }
func (d digest128) IsZero() bool { return d == [len(d)]byte{} }
func (d digest128) String() string { return hex.EncodeToString(d[:]) }
func (d digest128) Base64() string { return base64.StdEncoding.EncodeToString(d[:]) }
func (d digest128) Bytes() []byte { return d[:] }
// func (d digest128) Len() int { return len(d) }
func (d digest128) MarshalYAML() (interface{}, error) { return marshalYAMLBytes(d) }
func (d *digest128) UnmarshalYAML(unmarshal func(interface{}) error) error {
return unmarshalYAMLBytes(d[:], unmarshal)
}
func (d *digest128) MarshalBinary() ([]byte, error) {
return marshalBinaryArray((*[len(d)]byte)(d))
}
func (d *digest128) UnmarshalBinary(data []byte) error {
return unmarshalBinaryArray((*[len(d)]byte)(d), data)
}
// Consider removing these from the interface
// func (digest128) TypeName() string { panic("must implement"); return "" }
// func (d digest128) SRI() string { return fmtSRI(d.TypeName(), d[:]) }
func (digest160) Write([]byte) (int, error) { defer panic("Unimplemented"); return 0, nil }
func (digest160) Reset() { panic("Unimplemented") }
func (digest160) BlockSize() int { defer panic("Unimplemented"); return 0 }
func (d digest160) Size() int { return len(d) }
func (d digest160) Sum(in []byte) []byte { return append(in, d.Bytes()...) }
func (d digest160) IsZero() bool { return d == [len(d)]byte{} }
func (d digest160) String() string { return hex.EncodeToString(d[:]) }
func (d digest160) Base64() string { return base64.StdEncoding.EncodeToString(d[:]) }
func (d digest160) Bytes() []byte { return d[:] }
// func (d digest160) Len() int { return len(d) }
func (d digest160) MarshalYAML() (interface{}, error) { return marshalYAMLBytes(d) }
func (d *digest160) UnmarshalYAML(unmarshal func(interface{}) error) error {
return unmarshalYAMLBytes(d[:], unmarshal)
}
func (d *digest160) MarshalBinary() ([]byte, error) {
return marshalBinaryArray((*[len(d)]byte)(d))
}
func (d *digest160) UnmarshalBinary(data []byte) error {
return unmarshalBinaryArray((*[len(d)]byte)(d), data)
}
// Consider removing these from the interface
// func (digest160) TypeName() string { panic("must implement"); return "" }
// func (d digest160) SRI() string { return fmtSRI(d.TypeName(), d[:]) }
func (digest256) Write([]byte) (int, error) { defer panic("Unimplemented"); return 0, nil }
func (digest256) Reset() { panic("Unimplemented") }
func (digest256) BlockSize() int { defer panic("Unimplemented"); return 0 }
func (d digest256) Size() int { return len(d) }
func (d digest256) Sum(in []byte) []byte { return append(in, d.Bytes()...) }
func (d digest256) IsZero() bool { return d == zero.digest256 }
func (d digest256) String() string { return hex.EncodeToString(d[:]) }
func (d digest256) Base64() string { return base64.StdEncoding.EncodeToString(d[:]) }
func (d digest256) Bytes() []byte { return d[:] }
// func (d digest256) Len() int { return len(d) }
func (d digest256) MarshalYAML() (interface{}, error) { return marshalYAMLBytes(d) }
func (d *digest256) UnmarshalYAML(unmarshal func(interface{}) error) error {
return unmarshalYAMLBytes(d[:], unmarshal)
}
func (d *digest256) MarshalBinary() ([]byte, error) {
return marshalBinaryArray((*[len(d)]byte)(d))
}
func (d *digest256) UnmarshalBinary(data []byte) error {
return unmarshalBinaryArray((*[len(d)]byte)(d), data)
}
// Consider removing these from the interface
// func (digest256) TypeName() string { panic("must implement"); return "" }
// func (d digest256) SRI() string { return fmtSRI(d.TypeName(), d[:]) }
func (digest384) Write([]byte) (int, error) { defer panic("Unimplemented"); return 0, nil }
func (digest384) Reset() { panic("Unimplemented") }
func (digest384) BlockSize() int { defer panic("Unimplemented"); return 0 }
func (d digest384) Size() int { return len(d) }
func (d digest384) Sum(in []byte) []byte { return append(in, d.Bytes()...) }
func (d digest384) IsZero() bool { return d == [len(d)]byte{} }
func (d digest384) String() string { return hex.EncodeToString(d[:]) }
func (d digest384) Base64() string { return base64.StdEncoding.EncodeToString(d[:]) }
func (d digest384) Bytes() []byte { return d[:] }
// func (d digest384) Len() int { return len(d) }
func (d digest384) MarshalYAML() (interface{}, error) { return marshalYAMLBytes(d) }
func (d *digest384) UnmarshalYAML(unmarshal func(interface{}) error) error {
return unmarshalYAMLBytes(d[:], unmarshal)
}
func (d *digest384) MarshalBinary() ([]byte, error) {
return marshalBinaryArray((*[len(d)]byte)(d))
}
func (d *digest384) UnmarshalBinary(data []byte) error {
return unmarshalBinaryArray((*[len(d)]byte)(d), data)
}
// Consider removing these from the interface
// func (digest384) TypeName() string { panic("must implement"); return "" }
// func (d digest384) SRI() string { return fmtSRI(d.TypeName(), d[:]) }
func (digest512) Write([]byte) (int, error) { defer panic("Unimplemented"); return 0, nil }
func (digest512) Reset() { panic("Unimplemented") }
func (digest512) BlockSize() int { defer panic("Unimplemented"); return 0 }
func (d digest512) Size() int { return len(d) }
func (d digest512) Sum(in []byte) []byte { return append(in, d.Bytes()...) }
func (d digest512) IsZero() bool { return d == [len(d)]byte{} }
func (d digest512) String() string { return hex.EncodeToString(d[:]) }
func (d digest512) Base64() string { return base64.StdEncoding.EncodeToString(d[:]) }
func (d digest512) Bytes() []byte { return d[:] }
// func (d digest512) Len() int { return len(d) }
func (d digest512) MarshalYAML() (interface{}, error) { return marshalYAMLBytes(d) }
func (d *digest512) UnmarshalYAML(unmarshal func(interface{}) error) error {
return unmarshalYAMLBytes(d[:], unmarshal)
}
func (d *digest512) MarshalBinary() ([]byte, error) {
return marshalBinaryArray((*[len(d)]byte)(d))
}
func (d *digest512) UnmarshalBinary(data []byte) error {
return unmarshalBinaryArray((*[len(d)]byte)(d), data)
}
// Consider removing these from the interface
// func (digest512) TypeName() string { panic("must implement"); return "" }
// func (d digest512) SRI() string { return fmtSRI(d.TypeName(), d[:]) }