-
Notifications
You must be signed in to change notification settings - Fork 109
/
hash.go
181 lines (149 loc) · 4.44 KB
/
hash.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
// Package hash implements a cryptographic hash over arbitrary binary data.
package hash
import (
"crypto/sha512"
"crypto/subtle"
"encoding"
"encoding/base64"
"encoding/hex"
"errors"
"hash"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
"github.com/oasisprotocol/oasis-core/go/common/cbor"
)
// Size is the size of the cryptographic hash in bytes.
const Size = 32
var (
// ErrMalformed is the error returned when a hash is malformed.
ErrMalformed = errors.New("hash: malformed hash")
// ErrTruncateSize is the error returned when trying to truncate a hash to an invalid size.
ErrTruncateSize = errors.New("hash: invalid truncate size")
emptyHash = sha512.Sum512_256([]byte{})
_ encoding.BinaryMarshaler = (*Hash)(nil)
_ encoding.BinaryUnmarshaler = (*Hash)(nil)
)
// Hash is a cryptographic hash over arbitrary binary data.
type Hash [Size]byte
// MarshalBinary encodes a hash into binary form.
func (h *Hash) MarshalBinary() (data []byte, err error) {
data = append([]byte{}, h[:]...)
return
}
// UnmarshalBinary decodes a binary marshaled hash.
func (h *Hash) UnmarshalBinary(data []byte) error {
if len(data) != Size {
return ErrMalformed
}
copy(h[:], data)
return nil
}
// MarshalText encodes a Hash into text form.
func (h Hash) MarshalText() (data []byte, err error) {
return h.MarshalHex()
}
// UnmarshalText decodes a text marshaled Hash.
func (h *Hash) UnmarshalText(text []byte) error {
err := h.UnmarshalHex(string(text))
if err != nil {
// For backwards compatibility (e.g. to be able to load the
// Cobalt Upgrade genesis file), fallback to accepting
// Base64-encoded Hash values.
b, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return err
}
return h.UnmarshalBinary(b)
}
return nil
}
// MarshalHex encodes a Hash into a hexadecimal form.
func (h *Hash) MarshalHex() ([]byte, error) {
return []byte(hex.EncodeToString(h[:])), nil
}
// UnmarshalHex deserializes a hexadecimal text string into the given type.
func (h *Hash) UnmarshalHex(text string) error {
b, err := hex.DecodeString(text)
if err != nil {
return err
}
return h.UnmarshalBinary(b)
}
// From sets the hash to that of an arbitrary CBOR serializeable interface.
func (h *Hash) From(v interface{}) {
h.FromBytes(cbor.Marshal(v))
}
// FromBytes sets the hash to that of an arbitrary byte string.
func (h *Hash) FromBytes(data ...[]byte) {
hasher := sha512.New512_256()
for _, d := range data {
_, _ = hasher.Write(d)
}
sum := hasher.Sum([]byte{})
_ = h.UnmarshalBinary(sum[:])
}
// Equal compares vs another hash for equality.
func (h *Hash) Equal(cmp *Hash) bool {
if cmp == nil {
return false
}
return subtle.ConstantTimeCompare(h[:], cmp[:]) == 1
}
// Empty sets the hash to that of an empty (0 byte) string.
func (h *Hash) Empty() {
copy(h[:], emptyHash[:])
}
// IsEmpty returns true iff the hash is that of an empty (0 byte) string.
func (h *Hash) IsEmpty() bool {
return subtle.ConstantTimeCompare(h[:], emptyHash[:]) == 1
}
// Hex returns the hex-encoded representation of a hash.
func (h Hash) Hex() string {
return hex.EncodeToString(h[:])
}
// String returns the string representation of a hash.
func (h Hash) String() string {
return h.Hex()
}
// Truncate returns the first n bytes of a hash.
func (h Hash) Truncate(n int) ([]byte, error) {
if n <= 0 || n > Size {
return nil, ErrTruncateSize
}
return append([]byte{}, h[:n]...), nil
}
// NewFrom creates a new hash by hashing the CBOR representation of the given type.
func NewFrom(v interface{}) (h Hash) {
h.From(v)
return
}
// NewFromBytes creates a new hash by hashing the provided byte string(s).
func NewFromBytes(data ...[]byte) (h Hash) {
h.FromBytes(data...)
return
}
// LoadFromHexBytes creates a new hash by loading it from the given Tendermint
// HexBytes byte array.
func LoadFromHexBytes(data tmbytes.HexBytes) (h Hash) {
_ = h.UnmarshalBinary(data[:])
return
}
// Builder is a hash builder that can be used to compute hashes iteratively.
type Builder struct {
hasher hash.Hash
}
// Write adds more data to the running hash.
// It never returns an error.
func (b *Builder) Write(p []byte) (int, error) {
return b.hasher.Write(p)
}
// Build returns the current hash.
// It does not change the underlying hash state.
func (b *Builder) Build() (h Hash) {
sum := b.hasher.Sum([]byte{})
_ = h.UnmarshalBinary(sum[:])
return
}
// NewBuilder creates a new hash builder.
func NewBuilder() *Builder {
return &Builder{hasher: sha512.New512_256()}
}