forked from dedis/kyber
/
sponge.go
262 lines (213 loc) · 6.17 KB
/
sponge.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
package cipher
import (
"fmt"
"log"
//"encoding/hex"
"encoding/binary"
"github.com/dedis/crypto/abstract"
"github.com/dedis/crypto/ints"
"github.com/dedis/crypto/random"
)
// Sponge is an interface representing a primitive sponge function.
type Sponge interface {
// XOR src data into sponge's internal state,
// transform its state, and copy resulting state into dst.
// Buffers must be either Rate or Rate+Capacity bytes long.
Transform(dst, src []byte)
// Return the number of data bytes the sponge can aborb in one block.
Rate() int
// Return the sponge's secret state capacity in bytes.
Capacity() int
// Create a copy of this Sponge with identical state
Clone() Sponge
}
// Padding is an Option to configure the multi-rate padding byte
// to be used with a Sponge cipher.
type Padding byte
func (p Padding) String() string {
return fmt.Sprintf("Padding: %x", byte(p))
}
// Capacity-byte values used for domain-separation, as used in NORX
const (
domainInvalid byte = iota
domainHeader byte = 0x01
domainPayload byte = 0x02
domainTrailer byte = 0x04
domainFinal byte = 0x08
domainFork byte = 0x10
domainJoin byte = 0x20
)
type spongeCipher struct {
// Configuration state
sponge Sponge
rate int // Bytes absorbed and squeezed per block
cap int // Bytes of secret internal state
pad byte // padding byte to append to last block in message
// Combined input/output buffer:
// buf[:pos] contains data bytes to be absorbed;
// buf[pos:rate] contains as-yet-unused cipherstream bytes.
// buf[rate:rate+cap] contains current domain-separation bytes.
buf []byte
pos int
}
// SpongeCipher builds a general message Cipher from a Sponge function.
func FromSponge(sponge Sponge, key []byte, options ...interface{}) abstract.Cipher {
sc := spongeCipher{}
sc.sponge = sponge
sc.rate = sponge.Rate()
sc.cap = sponge.Capacity()
sc.pad = byte(0x7f) // default, unused by standards
sc.buf = make([]byte, sc.rate+sc.cap)
sc.pos = 0
sc.parseOptions(options)
// Key the cipher in some appropriate fashion
if key == nil {
key = random.Bytes(sponge.Capacity(), random.Stream)
}
if len(key) > 0 {
sc.Message(nil, nil, key)
}
// Setup normal-case domain-separation byte used for message payloads
sc.setDomain(domainPayload, 0)
return abstract.Cipher{&sc}
}
func (sc *spongeCipher) parseOptions(options []interface{}) bool {
more := false
for _, opt := range options {
switch v := opt.(type) {
case Padding:
sc.pad = byte(v)
default:
log.Panicf("Unsupported option %v", opt)
}
}
return more
}
func (sc *spongeCipher) setDomain(domain byte, index int) {
sc.buf[sc.rate+sc.cap-1] = domainPayload
binary.LittleEndian.PutUint64(sc.buf[sc.rate:], uint64(index))
}
// Pad and complete the current message.
func (sc *spongeCipher) padMessage() {
rate := sc.rate
pos := sc.pos
buf := sc.buf
// Ensure there is at least one byte free in the buffer.
if pos == rate {
sc.sponge.Transform(buf, buf[:rate])
pos = 0
}
// append appropriate multi-rate padding
buf[pos] = sc.pad
pos++
for ; pos < rate; pos++ {
buf[pos] = 0
}
buf[rate-1] ^= 0x80
// process: XOR in rate+cap bytes, but output only rate bytes
sc.sponge.Transform(buf, buf[:rate])
sc.pos = 0
}
func (sc *spongeCipher) Partial(dst, src, key []byte) {
sp := sc.sponge
rate := sc.rate
buf := sc.buf
pos := sc.pos
rem := ints.Max(len(dst), len(src), len(key)) // bytes to process
for rem > 0 {
if pos == rate { // process next block if needed
sp.Transform(buf, buf[:rate])
pos = 0
}
n := ints.Min(rem, rate-pos) // bytes to process in this block
// squeeze cryptographic output
ndst := ints.Min(n, len(dst)) // # bytes to write to dst
nsrc := ints.Min(ndst, len(src)) // # src bytes available
for i := 0; i < nsrc; i++ { // XOR-encrypt from src to dst
dst[i] = src[i] ^ buf[pos+i]
}
copy(dst[nsrc:ndst], buf[pos+nsrc:]) // "XOR" with 0 bytes
dst = dst[ndst:]
src = src[nsrc:]
// absorb cryptographic input (which may overlap with dst)
nkey := ints.Min(n, len(key)) // # key bytes available
copy(buf[pos:], key[:nkey])
for i := nkey; i < n; i++ { // missing key bytes implicitly 0
buf[pos+i] = 0
}
key = key[nkey:]
pos += n
rem -= n
}
sc.pos = pos
//println("Decrypted",more,"\n" + hex.Dump(osrc) + "->\n" + hex.Dump(odst))
}
func (sc *spongeCipher) Message(dst, src, key []byte) {
sc.Partial(dst, src, key)
sc.padMessage()
}
func (sc *spongeCipher) special(domain byte, index int) {
// ensure buffer is non-full before changing domain-separator
rate := sc.rate
if sc.pos == rate {
sc.sponge.Transform(sc.buf, sc.buf[:rate])
sc.pos = 0
}
// set the temporary capacity-bytes domain-separation configuration
sc.setDomain(domain, index)
// process one special block
sc.padMessage()
// revert to the normal domain-separation configuration
sc.setDomain(domainPayload, 0)
}
/*
// XXX move to abstract.Cipher?
func (sc *spongeCipher) Fork(nsubs int) []abstract.CipherState {
subs := make([]abstract.Cipher, nsubs)
for i := range subs {
sub := sc.clone()
sub.special(domainFork, 1+i) // reserve 0 for parent
subs[i] = sub
}
// ensure the parent is separated from all its children
sc.special(domainFork, 0)
return subs
}
func xorBytes(dst, src []byte) {
for i := range dst {
dst[i] ^= src[i]
}
}
// XXX move to abstract.Cipher?
func (sc *spongeCipher) Join(subs ...abstract.CipherState) {
// mark the join transformation in the parent first
sc.special(domainJoin, 0)
// now transform and mix in all the children
buf := sc.buf
for i := range subs {
sub := subs[i].(*spongeCipher)
sub.special(domainJoin, 1+i) // reserve 0 for parent
xorBytes(buf, sub.buf) // XOR sub's state into parent's
sub.buf = nil // make joined sub unusable
}
}
*/
func (sc *spongeCipher) clone() *spongeCipher {
nsc := *sc
nsc.sponge = sc.sponge.Clone()
nsc.buf = make([]byte, sc.rate+sc.cap)
copy(nsc.buf, sc.buf)
return &nsc
}
func (sc *spongeCipher) Clone() abstract.CipherState {
return sc.clone()
}
func (sc *spongeCipher) KeySize() int {
return sc.sponge.Capacity() >> 1
}
func (sc *spongeCipher) HashSize() int {
return sc.sponge.Capacity()
}
func (sc *spongeCipher) BlockSize() int {
return sc.sponge.Rate()
}