/
sha.go
174 lines (141 loc) · 4.25 KB
/
sha.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
// NXP Data Co-Processor (DCP) driver
// https://github.com/usbarmory/tamago
//
// Copyright (c) WithSecure Corporation
// https://foundry.withsecure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
package dcp
import (
"crypto/sha256"
"errors"
"io"
"golang.org/x/sync/semaphore"
)
// A single DCP channel is used for all operations, this entails that only one
// digest state can be kept at any given time.
var sem = semaphore.NewWeighted(1)
// Hash is the common interface to DCP hardware backed hash functions.
//
// While similar to Go native hash.Hash, this interface is not fully compatible
// with it as hardware errors must be checked and checksum computation affects
// state.
type Hash interface {
// Write (via the embedded io.Writer interface) adds more data to the running hash.
// It can return an error. It returns an error if Sum has been already invoked.
io.Writer
// Sum appends the current hash to b and returns the resulting slice.
// Its invocation terminates the digest instance, for this reason Write
// will return errors after Sum is invoked.
Sum(b []byte) ([]byte, error)
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently if all writes
// are a multiple of the block size.
BlockSize() int
}
type digest struct {
dcp *DCP
mode int
n int
bs int
init bool
buf []byte
sum []byte
}
// Write adds more data to the running hash. It returns an error if Sum has
// been already invoked or in case of hardware errors.
//
// There must be sufficient DMA memory allocated to hold the data, otherwise
// the function will panic.
func (d *digest) Write(p []byte) (n int, err error) {
if len(d.sum) != 0 {
return 0, errors.New("digest instance can no longer be used")
}
// If we still don't have enough data for a block, accumulate and early
// out.
if len(d.buf)+len(p) < d.bs {
d.buf = append(d.buf, p...)
return len(p), nil
}
pl := len(p)
// top up partial block buffer, and process that
cut := d.bs - len(d.buf)
d.buf = append(d.buf, p[:cut]...)
p = p[cut:]
if _, err = d.dcp.hash(d.buf, d.mode, d.n, d.init, false); err != nil {
return
}
if d.init {
d.init = false
}
// work through any more full blocks in p
if l := len(p); l > d.bs {
r := l % d.bs
if _, err = d.dcp.hash(p[:l-r], d.mode, d.n, d.init, false); err != nil {
return
}
p = p[l-r:]
}
// save off any partial block remaining
d.buf = append(d.buf[0:0], p...)
return pl, nil
}
// Sum appends the current hash to in and returns the resulting slice. Its
// invocation terminates the digest instance, for this reason Write will return
// errors after Sum is invoked.
func (d *digest) Sum(in []byte) (sum []byte, err error) {
if len(d.sum) != 0 {
return append(in, d.sum[:]...), nil
}
defer sem.Release(1)
if d.init && len(d.buf) == 0 {
d.sum = sha256.New().Sum(nil)
} else {
s, err := d.dcp.hash(d.buf, HASH_SELECT_SHA256, d.n, d.init, true)
if err != nil {
return nil, err
}
d.sum = s
}
return append(in, d.sum[:]...), nil
}
// BlockSize returns the hash's underlying block size.
func (d *digest) BlockSize() int {
return d.bs
}
// New256 returns a new Digest computing the SHA256 checksum.
//
// A single DCP channel is used for all operations, this entails that only one
// digest instance can be kept at any given time, if this condition is not met
// an error is returned.
//
// The digest instance starts with New256() and terminates when when Sum() is
// invoked, after which the digest state can no longer be changed.
func (hw *DCP) New256() (Hash, error) {
if !sem.TryAcquire(1) {
return nil, errors.New("another digest instance is already in use")
}
d := &digest{
dcp: hw,
mode: HASH_SELECT_SHA256,
n: sha256.Size,
bs: sha256.BlockSize,
init: true,
buf: make([]byte, 0, sha256.BlockSize),
}
return d, nil
}
// Sum256 returns the SHA256 checksum of the data.
//
// There must be sufficient DMA memory allocated to hold the data, otherwise
// the function will panic.
func (hw *DCP) Sum256(data []byte) (sum [32]byte, err error) {
s, err := hw.hash(data, HASH_SELECT_SHA256, len(sum), true, true)
if err != nil {
return
}
copy(sum[:], s)
return
}