-
Notifications
You must be signed in to change notification settings - Fork 5
/
strobe.go
521 lines (455 loc) · 13.7 KB
/
strobe.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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
package strobe
/***************************************************/
/*
/* This is a compact implementation of Strobe.
/* As it hasn't been thoroughly tested only use this
/* for experimental purposes :)
/*
/* Author: David Wong
/* Contact: www.cryptologie.net/contact
/*
/***************************************************/
import (
"bytes"
"encoding/binary"
"encoding/hex"
)
const (
// The size of the authentication tag used in AEAD functions
MACLEN = 16
)
// KEY inserts a key into the state.
// It also provides forward secrecy.
func (s *Strobe) KEY(key []byte) {
s.Operate(false, "KEY", key, 0, false)
}
// PRF provides a hash of length `output_len` of all previous operations
// It can also be used to generate random numbers, it is forward secure.
func (s *Strobe) PRF(outputLen int) []byte {
return s.Operate(false, "PRF", []byte{}, outputLen, false)
}
// Send_ENC_unauthenticated is used to encrypt some plaintext
// it should be followed by Send_MAC in order to protect its integrity
// `meta` is used for encrypted framing data.
func (s *Strobe) Send_ENC_unauthenticated(meta bool, plaintext []byte) []byte {
return s.Operate(meta, "send_ENC", plaintext, 0, false)
}
// Recv_ENC_unauthenticated is used to decrypt some received ciphertext
// it should be followed by Recv_MAC in order to protect its integrity
// `meta` is used for decrypting framing data.
func (s *Strobe) Recv_ENC_unauthenticated(meta bool, ciphertext []byte) []byte {
return s.Operate(meta, "recv_ENC", ciphertext, 0, false)
}
// AD allows you to authenticate Additional Data
// it should be followed by a Send_MAC or Recv_MAC in order to truly work
func (s *Strobe) AD(meta bool, additionalData []byte) {
s.Operate(meta, "AD", additionalData, 0, false)
}
// Send_CLR allows you to send data in cleartext
// `meta` is used to send framing data
func (s *Strobe) Send_CLR(meta bool, cleartext []byte) {
s.Operate(meta, "send_CLR", cleartext, 0, false)
}
// Recv_CLR allows you to receive data in cleartext.
// `meta` is used to receive framing data
func (s *Strobe) Recv_CLR(meta bool, cleartext []byte) {
s.Operate(meta, "recv_CLR", cleartext, 0, false)
}
// Send_MAC allows you to produce an authentication tag.
// `meta` is appropriate for checking the integrity of framing data.
func (s *Strobe) Send_MAC(meta bool, output_length int) []byte {
return s.Operate(meta, "send_MAC", []byte{}, output_length, false)
}
// Recv_MAC allows you to verify a received authentication tag.
// `meta` is appropriate for checking the integrity of framing data.
func (s *Strobe) Recv_MAC(meta bool, MAC []byte) bool {
if s.Operate(meta, "recv_MAC", MAC, 0, false)[0] == 0 {
return true
}
return false
}
// RATCHET allows you to introduce forward secrecy in a protocol.
func (s *Strobe) RATCHET(length int) {
s.Operate(false, "RATCHET", []byte{}, length, false)
}
// Send_AEAD allows you to encrypt data and authenticate additional data
// It is similar to AES-GCM.
func (s *Strobe) Send_AEAD(plaintext, ad []byte) (ciphertext []byte) {
ciphertext = append(ciphertext, s.Send_ENC_unauthenticated(false, plaintext)...)
s.AD(false, ad)
ciphertext = append(ciphertext, s.Send_MAC(false, MACLEN)...)
return
}
// Recv_AEAD allows you to decrypt data and authenticate additional data
// It is similar to AES-GCM.
func (s *Strobe) Recv_AEAD(ciphertext, ad []byte) (plaintext []byte, ok bool) {
if len(ciphertext) < MACLEN {
ok = false
return
}
plaintext = s.Recv_ENC_unauthenticated(false, ciphertext[:len(ciphertext)-MACLEN])
s.AD(false, ad)
ok = s.Recv_MAC(false, ciphertext[len(ciphertext)-MACLEN:])
return
}
//
// Strobe Objects
//
type role uint8 // for strobe.I0
const (
iInitiator role = iota // set if we send the first transport message
iResponder // set if we receive the first transport message
iNone // starting value
)
/*
We do not use strobe's `pos` variable here since it is easily
obtainable via `len(buf)`
*/
// TODO: accept permutations of different sizes
type Strobe struct {
// config
duplexRate int // 1600/8 - security/4
StrobeR int // duplexRate - 2
// strobe specific
initialized bool // used to avoid padding during the first permutation
posBegin uint8 // start of the current operation (0 := previous block)
I0 role
// streaming API
curFlags flag
// duplex construction (see sha3.go)
a [25]uint64 // the actual state
buf []byte // a pointer into the storage, it also serves as `pos` variable
storage []byte // to-be-XORed (used for optimizations purposes)
tempStateBuf []byte // utility slice used for temporary duplexing operations
}
// Clone allows you to clone a Strobe state.
func (s Strobe) Clone() *Strobe {
ret := s
// need to recreate some buffers
ret.storage = make([]byte, s.duplexRate)
copy(ret.storage, s.storage)
ret.tempStateBuf = make([]byte, s.duplexRate)
copy(ret.tempStateBuf, s.tempStateBuf)
// and set pointers
ret.buf = ret.storage[:len(ret.buf)]
return &ret
}
// Serialize allows one to serialize the strobe state to later recover it.
// [security(1)|initialized(1)|I0(1)|curFlags(1)|posBegin(1)|pos(1)|[25]uint64 state]
func (s Strobe) Serialize() []byte {
// serialized data
serialized := make([]byte, 6+25*8) // TODO: this is only for keccak-f[1600]
// security?
security := (1600/8 - s.duplexRate) * 4
if security == 128 {
serialized[0] = 0
} else {
serialized[0] = 1
}
// initialized?
if s.initialized {
serialized[1] = 1
} else {
serialized[1] = 0
}
// I0
serialized[2] = byte(s.I0)
// curFlags
serialized[3] = byte(s.curFlags)
// posBegin
serialized[4] = byte(s.posBegin)
// pos
serialized[5] = byte(len(s.buf))
// make sure to XOR what's left to XOR in the storage
var buf [1600 / 8]byte
var state [25]uint64
copy(buf[:len(s.buf)], s.storage[:len(s.buf)]) // len(s.buf) = pos
copy(state[:], s.a[:])
xorState(&state, buf[:])
// state
var b []byte
b = serialized[6:]
for i := 0; len(b) >= 8; i++ {
binary.LittleEndian.PutUint64(b, state[i])
b = b[8:]
}
//
return serialized
}
// Recover state allows one to re-create a strobe state from a serialized state.
// [security(1)|initialized(1)|I0(1)|curFlags(1)|posBegin(1)|pos(1)|[25]uint64 state]
func RecoverState(serialized []byte) (s Strobe) {
if len(serialized) != 6+25*8 {
panic("strobe: cannot recover state of invalid length")
}
// security?
if serialized[0] > 1 {
panic("strobe: cannot recover state with invalid security")
}
security := 128
if security == 1 {
security = 256
}
// init vars from security
s.duplexRate = 1600/8 - security/4
s.StrobeR = s.duplexRate - 2
// need to recreate some buffers
s.storage = make([]byte, s.duplexRate)
s.tempStateBuf = make([]byte, s.duplexRate)
// initialized?
if serialized[1] == 1 {
s.initialized = true
} else {
s.initialized = false
}
// I0?
if serialized[2] > 3 {
panic("strobe: cannot recover state with invalid role")
}
s.I0 = role(serialized[2])
// curFlags + posBegin
s.curFlags = flag(serialized[3])
s.posBegin = uint8(serialized[4])
// pos
pos := int(serialized[5])
s.buf = s.storage[:pos]
// state
serialized = serialized[6:]
for i := 0; i < 25; i++ {
a := binary.LittleEndian.Uint64(serialized[:8])
s.a[i] = a
serialized = serialized[8:]
}
//
return
}
//
// Flags
//
type flag uint8
const (
flagI flag = 1 << iota
flagA
flagC
flagT
flagM
flagK
)
var operationMap = map[string]flag{
"AD": flagA,
"KEY": flagA | flagC,
"PRF": flagI | flagA | flagC,
"send_CLR": flagA | flagT,
"recv_CLR": flagI | flagA | flagT,
"send_ENC": flagA | flagC | flagT,
"recv_ENC": flagI | flagA | flagC | flagT,
"send_MAC": flagC | flagT,
"recv_MAC": flagI | flagC | flagT,
"RATCHET": flagC,
}
//
// Helper
//
// this only works for 8-byte alligned buffers
func xorState(state *[25]uint64, buf []byte) {
n := len(buf) / 8
for i := 0; i < n; i++ {
a := binary.LittleEndian.Uint64(buf)
state[i] ^= a
buf = buf[8:]
}
}
// this only works for 8-byte alligned buffers
func outState(state [25]uint64, b []byte) {
for i := 0; len(b) >= 8; i++ {
binary.LittleEndian.PutUint64(b, state[i])
b = b[8:]
}
}
// since the golang implementation does not absorb
// things in the state "right away" (sometimes just
// wait for the buffer to fill) we need a function
// to properly print the state even when the state
// is in this "temporary" state.
func (s Strobe) debugPrintState() string {
// copy _storage into buf
var buf [1600 / 8]byte
copy(buf[:len(s.buf)], s.storage[:len(s.buf)])
// copy _state into state
var state [25]uint64
copy(state[:], s.a[:])
// xor
xorState(&state, buf[:])
// print
outState(state, buf[:])
return hex.EncodeToString(buf[:])
}
//
// Core functions
//
// InitStrobe allows you to initialize a new strobe instance with a customization string (that can be empty) and a security target (either 128 or 256).
func InitStrobe(customizationString string, security int) (s Strobe) {
// compute security and rate
if security != 128 && security != 256 {
panic("strobe: security must be set to either 128 or 256")
}
s.duplexRate = 1600/8 - security/4
s.StrobeR = s.duplexRate - 2
// init vars
s.storage = make([]byte, s.duplexRate)
s.tempStateBuf = make([]byte, s.duplexRate)
s.I0 = iNone
s.initialized = false
// absorb domain + initialize + absorb custom string
domain := []byte{1, byte(s.StrobeR + 2), 1, 0, 1, 12 * 8}
domain = append(domain, []byte("STROBEv1.0.2")...)
s.buf = s.storage[:0]
s.duplex(domain, false, false, true)
s.initialized = true
s.Operate(true, "AD", []byte(customizationString), 0, false)
return
}
// runF: applies the STROBE's + cSHAKE's padding and the Keccak permutation
func (s *Strobe) runF() {
if s.initialized {
// if we're initialize we apply the strobe padding
if len(s.buf) > s.StrobeR {
panic("strobe: buffer is never supposed to reach strobeR")
}
s.buf = append(s.buf, s.posBegin)
s.buf = append(s.buf, 0x04)
zerosStart := len(s.buf)
s.buf = s.storage[:s.duplexRate]
for i := zerosStart; i < s.duplexRate; i++ {
s.buf[i] = 0
}
s.buf[s.duplexRate-1] ^= 0x80
xorState(&s.a, s.buf)
} else if len(s.buf) != 0 {
// otherwise we just pad with 0s for xorState to work
zerosStart := len(s.buf) // rate = [0--end_of_buffer/zeroStart---duplexRate]
s.buf = s.storage[:s.duplexRate]
for i := zerosStart; i < s.duplexRate; i++ {
s.buf[i] = 0
}
xorState(&s.a, s.buf)
}
// run the permutation
keccakF1600(&s.a, 24)
// reset the buffer and set posBegin to 0
// (meaning that the current operation started on a previous block)
s.buf = s.storage[:0]
s.posBegin = 0
}
// duplex: the duplex call
func (s *Strobe) duplex(data []byte, cbefore, cafter, forceF bool) {
// process data block by block
for len(data) > 0 {
todo := s.StrobeR - len(s.buf)
if todo > len(data) {
todo = len(data)
}
if cbefore {
outState(s.a, s.tempStateBuf)
for idx, state := range s.tempStateBuf[len(s.buf) : len(s.buf)+todo] {
data[idx] ^= state
}
}
// buffer what's to be XOR'ed (we XOR once during runF)
s.buf = append(s.buf, data[:todo]...)
if cafter {
outState(s.a, s.tempStateBuf)
for idx, state := range s.tempStateBuf[len(s.buf)-todo : len(s.buf)] {
data[idx] ^= state
}
}
// what's next for the loop?
data = data[todo:]
// If the duplex is full, time to XOR + padd + permutate.
if len(s.buf) == s.StrobeR {
s.runF()
}
}
// sometimes we the next operation to start on a new block
if forceF && len(s.buf) != 0 {
s.runF()
}
return
}
// Operate runs an operation (see OperationMap for a list of operations).
// For operations that only require a length, provide the length via the
// length argument with an empty slice []byte{}. For other operations provide
// a zero length.
// Result is always retrieved through the return value. For boolean results,
// check that the first index is 0 for true, 1 for false.
func (s *Strobe) Operate(meta bool, operation string, dataConst []byte, length int, more bool) []byte {
// operation is valid?
var flags flag
var ok bool
if flags, ok = operationMap[operation]; !ok {
panic("not a valid operation")
}
// operation is meta?
if meta {
flags |= flagM
}
// does the operation requires a length?
var data []byte
if (flags&(flagI|flagT) != (flagI | flagT)) && (flags&(flagI|flagA) != flagA) {
if length == 0 {
panic("A length should be set for this operation.")
}
data = bytes.Repeat([]byte{0}, length)
} else {
if length != 0 {
panic("Output length must be zero except for PRF, send_MAC and RATCHET operations.")
}
data = make([]byte, len(dataConst))
copy(data, dataConst)
}
// is this call the continuity of a previous call?
if more {
if flags != s.curFlags {
panic("Flag should be the same when streaming operations.")
}
} else {
s.beginOp(flags)
s.curFlags = flags
}
// Operation
cAfter := (flags & (flagC | flagI | flagT)) == (flagC | flagT)
cBefore := (flags&flagC != 0) && (!cAfter)
s.duplex(data, cBefore, cAfter, false)
if (flags & (flagI | flagA)) == (flagI | flagA) {
// Return data for the application
return data
} else if (flags & (flagI | flagT)) == flagT {
// Return data for the transport.
return data
} else if (flags & (flagI | flagA | flagT)) == (flagI | flagT) {
// Check MAC: all output bytes must be 0
if more {
panic("not supposed to check a MAC with the 'more' streaming option")
}
var failures byte
for _, dataByte := range data {
failures |= dataByte
}
return []byte{failures} // 0 if correct, 1 if not
}
// Operation has no output
return nil
}
// beginOp: starts an operation
func (s *Strobe) beginOp(flags flag) {
if flags&flagT != 0 {
if s.I0 == iNone {
s.I0 = role(flags & flagI)
}
flags ^= flag(s.I0)
}
oldBegin := s.posBegin
s.posBegin = uint8(len(s.buf) + 1) // s.pos + 1
forceF := (flags&(flagC|flagK) != 0)
s.duplex([]byte{oldBegin, byte(flags)}, false, false, forceF)
}