chiark / gitweb /
SECURITY: fixed fix to buffer handling
[secnet.git] / transform-eax.c
1 /*
2  * eax-transform.c: EAX-Serpent bulk data transformation
3  *
4  * We use EAX with the following parameters:
5  *
6  *   Plaintext:
7  *      Concatenation of:
8  *        Data packet as supplied to us
9  *        Zero or more zero bytes ignored by receiver } padding
10  *        One byte padding length                     }
11  *      This is a bit like PKCS#5.  It helps disguise message lengths.
12  *      It also provides a further room for future expansion.  When
13  *      transmitting we pad the message to the next multiple of
14  *      a configurable rounding factor, 16 bytes by default.
15  *
16  *   Transmitted message:
17  *      Concatenation of:
18  *        EAX ciphertext
19  *        32-bit sequence number (initially zero)
20  *      The sequence number allows us to discard far-too-old
21  *      packets.
22  *
23  *   Nonce:
24  *      Concatenation of:
25  *        32-bit sequence number (big endian)
26  *               initial value comes from SHA-512 hash (see below)
27  *        1 byte: 0x01 if sender has setup priority, 0x00 if it doesn't
28  *               (ie, the direction of data flow)
29  *
30  *   Header: None
31  *
32  *   Tag length:
33  *      16 bytes (128 bits) by default
34  *
35  *   Key:
36  *      The first 32 bytes of the SHA-512 hash of the shared secret
37  *      from the DH key exchange (the latter being expressed as
38  *      the shortest possible big-endian octet string).
39  *
40  * The bytes [32,40> of the hash of the shared secret are used for
41  * initial sequence numbers: [32,36> for those sent by the end without
42  * setup priority, [36,40> for those for the other end.
43  *
44  */
45
46 #include "secnet.h"
47 #include "unaligned.h"
48 #include "util.h"
49 #include "serpent.h"
50 #include "sha512.h"
51 #include "transform-common.h"
52 #include "hexdebug.h"
53
54 #define BLOCK_SIZE 16
55 #define SEQLEN 4
56
57 struct transform_params {
58     uint32_t max_seq_skew, tag_length, padding_mask;
59 };
60
61 struct transform {
62     closure_t cl;
63     struct transform_if ops;
64     struct transform_params p;
65 };
66
67 struct transform_inst {
68     struct transform_inst_if ops;
69     struct transform_params p;
70     unsigned keyed:1;
71     /* remaining valid iff keyed */
72     unsigned direction:1;
73     uint32_t sendseq;
74     uint32_t lastrecvseq;
75     struct keyInstance key;
76     uint8_t info_b[BLOCK_SIZE], info_p[BLOCK_SIZE];
77 };
78
79 static void block_encrypt(struct transform_inst *transform_inst,
80                           uint8_t dst[BLOCK_SIZE],
81                           const uint8_t src[BLOCK_SIZE])
82 {
83     serpent_encrypt(&transform_inst->key, src, dst);
84 }
85
86 #define INFO                    struct transform_inst *transform_inst
87 #define I                       transform_inst
88 #define EAX_ENTRYPOINT_DECL     static
89 #define BLOCK_ENCRYPT(dst,src)  block_encrypt(transform_inst,dst,src)
90 #define INFO_B                  (transform_inst->info_b)
91 #define INFO_P                  (transform_inst->info_p)
92
93 #include "eax.c"
94
95 #if 0
96
97 #define TEAX_DEBUG(ary,sz) teax_debug(__func__,__LINE__,#ary,#sz,ary,sz)
98 static void teax_debug(const char *func, int line,
99                        const char *aryp, const char *szp,
100                        const void *ary, size_t sz)
101 {
102     fprintf(stderr,"TEAX %s:%-3d %10s %15s : ", func,line,aryp,szp);
103     hexdebug(stderr,ary,sz);
104     fprintf(stderr,"\n");
105 }
106
107 #else
108
109 #define TEAX_DEBUG(ary,sz) /* empty */
110
111 #endif
112
113 static bool_t transform_setkey(void *sst, uint8_t *key, int32_t keylen,
114                                bool_t direction)
115 {
116     struct transform_inst *ti=sst;
117     struct sha512_ctx hash_ctx;
118     uint8_t hash_out[64];
119
120     TEAX_DEBUG(key,keylen);
121
122     sha512_init_ctx(&hash_ctx);
123     sha512_process_bytes(key, keylen, &hash_ctx);
124     sha512_finish_ctx(&hash_ctx, hash_out);
125
126     TEAX_DEBUG(hash_out,32);
127     TEAX_DEBUG(hash_out+32,8);
128
129     ti->direction=direction;
130     ti->sendseq=get_uint32(hash_out+32+direction*4);
131     ti->lastrecvseq=get_uint32(hash_out+32+!direction*4);
132     serpent_makekey(&ti->key, 32*8, hash_out);
133     eax_setup(ti);
134     ti->keyed=True;
135
136     return True;
137 }
138
139 TRANSFORM_VALID;
140
141 TRANSFORM_DESTROY;
142
143 static void transform_delkey(void *sst)
144 {
145     struct transform_inst *ti=sst;
146
147     FILLZERO(ti->key);
148     FILLZERO(ti->info_b);
149     FILLZERO(ti->info_p);
150     ti->keyed=False;
151 }
152
153 static uint32_t transform_forward(void *sst, struct buffer_if *buf,
154                                   const char **errmsg)
155 {
156     struct transform_inst *ti=sst;
157
158     KEYED_CHECK;
159     
160     size_t padlen = ti->p.padding_mask - buf->size;
161     padlen &= ti->p.padding_mask;
162     padlen++;
163
164     uint8_t *pad = buf_append(buf,padlen);
165     memset(pad, 0, padlen-1);
166     pad[padlen-1] = padlen;
167
168     uint8_t nonce[SEQLEN+1];
169     put_uint32(nonce,ti->sendseq);
170     nonce[SEQLEN] = ti->direction;
171
172     TEAX_DEBUG(nonce,sizeof(nonce));
173     TEAX_DEBUG(buf->start,buf->size);
174
175     assert(buf_append(buf,ti->p.tag_length));
176     eax_encrypt(ti, nonce,sizeof(nonce), 0,0,
177                 buf->start,buf->size-ti->p.tag_length,
178                 ti->p.tag_length, buf->start);
179
180     TEAX_DEBUG(buf->start,buf->size);
181
182     memcpy(buf_append(buf,SEQLEN), nonce, SEQLEN);
183
184     TEAX_DEBUG(nonce,SEQLEN);
185
186     ti->sendseq++;
187
188     return 0;
189 }
190
191 static uint32_t transform_reverse(void *sst, struct buffer_if *buf,
192                                   const char **errmsg)
193 {
194     struct transform_inst *ti=sst;
195
196     KEYED_CHECK;
197
198     TEAX_DEBUG(buf->start,buf->size);
199
200     uint8_t nonce[SEQLEN+1];
201     const uint8_t *seqp = buf_unappend(buf,SEQLEN);
202     if (!seqp) goto too_short;
203
204     TEAX_DEBUG(seqp,SEQLEN);
205
206     uint32_t seqnum = get_uint32(seqp);
207
208     memcpy(nonce,seqp,SEQLEN);
209     nonce[4] = !ti->direction;
210
211     TEAX_DEBUG(nonce,sizeof(nonce));
212     TEAX_DEBUG(buf->start,buf->size);
213
214     bool_t ok = eax_decrypt(ti, nonce,sizeof(nonce), 0,0, buf->start,buf->size,
215                             ti->p.tag_length, buf->start);
216     if (!ok) {
217         TEAX_DEBUG(0,0);
218         *errmsg="EAX decryption failed";
219         return 1;
220     }
221     assert(buf->size >= (int)ti->p.tag_length);
222     buf->size -= ti->p.tag_length;
223
224     TEAX_DEBUG(buf->start,buf->size);
225
226     const uint8_t *padp = buf_unappend(buf,1);
227     if (!padp) goto too_short;
228
229     TEAX_DEBUG(padp,1);
230
231     size_t padlen = *padp;
232     if (!buf_unappend(buf,padlen-1)) goto too_short;
233
234     SEQNUM_CHECK(seqnum, ti->p.max_seq_skew);
235
236     TEAX_DEBUG(buf->start,buf->size);
237
238     return 0;
239
240  too_short:
241     *errmsg="ciphertext or plaintext too short";
242     return 1;
243 }
244
245 static struct transform_inst_if *transform_create(void *sst)
246 {
247     struct transform *st=sst;
248
249     TRANSFORM_CREATE_CORE;
250
251     ti->p=st->p;
252
253     return &ti->ops;
254 }
255
256 static list_t *transform_apply(closure_t *self, struct cloc loc,
257                                dict_t *context, list_t *args)
258 {
259     struct transform *st;
260     item_t *item;
261     dict_t *dict;
262
263     st=safe_malloc(sizeof(*st),"eax-serpent");
264     st->cl.description="eax-serpent";
265     st->cl.type=CL_TRANSFORM;
266     st->cl.apply=NULL;
267     st->cl.interface=&st->ops;
268     st->ops.st=st;
269
270     /* First parameter must be a dict */
271     item=list_elem(args,0);
272     if (!item || item->type!=t_dict)
273         cfgfatal(loc,"eax-serpent","parameter must be a dictionary\n");
274     dict=item->data.dict;
275
276     SET_CAPAB_TRANSFORMNUM(CAPAB_TRANSFORMNUM_EAXSERPENT);
277
278     st->p.max_seq_skew=dict_read_number(dict, "max-sequence-skew",
279                                         False, "eax-serpent", loc, 10);
280
281     st->p.tag_length=dict_read_number(dict, "tag-length-bytes",
282                                       False, "eax-serpent", loc, 128/8);
283     if (st->p.tag_length<1 || st->p.tag_length>BLOCK_SIZE)
284         cfgfatal(loc,"eax-serpent","tag-length-bytes out of range 0..%d\n",
285                  BLOCK_SIZE);
286
287     uint32_t padding_round=dict_read_number(dict, "padding-rounding",
288                                             False, "eax-serpent", loc, 16);
289     if (padding_round & (padding_round-1))
290         cfgfatal(loc,"eax-serpent","padding-round not a power of two\n");
291     if (padding_round > 255)
292         cfgfatal(loc,"eax-serpent","padding-round must be 1..128\n");
293     if (padding_round == 0)
294         padding_round = 1;
295     st->p.padding_mask = padding_round-1;
296
297     update_max_start_pad(&transform_max_start_pad, 0);
298
299     st->ops.keylen=0;
300     st->ops.create=transform_create;
301
302     return new_closure(&st->cl);
303 }
304
305 void transform_eax_module(dict_t *dict)
306 {
307     add_closure(dict,"eax-serpent",transform_apply);
308 }