forked from keybase/kbfs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hash.go
351 lines (291 loc) · 8.79 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
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
// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.
package kbfshash
import (
"crypto/hmac"
"crypto/sha256"
"encoding"
"encoding/hex"
"fmt"
"github.com/pkg/errors"
)
// See https://keybase.io/admin-docs/hash-format for the design doc
// for the keybase hash format.
const (
// MinHashByteLength is the minimum number of bytes a valid
// keybase hash can be, including the 1 byte for the type.
MinHashByteLength = 33
// DefaultHashByteLength is the number of bytes in a default
// keybase hash.
DefaultHashByteLength = 1 + sha256.Size
// MaxHashByteLength is the maximum number of bytes a valid
// keybase hash can be, including the 1 byte for the type.
MaxHashByteLength = 129
// MinHashStringLength is the minimum number of characters in
// the string representation (hex encoding) of a valid keybase
// hash.
MinHashStringLength = 2 * MinHashByteLength
// DefaultHashStringLength is the number of characters in the
// string representation of a default keybase hash.
DefaultHashStringLength = 2 * DefaultHashByteLength
// MaxHashStringLength is the maximum number of characters the
// string representation of a valid keybase hash can be.
MaxHashStringLength = 2 * MaxHashByteLength
)
// HashType is the type of a keybase hash.
type HashType byte
const (
// InvalidHash is the zero HashType value, which is invalid.
InvalidHash HashType = 0
// SHA256Hash is the type of a SHA256 hash.
SHA256Hash HashType = 1
)
// MaxDefaultHash is the maximum value of RawDefaultHash
var MaxDefaultHash = RawDefaultHash{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
func (t HashType) String() string {
switch t {
case InvalidHash:
return "InvalidHash"
case SHA256Hash:
return "SHA256Hash"
default:
return fmt.Sprintf("HashType(%d)", t)
}
}
// DefaultHashType is the current default keybase hash type.
const DefaultHashType HashType = SHA256Hash
// DefaultHashNew is a function that creates a new hash.Hash object
// with the default hash.
var DefaultHashNew = sha256.New
// RawDefaultHash is the type for the raw bytes of a default keybase
// hash. This is exposed for use as in-memory keys.
type RawDefaultHash [sha256.Size]byte
// DoRawDefaultHash computes the default keybase hash of the given
// data, and returns the type and the raw hash bytes.
func DoRawDefaultHash(p []byte) (HashType, RawDefaultHash) {
return DefaultHashType, RawDefaultHash(sha256.Sum256(p))
}
// Copy returns a copied RawDefaultHash
func (rdh *RawDefaultHash) Copy() *RawDefaultHash {
if rdh == nil {
return nil
}
hashCopy := RawDefaultHash{}
copy(hashCopy[:], rdh[:])
return &hashCopy
}
// Hash is the type of a keybase hash.
type Hash struct {
// Stored as a string so that this can be used as a map key.
h string
}
var _ encoding.BinaryMarshaler = Hash{}
var _ encoding.BinaryUnmarshaler = (*Hash)(nil)
var _ encoding.TextMarshaler = Hash{}
var _ encoding.TextUnmarshaler = (*Hash)(nil)
// HashFromRaw creates a hash from a type and raw hash data. If the
// returned error is nil, the returned Hash is valid.
func HashFromRaw(hashType HashType, rawHash []byte) (Hash, error) {
return HashFromBytes(append([]byte{byte(hashType)}, rawHash...))
}
// HashFromBytes creates a hash from the given byte array. If the
// returned error is nil, the returned Hash is valid.
func HashFromBytes(data []byte) (Hash, error) {
h := Hash{string(data)}
if !h.IsValid() {
return Hash{}, errors.WithStack(InvalidHashError{h})
}
return h, nil
}
// HashFromString creates a hash from the given string. If the
// returned error is nil, the returned Hash is valid.
func HashFromString(dataStr string) (Hash, error) {
data, err := hex.DecodeString(dataStr)
if err != nil {
return Hash{}, errors.WithStack(err)
}
return HashFromBytes(data)
}
// DefaultHash computes the hash of the given data with the default
// hash type.
func DefaultHash(buf []byte) (Hash, error) {
hashType, rawHash := DoRawDefaultHash(buf)
return HashFromRaw(hashType, rawHash[:])
}
func (h Hash) hashType() HashType {
return HashType(h.h[0])
}
func (h Hash) hashData() []byte {
return []byte(h.h[1:])
}
// IsValid returns whether the hash is valid. Note that a hash with an
// unknown version is still valid.
func (h Hash) IsValid() bool {
if len(h.h) < MinHashByteLength {
return false
}
if len(h.h) > MaxHashByteLength {
return false
}
if h.hashType() == InvalidHash {
return false
}
return true
}
// Bytes returns the bytes of the hash.
func (h Hash) Bytes() []byte {
return []byte(h.h)
}
func (h Hash) String() string {
return hex.EncodeToString([]byte(h.h))
}
// MarshalBinary implements the encoding.BinaryMarshaler interface for
// Hash. Returns an error if the hash is invalid and not the zero
// hash.
func (h Hash) MarshalBinary() (data []byte, err error) {
if h == (Hash{}) {
return nil, nil
}
if !h.IsValid() {
return nil, errors.WithStack(InvalidHashError{h})
}
return []byte(h.h), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
// for Hash. Returns an error if the given byte array is non-empty and
// the hash is invalid.
func (h *Hash) UnmarshalBinary(data []byte) error {
if len(data) == 0 {
*h = Hash{}
return nil
}
h.h = string(data)
if !h.IsValid() {
err := InvalidHashError{*h}
*h = Hash{}
return errors.WithStack(err)
}
return nil
}
// Verify makes sure that the hash matches the given data and returns
// an error otherwise.
func (h Hash) Verify(buf []byte) error {
if !h.IsValid() {
return errors.WithStack(InvalidHashError{h})
}
// Once we have multiple hash types we'll need to expand this.
t := h.hashType()
if t != DefaultHashType {
return errors.WithStack(UnknownHashTypeError{t})
}
expectedH, err := DefaultHash(buf)
if err != nil {
return err
}
if h != expectedH {
return errors.WithStack(HashMismatchError{expectedH, h})
}
return nil
}
// MarshalText implements the encoding.TextMarshaler interface for
// Hash.
func (h Hash) MarshalText() ([]byte, error) {
return []byte(h.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface
// for Hash.
func (h *Hash) UnmarshalText(data []byte) error {
newH, err := HashFromString(string(data))
if err != nil {
return err
}
*h = newH
return nil
}
// HMAC is the type of a keybase hash that is an HMAC.
//
// All the constants for Hash also apply to HMAC.
type HMAC struct {
h Hash
}
var _ encoding.BinaryMarshaler = HMAC{}
var _ encoding.BinaryUnmarshaler = (*HMAC)(nil)
var _ encoding.TextMarshaler = HMAC{}
var _ encoding.TextUnmarshaler = (*HMAC)(nil)
// DefaultHMAC computes the HMAC with the given key of the given data
// using the default hash.
func DefaultHMAC(key, buf []byte) (HMAC, error) {
mac := hmac.New(DefaultHashNew, key)
mac.Write(buf)
h, err := HashFromRaw(DefaultHashType, mac.Sum(nil))
if err != nil {
return HMAC{}, err
}
return HMAC{h}, nil
}
func (hmac HMAC) hashType() HashType {
return hmac.h.hashType()
}
func (hmac HMAC) hashData() []byte {
return hmac.h.hashData()
}
// IsValid returns whether the HMAC is valid. Note that an HMAC with an
// unknown version is still valid.
func (hmac HMAC) IsValid() bool {
return hmac.h.IsValid()
}
// Bytes returns the bytes of the HMAC.
func (hmac HMAC) Bytes() []byte {
return hmac.h.Bytes()
}
func (hmac HMAC) String() string {
return hmac.h.String()
}
// MarshalBinary implements the encoding.BinaryMarshaler interface for
// HMAC. Returns an error if the HMAC is invalid and not the zero
// HMAC.
func (hmac HMAC) MarshalBinary() (data []byte, err error) {
return hmac.h.MarshalBinary()
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
// for HMAC. Returns an error if the given byte array is non-empty and
// the HMAC is invalid.
func (hmac *HMAC) UnmarshalBinary(data []byte) error {
return hmac.h.UnmarshalBinary(data)
}
// MarshalText implements the encoding.TextMarshaler interface for
// HMAC.
func (hmac HMAC) MarshalText() ([]byte, error) {
return hmac.h.MarshalText()
}
// UnmarshalText implements the encoding.TextUnmarshaler interface
// for HMAC.
func (hmac *HMAC) UnmarshalText(data []byte) error {
return hmac.h.UnmarshalText(data)
}
// Verify makes sure that the HMAC matches the given data.
func (hmac HMAC) Verify(key, buf []byte) error {
if !hmac.IsValid() {
return errors.WithStack(InvalidHashError{hmac.h})
}
// Once we have multiple hash types we'll need to expand this.
t := hmac.hashType()
if t != DefaultHashType {
return errors.WithStack(UnknownHashTypeError{t})
}
expectedHMAC, err := DefaultHMAC(key, buf)
if err != nil {
return err
}
if hmac != expectedHMAC {
return errors.WithStack(
HashMismatchError{expectedHMAC.h, hmac.h})
}
return nil
}