-
Notifications
You must be signed in to change notification settings - Fork 0
/
hash.go
222 lines (203 loc) · 5.01 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
package xsum
import (
"bytes"
"encoding/hex"
"fmt"
"hash"
"io"
"os"
"os/exec"
"github.com/sclevine/xsum/encoding"
)
const (
HashNone = ""
HashMD4 = "md4"
HashMD5 = "md5"
HashSHA1 = "sha1"
HashSHA256 = "sha256"
HashSHA224 = "sha224"
HashSHA512 = "sha512"
HashSHA384 = "sha384"
HashSHA512_224 = "sha512-224"
HashSHA512_256 = "sha512-256"
HashSHA3_224 = "sha3-224"
HashSHA3_256 = "sha3-256"
HashSHA3_384 = "sha3-384"
HashSHA3_512 = "sha3-512"
HashBlake2s256 = "blake2s256"
HashBlake2b256 = "blake2b256"
HashBlake2b384 = "blake2b384"
HashBlake2b512 = "blake2b512"
HashRMD160 = "rmd160"
HashCRC32 = "crc32"
HashCRC32c = "crc32c"
HashCRC32k = "crc32k"
HashCRC64ISO = "crc64iso"
HashCRC64ECMA = "crc64ecma"
HashAdler32 = "adler32"
HashFNV32 = "fnv32"
HashFNV32a = "fnv32a"
HashFNV64 = "fnv64"
HashFNV64a = "fnv64a"
HashFNV128 = "fnv128"
HashFNV128a = "fnv128a"
)
func hashToEncoding(h string) encoding.HashType {
switch h {
case HashNone:
return encoding.HashNone
case HashMD4:
return encoding.HashMD4
case HashMD5:
return encoding.HashMD5
case HashSHA1:
return encoding.HashSHA1
case HashSHA256:
return encoding.HashSHA256
case HashSHA224:
return encoding.HashSHA224
case HashSHA512:
return encoding.HashSHA512
case HashSHA384:
return encoding.HashSHA384
case HashSHA512_224:
return encoding.HashSHA512_224
case HashSHA512_256:
return encoding.HashSHA512_256
case HashSHA3_224:
return encoding.HashSHA3_224
case HashSHA3_256:
return encoding.HashSHA3_256
case HashSHA3_384:
return encoding.HashSHA3_384
case HashSHA3_512:
return encoding.HashSHA3_512
case HashBlake2s256:
return encoding.HashBlake2s256
case HashBlake2b256:
return encoding.HashBlake2b256
case HashBlake2b384:
return encoding.HashBlake2b384
case HashBlake2b512:
return encoding.HashBlake2b512
case HashRMD160:
return encoding.HashRMD160
case HashCRC32:
return encoding.HashCRC32
case HashCRC32c:
return encoding.HashCRC32c
case HashCRC32k:
return encoding.HashCRC32k
case HashCRC64ISO:
return encoding.HashCRC64ISO
case HashCRC64ECMA:
return encoding.HashCRC64ECMA
case HashAdler32:
return encoding.HashAdler32
case HashFNV32:
return encoding.HashFNV32
case HashFNV32a:
return encoding.HashFNV32a
case HashFNV64:
return encoding.HashFNV64
case HashFNV64a:
return encoding.HashFNV64a
case HashFNV128:
return encoding.HashFNV128
case HashFNV128a:
return encoding.HashFNV128a
default:
return encoding.HashUnknown
}
}
// Hash provides a named hash function applicable to several types of data.
type Hash interface {
String() string // returns function name
Metadata(b []byte) ([]byte, error)
Data(r io.Reader) ([]byte, error)
File(path string) ([]byte, error)
}
// NewHashFunc returns a Hash defined by func fn.
// The same func fn is used for all types of data.
func NewHashFunc(name string, fn func() hash.Hash) Hash {
return &hashFunc{
name: name,
fn: fn,
}
}
// NewHashPlugin returns a Hash backed by the xsum plugin at the specified path.
// File()/Data() and Metadata() may use different underlying hash functions.
// See PLUGIN.md for details.
func NewHashPlugin(name, path string) Hash {
return &hashPlugin{
name: name,
path: path,
}
}
type hashFunc struct {
name string
fn func() hash.Hash
}
func (h *hashFunc) String() string {
return h.name
}
func (h *hashFunc) Metadata(b []byte) ([]byte, error) {
hf := h.fn()
if _, err := hf.Write(b); err != nil {
return nil, err
}
return hf.Sum(nil), nil
}
func (h *hashFunc) Data(r io.Reader) ([]byte, error) {
hf := h.fn()
if _, err := io.Copy(hf, r); err != nil {
return nil, err
}
return hf.Sum(nil), nil
}
func (h *hashFunc) File(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
return h.Data(f)
}
type hashPlugin struct {
name, path string
}
func (h *hashPlugin) String() string {
return h.name
}
func (h *hashPlugin) Metadata(b []byte) ([]byte, error) {
return h.readCmd(bytes.NewReader(b), "metadata")
}
func (h *hashPlugin) Data(r io.Reader) ([]byte, error) {
return h.readCmd(r, "data")
}
func (h *hashPlugin) File(path string) ([]byte, error) {
return h.argCmd(path, "data")
}
func (h *hashPlugin) readCmd(r io.Reader, ptype string) ([]byte, error) {
cmd := exec.Command(h.path)
cmd.Env = append(os.Environ(), "XSUM_PLUGIN_TYPE="+ptype)
cmd.Stdin = r
sum, err := cmd.Output()
if eErr, ok := err.(*exec.ExitError); ok {
return nil, fmt.Errorf("plugin error:\n\t%s", string(eErr.Stderr))
} else if err != nil {
return nil, err
}
return hex.DecodeString(string(sum))
}
func (h *hashPlugin) argCmd(path, ptype string) ([]byte, error) {
cmd := exec.Command(h.path, path)
cmd.Env = append(os.Environ(), "XSUM_PLUGIN_TYPE="+ptype)
sum, err := cmd.Output()
if eErr, ok := err.(*exec.ExitError); ok {
return nil, fmt.Errorf("plugin error:\n\t%s", string(eErr.Stderr))
} else if err != nil {
return nil, err
}
return hex.DecodeString(string(sum))
}