...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package v1
16
17 import (
18 "crypto"
19 "encoding/hex"
20 "encoding/json"
21 "fmt"
22 "hash"
23 "io"
24 "strconv"
25 "strings"
26 )
27
28
29 type Hash struct {
30
31 Algorithm string
32
33
34 Hex string
35 }
36
37
38 func (h Hash) String() string {
39 return fmt.Sprintf("%s:%s", h.Algorithm, h.Hex)
40 }
41
42
43 func NewHash(s string) (Hash, error) {
44 h := Hash{}
45 if err := h.parse(s); err != nil {
46 return Hash{}, err
47 }
48 return h, nil
49 }
50
51
52 func (h Hash) MarshalJSON() ([]byte, error) {
53 return json.Marshal(h.String())
54 }
55
56
57 func (h *Hash) UnmarshalJSON(data []byte) error {
58 s, err := strconv.Unquote(string(data))
59 if err != nil {
60 return err
61 }
62 return h.parse(s)
63 }
64
65
66
67 func (h Hash) MarshalText() (text []byte, err error) {
68 return []byte(h.String()), nil
69 }
70
71
72
73 func (h *Hash) UnmarshalText(text []byte) error {
74 return h.parse(string(text))
75 }
76
77
78 func Hasher(name string) (hash.Hash, error) {
79 switch name {
80 case "sha256":
81 return crypto.SHA256.New(), nil
82 default:
83 return nil, fmt.Errorf("unsupported hash: %q", name)
84 }
85 }
86
87 func (h *Hash) parse(unquoted string) error {
88 parts := strings.Split(unquoted, ":")
89 if len(parts) != 2 {
90 return fmt.Errorf("cannot parse hash: %q", unquoted)
91 }
92
93 rest := strings.TrimLeft(parts[1], "0123456789abcdef")
94 if len(rest) != 0 {
95 return fmt.Errorf("found non-hex character in hash: %c", rest[0])
96 }
97
98 hasher, err := Hasher(parts[0])
99 if err != nil {
100 return err
101 }
102
103 if len(parts[1]) != hasher.Size()*2 {
104 return fmt.Errorf("wrong number of hex digits for %s: %s", parts[0], parts[1])
105 }
106
107 h.Algorithm = parts[0]
108 h.Hex = parts[1]
109 return nil
110 }
111
112
113 func SHA256(r io.Reader) (Hash, int64, error) {
114 hasher := crypto.SHA256.New()
115 n, err := io.Copy(hasher, r)
116 if err != nil {
117 return Hash{}, 0, err
118 }
119 return Hash{
120 Algorithm: "sha256",
121 Hex: hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))),
122 }, n, nil
123 }
124
View as plain text