-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstrobe.h
420 lines (368 loc) · 14.9 KB
/
strobe.h
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
/**
* @file strobe.h
* @copyright
* Copyright (c) 2015-2016 Cryptography Research, Inc. \n
* Released under the MIT License. See LICENSE.txt for license information.
* @author Mike Hamburg
* @brief Strobe lite protocol instances.
*/
#ifndef __STROBE_H__
#define __STROBE_H__
/* TODO: Implement state compaction, particularly for PRNG state */
/* TODO: Test this against the Python reference. */
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include "strobe_config.h"
/**
* A control word holds flags and other information that control a STROBE operation.
* Defined below.
*/
typedef uint32_t control_word_t;
/* Strobe object, below */
struct strobe_s;
/** Initialize a STROBE object, using the description as a domain separator. */
void strobe_init (
struct strobe_s *__restrict__ strobe,
const uint8_t *description,
size_t desclen
);
/** Underlying duplex primitive. */
ssize_t strobe_duplex (
struct strobe_s *__restrict__ strobe,
control_word_t flags,
uint8_t *inside,
ssize_t len
);
/**
* More complex duplex primitive.
* First reads/writes metadata based on control_flags, then data. Can pass
* -len instead of len when reading. This means any length up to len.
* Returns the number of data bytes read, or <0 on error.
*/
ssize_t strobe_operate (
struct strobe_s *__restrict__ strobe,
control_word_t control_flags,
uint8_t *inside,
ssize_t len
);
#if STROBE_SUPPORT_PRNG
/** Seed the generator with len bytes of randomness. */
void strobe_seed_prng(const uint8_t *data, ssize_t len);
/**
* Fill *data with len bytes of randomness.
* Return <0 if the generator is not seeded.
*/
int __attribute__((warn_unused_result))
strobe_randomize(uint8_t *data, ssize_t len);
#endif /* STROBE_SUPPORT_PRNG */
/* Flags as defined in the paper */
#define FLAG_I (1<<0) /**< Inbound */
#define FLAG_A (1<<1) /**< Has application-side data (eg, not a MAC) */
#define FLAG_C (1<<2) /**< Uses encryption or rekeying. */
#define FLAG_T (1<<3) /**< Send or receive data via transport */
#define FLAG_M (1<<4) /**< Operation carries metadata. */
#define FLAG_META_I (1<<20) /**< Metadata has I */
#define FLAG_META_A (1<<21) /**< Metadata has A (always set) */
#define FLAG_META_C (1<<22) /**< Metadata has C */
#define FLAG_META_T (1<<23) /**< Metadata has T */
#define FLAG_META_M (1<<24) /**< Metadata has M (always set) */
#define GET_META_FLAGS(cw) (((cw) >> 20) & 0x3F)
#define FLAG_MORE (1<<28) /**< Continue a streaming operation. */
#define FLAG_NO_DATA (1<<29) /**< Just send/recv the metadata, not the data. */
#if STROBE_SUPPORT_FLAG_POST
/**
* Post-op ratchet and/or MAC.
*
* TODO: I might want to remove these, because some details of how this is
* done might be application-specific. In particular, some applications will
* want to frame their MACs on the wire, and some will not.
*/
#define FLAG_POST_RATCHET (1<<30) /**< Ratchet state after */
#define FLAG_POST_MAC (1<<31) /**< Send/receive MAC after */
#endif
/* Operation types as defined in the paper */
#define TYPE_AD FLAG_A /**< Context data, not sent to trensport */
#define TYPE_KEY (FLAG_A | FLAG_C) /**< Symmetric key, not sent to transport */
#define TYPE_CLR (FLAG_T | FLAG_A) /**< Data to be sent in the clear */
#define TYPE_ENC (FLAG_T | FLAG_A | FLAG_C) /**< Data sent encrypted */
#define TYPE_MAC (FLAG_T | FLAG_C) /**< Message authentication code */
#define TYPE_RATCHET FLAG_C /**< Erase data to prevent rollback */
#define TYPE_PRF (FLAG_I | FLAG_A | FLAG_C) /**< Return pseudorandom hash */
/** For operate, have (n)-byte little-endian length field. */
#define CW_LENGTH_BYTES(n) ((uint32_t)(n)<<16)
#define STROBE_CW_GET_LENGTH_BYTES(cw) ((cw)>>16 & 0xF)
#define MAKE_IMPLICIT(cw) ((cw) &~ (FLAG_T | FLAG_META_T))
#define MAKE_LENGTHLESS(cw) ((cw) &~ CW_LENGTH_BYTES(0x0F))
#define GET_CONTROL_TAG(cw) (((cw)>>8)&0xFF)
#define CONTROL_WORD(w,value,flags) enum { w=value<<8|flags|FLAG_META_A|FLAG_META_M }
/* Recommended control words */
/* 0x00-0x0F: symmetric cryptography */
CONTROL_WORD(SYM_SCHEME , 0x00, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(SYM_KEY , 0x01, TYPE_KEY );
CONTROL_WORD(APP_PLAINTEXT , 0x02, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(APP_CIPHERTEXT , 0x03, TYPE_ENC | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(NONCE , 0x04, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(AUTH_DATA , 0x05, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(MAC , 0x06, TYPE_MAC | CW_LENGTH_BYTES(2) );
CONTROL_WORD(HASH , 0x07, TYPE_PRF | CW_LENGTH_BYTES(2) );
CONTROL_WORD(DERIVE_KEY , 0x08, TYPE_PRF | CW_LENGTH_BYTES(2) );
CONTROL_WORD(BE_SLOW , 0x0C, TYPE_RATCHET | CW_LENGTH_BYTES(4) );
CONTROL_WORD(SIV_PT_INNER , 0x0D, TYPE_CLR ); /* FUTURE: implement SIV */
CONTROL_WORD(SIV_MAC_OUTER , 0x0E, TYPE_CLR );
CONTROL_WORD(RATCHET , 0x0F, TYPE_RATCHET );
/* 0x10-0x1F: Asymmetric key exchange and encryption */
CONTROL_WORD(KEM_SCHEME , 0x10, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(PUBLIC_KEY , 0x11, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(KEM_EPH , 0x12, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(KEM_RESULT , 0x13, TYPE_KEY );
/* 0x18-0x1F: Signatures */
CONTROL_WORD(SIG_SCHEME , 0x18, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(SIG_EPH , 0x19, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(SIG_CHALLENGE , 0x1A, TYPE_PRF | CW_LENGTH_BYTES(2) );
CONTROL_WORD(SIG_RESPONSE , 0x1B, TYPE_ENC | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(SIG_DETERM , 0x1C, TYPE_PRF | CW_LENGTH_BYTES(2) );
/* 0x20-0x2F: header and other metadata */
CONTROL_WORD(HANDSHAKE , 0x20, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(VERSION , 0x21, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CIPHERSUITE , 0x22, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(META_PLAINTEXT , 0x24, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(META_CIPHERTEXT, 0x25, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERTIFICATE , 0x26, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(ENCRYPTED_CERT , 0x27, TYPE_ENC | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(OVER , 0x2E, TYPE_MAC | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CLOSE , 0x2F, TYPE_MAC | CW_LENGTH_BYTES(2) | FLAG_META_T );
/* 0x30-0x3F: Certificates.
*
* These are still experimental and unimplemented, and will probably change.
* The intention is that certs should look something like this:
*
* CERT_VERSION 1
* CERT_SERIAL .{0,32} ? # for revocation, else omitted
* CERT_VALIDITY? # omitted if your devices aren't tracking time or can't update
* CERT_PURPOSE .*? # if your application makes such a distinction
* ((CERT_REC_PRE .* CERT_REC_POST .*) | (CERT_NAME .*))
* ^ if the cert is an intermediate ^ if it isn't an intermediate
* (CERT_PK_SCHEME PUBLIC_KEY)+ # Limited to 1? Could have multiple? Dunno.
*
* CERT_COMMENT *
* (SIG_SCHEME SIG_CHAL SIG_EPH SIG_RESPONSE)+ # or similar depending on sig scheme
* ^ Possibly this should be limited to 1.
*
* If your application uses cert chains, they would be given in separate CERTIFICATE
* (or ENCRYPTED_CERT) messages, in order from CA to leaf. At any time, the client
* could track "what's the most recent trusted intermediate which has authority over
* the name I'm trying to contact." Then if there are multiple certifying chains, the
* untrusted ones would be ignored.
*/
CONTROL_WORD(CERT_VERSION , 0x30, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_SERIAL , 0x31, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_VALIDITY , 0x32, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_PURPOSE , 0x33, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_REC_PRE , 0x34, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_REC_POST , 0x35, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_NAME , 0x36, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_PK_SCHEME , 0x37, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
CONTROL_WORD(CERT_COMMENT , 0x3F, TYPE_CLR | CW_LENGTH_BYTES(2) | FLAG_META_T );
#if STROBE_INTEROP_F_BITS == 1600
#define kword_t uint64_t
#elif STROBE_INTEROP_F_BITS == 800
#define kword_t uint32_t
#elif STROBE_INTEROP_F_BITS == 400
#define kword_t uint16_t
#else
#error "Strobe supports only Keccak-F{400,800,1600}"
#endif
/* IO callback context. Opaque to STROBE. */
typedef struct {
void *a, *b; /**< Two pointers for use by the callback context. */
#if STROBE_IO_CTX_HAS_FD
int fd; /**< A file descriptor, or whatever. */
#endif
} strobe_io_ctx_s;
/** Callback context */
typedef struct {
// FUTURE: make these return two values?
/**
* Read up to [size] bytes of data. Set the buffer to where it
* points, and return how many bytes were actually read.
*/
ssize_t (*read) (strobe_io_ctx_s *ctx, const uint8_t **buffer, ssize_t size);
/**
* The write callback is trickier.
*
* The first call returns a buffer to write the data to.
*
* The next call writes that data out, and returns a new (or more likely,
* the same) buffer.
*/
ssize_t (*write) (strobe_io_ctx_s *ctx, uint8_t **buffer, ssize_t size);
} strobe_io_callbacks_s;
extern const strobe_io_callbacks_s strobe_io_cb_buffer, strobe_io_cb_const_buffer;
/** Keccak's domain: 25 words of size b/25, or b/8 bytes. */
typedef union {
kword_t w[25]; uint8_t b[25*sizeof(kword_t)/sizeof(uint8_t)];
} kdomain_s;
/** The main strobe state object. */
typedef struct strobe_s {
kdomain_s state;
uint8_t position, pos_begin, flags;
const strobe_io_callbacks_s *io;
strobe_io_ctx_s io_ctx;
} strobe_s, strobe_t[1];
#if STROBE_CW_MAX_LENGTH_BYTES <= 1
typedef uint8_t strobe_length_t;
#elif STROBE_CW_MAX_LENGTH_BYTES <= 2
typedef uint16_t strobe_length_t;
#elif STROBE_CW_MAX_LENGTH_BYTES <= 4
typedef uint32_t strobe_length_t;
#elif STROBE_CW_MAX_LENGTH_BYTES <= 8
typedef uint64_t strobe_length_t;
#elif STROBE_CW_MAX_LENGTH_BYTES <= 16
typedef uint128_t strobe_length_t;
#else
#error "Can't deal with >128-bit length fields'"
#endif
typedef struct {
uint8_t control;
strobe_length_t len;
} __attribute__((packed)) strobe_serialized_control_t;
/* Protocol building blocks */
#define TRY(foo) do { ssize_t _ret = (foo); if (_ret < 0) return _ret; } while(0)
/**
* Destroy a STROBE object by writing zeros over it.
* NB: if you don't have C11's memset_s, the compiler might optimize this call
* away!
*/
static inline void strobe_destroy(strobe_t strobe) {
#ifdef __STDC_LIB_EXT1__
memset_s(strobe,sizeof(*strobe),0,sizeof(*strobe));
#else
memset(strobe,0,sizeof(*strobe));
#endif
}
/**
* Detach the I/O from a STROBE object.
*/
static inline void strobe_detach(strobe_t strobe) {
strobe->io = NULL;
/* Not really relevant: */
// strobe->io_ctx.a = strobe->io_ctx.b = NULL;
}
/** Reverse the role of a STROBE object (i.e. to emulate the other guy). */
static inline void strobe_reverse(strobe_t strobe) {
strobe->flags ^= FLAG_I;
}
/** Attach a buffer to a strobe object. */
static inline void strobe_attach_buffer(strobe_t strobe, uint8_t *start, size_t length) {
strobe->io = &strobe_io_cb_buffer;
strobe->io_ctx.a = start;
strobe->io_ctx.b = start+length;
}
/** Attach a buffer to a strobe object. */
static inline void strobe_attach_const_buffer(strobe_t strobe, uint8_t *start, size_t length) {
strobe->io = &strobe_io_cb_const_buffer;
strobe->io_ctx.a = start;
strobe->io_ctx.b = start+length;
}
/** Same as operate, but only for sending data or putting it into the sponge. */
static inline ssize_t strobe_put (
strobe_s *__restrict__ strobe,
control_word_t control_flags,
const uint8_t *inside,
ssize_t len
) {
assert(!(control_flags & (FLAG_M|FLAG_I|FLAG_META_I)));
return strobe_operate(strobe,control_flags,(uint8_t *)inside,len);
}
/** Receive data or extract it from the sponge. */
static inline ssize_t strobe_get (
strobe_s *__restrict__ strobe,
control_word_t control_flags,
uint8_t *inside,
ssize_t len
) {
assert(!(control_flags & FLAG_M));
assert((control_flags & (FLAG_T|FLAG_C)) != 0);
control_flags |= FLAG_I;
if (control_flags & FLAG_META_T) control_flags |= FLAG_META_I;
return strobe_operate(strobe,control_flags,(uint8_t *)inside,len);
}
/* Endian swaps */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
static inline kword_t eswap_letoh(kword_t w) { return w; }
static inline kword_t eswap_htole(kword_t w) { return w; }
static inline uint16_t eswap_letoh_16(uint16_t w) { return w; }
static inline uint16_t eswap_htole_16(uint16_t w) { return w; }
static inline uint32_t eswap_letoh_32(uint32_t w) { return w; }
static inline uint32_t eswap_htole_32(uint32_t w) { return w; }
static inline uint64_t eswap_letoh_64(uint64_t w) { return w; }
static inline uint64_t eswap_htole_64(uint64_t w) { return w; }
static inline strobe_length_t eswap_letoh_sl(strobe_length_t w) { return w; }
static inline strobe_length_t eswap_htole_sl(strobe_length_t w) { return w; }
#else
#error "Fix eswap() on non-little-endian machine"
#endif
/**
* Receive just control/metadata from the other party.
* This is for complex protocols where you may not know what will come next.
*/
static inline ssize_t strobe_get_control (
strobe_s *__restrict__ strobe,
strobe_serialized_control_t *cw,
uint32_t flags
) {
unsigned int length_bytes = STROBE_CW_GET_LENGTH_BYTES(flags);
control_word_t cwf = GET_META_FLAGS(flags) | FLAG_I | FLAG_T;
cw->len = 0;
ssize_t ret = strobe_duplex(strobe, cwf, (uint8_t *)cw, sizeof(cw->control) + length_bytes);
/* Probably a nop, allowing tailcall, except we're inline anyway */
cw->len = eswap_letoh_sl(cw->len);
return ret;
}
static inline ssize_t strobe_put_mac( strobe_t strobe ) {
return strobe_operate(strobe, MAC, NULL, STROBE_INTEROP_MAC_BYTES);
}
static inline ssize_t strobe_get_mac(strobe_t strobe ) {
return strobe_operate(strobe, MAC|FLAG_I, NULL, STROBE_INTEROP_MAC_BYTES);
}
static inline ssize_t strobe_key (
strobe_t strobe,
control_word_t cw,
const uint8_t *data,
uint16_t len
) {
assert(!(cw & FLAG_T));
return strobe_put(strobe, cw, data, len);
}
#if STROBE_CONVENIENCE_ECDH
int strobe_eph_ecdh (
strobe_t strobe,
int i_go_first
);
#endif
#if X25519_SUPPORT_SIGN
int strobe_session_sign (
strobe_t strobe,
const uint8_t my_seckey[32],
const uint8_t my_pubkey[32]
);
#endif
#if X25519_SUPPORT_VERIFY
int strobe_session_verify (
strobe_t strobe,
const uint8_t their_pubkey[32]
);
#if STROBE_SUPPORT_CERT_VERIFY
/* Parse a signature but don't verify it.
* Useful for intermediate certs we don't trust.
*/
int strobe_session_dont_verify (
strobe_t strobe,
const uint8_t their_pubkey[32]
);
#endif
#endif
#endif /* __STROBE_H__ */