forked from juju/utils
/
fingerprint.go
132 lines (111 loc) · 3.33 KB
/
fingerprint.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
// Copyright 2016 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package hash
import (
"encoding/base64"
"encoding/hex"
"hash"
"io"
"github.com/juju/errors"
)
// Fingerprint represents the checksum for some data.
type Fingerprint struct {
sum []byte
}
// NewFingerprint returns wraps the provided raw hash sum. This function
// roundtrips with Fingerprint.Bytes().
func NewFingerprint(sum []byte, validate func([]byte) error) (Fingerprint, error) {
if validate == nil {
return Fingerprint{}, errors.New("missing validate func")
}
if err := validate(sum); err != nil {
return Fingerprint{}, errors.Trace(err)
}
return newFingerprint(sum), nil
}
// NewValidFingerprint returns a Fingerprint corresponding
// to the current of the provided hash.
func NewValidFingerprint(hash hash.Hash) Fingerprint {
sum := hash.Sum(nil)
return newFingerprint(sum)
}
func newFingerprint(sum []byte) Fingerprint {
return Fingerprint{
sum: append([]byte{}, sum...), // Use an isolated copy.
}
}
// GenerateFingerprint returns the fingerprint for the provided data.
func GenerateFingerprint(reader io.Reader, newHash func() hash.Hash) (Fingerprint, error) {
var fp Fingerprint
if reader == nil {
return fp, errors.New("missing reader")
}
if newHash == nil {
return fp, errors.New("missing new hash func")
}
hash := newHash()
if _, err := io.Copy(hash, reader); err != nil {
return fp, errors.Trace(err)
}
fp.sum = hash.Sum(nil)
return fp, nil
}
// ParseHexFingerprint returns wraps the provided raw fingerprint string.
// This function roundtrips with Fingerprint.Hex().
func ParseHexFingerprint(hexSum string, validate func([]byte) error) (Fingerprint, error) {
if validate == nil {
return Fingerprint{}, errors.New("missing validate func")
}
sum, err := hex.DecodeString(hexSum)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
fp, err := NewFingerprint(sum, validate)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
return fp, nil
}
// ParseBase64Fingerprint returns wraps the provided raw fingerprint string.
// This function roundtrips with Fingerprint.Base64().
func ParseBase64Fingerprint(b64Sum string, validate func([]byte) error) (Fingerprint, error) {
if validate == nil {
return Fingerprint{}, errors.New("missing validate func")
}
sum, err := base64.StdEncoding.DecodeString(b64Sum)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
fp, err := NewFingerprint(sum, validate)
if err != nil {
return Fingerprint{}, errors.Trace(err)
}
return fp, nil
}
// String implements fmt.Stringer.
func (fp Fingerprint) String() string {
return fp.Hex()
}
// Hex returns the hex string representation of the fingerprint.
func (fp Fingerprint) Hex() string {
return hex.EncodeToString(fp.sum)
}
// Base64 returns the base64 encoded fingerprint.
func (fp Fingerprint) Base64() string {
return base64.StdEncoding.EncodeToString(fp.sum)
}
// Bytes returns the raw (sum) bytes of the fingerprint.
func (fp Fingerprint) Bytes() []byte {
return append([]byte{}, fp.sum...)
}
// IsZero returns whether or not the fingerprint is the zero value.
func (fp Fingerprint) IsZero() bool {
return len(fp.sum) == 0
}
// Validate returns an error if the fingerprint is invalid.
func (fp Fingerprint) Validate() error {
if fp.IsZero() {
return errors.NotValidf("zero-value fingerprint")
}
return nil
}