Commit | Line | Data |
---|---|---|
a93aacce MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Bulk crypto transformations | |
4 | * | |
5 | * (c) 2014 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Trivial IP Encryption (TrIPE). | |
11 | * | |
12 | * TrIPE is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * TrIPE is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with TrIPE; if not, write to the Free Software Foundation, | |
24 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
25 | */ | |
26 | ||
27 | /*----- Header files ------------------------------------------------------*/ | |
28 | ||
29 | #include "tripe.h" | |
30 | ||
31 | /*----- Utilities ---------------------------------------------------------*/ | |
32 | ||
33 | #define SEQSZ 4 /* Size of sequence number packet */ | |
34 | ||
35 | #define TRACE_IV(qiv, ivsz) do { IF_TRACING(T_KEYSET, { \ | |
36 | trace_block(T_CRYPTO, "crypto: initialization vector", \ | |
37 | (qiv), (ivsz)); \ | |
38 | }) } while (0) | |
39 | ||
40 | #define TRACE_CT(qpk, sz) do { IF_TRACING(T_KEYSET, { \ | |
41 | trace_block(T_CRYPTO, "crypto: encrypted packet", (qpk), (sz)); \ | |
42 | }) } while (0) | |
43 | ||
44 | #define TRACE_MAC(qmac, tagsz) do { IF_TRACING(T_KEYSET, { \ | |
45 | trace_block(T_CRYPTO, "crypto: computed MAC", (qmac), (tagsz)); \ | |
46 | }) } while (0) | |
47 | ||
9a361a98 MW |
48 | #define TRACE_MACERR(pmac, tagsz) do { IF_TRACING(T_KEYSET, { \ |
49 | trace(T_KEYSET, "keyset: incorrect MAC: decryption failed"); \ | |
50 | trace_block(T_CRYPTO, "crypto: expected MAC", (pmac), (tagsz)); \ | |
51 | }) } while (0) | |
52 | ||
c70a7c5c MW |
53 | /*----- Common functionality for generic-composition transforms -----------*/ |
54 | ||
a93aacce MW |
55 | #define CHECK_MAC(h, pmac, tagsz) do { \ |
56 | ghash *_h = (h); \ | |
57 | const octet *_pmac = (pmac); \ | |
58 | size_t _tagsz = (tagsz); \ | |
59 | octet *_mac = GH_DONE(_h, 0); \ | |
60 | int _eq = ct_memeq(_mac, _pmac, _tagsz); \ | |
61 | TRACE_MAC(_mac, _tagsz); \ | |
62 | GH_DESTROY(_h); \ | |
63 | if (!_eq) { \ | |
9a361a98 | 64 | TRACE_MACERR(_pmac, _tagsz); \ |
a93aacce MW |
65 | return (KSERR_DECRYPT); \ |
66 | } \ | |
67 | } while (0) | |
68 | ||
c70a7c5c MW |
69 | typedef struct gencomp_algs { |
70 | const gccipher *c; size_t cksz; | |
71 | const gcmac *m; size_t mksz; size_t tagsz; | |
72 | } gencomp_algs; | |
73 | ||
74 | typedef struct gencomp_chal { | |
75 | bulkchal _b; | |
76 | gmac *m; size_t tagsz; | |
77 | } gencomp_chal; | |
78 | ||
79 | static int gencomp_getalgs(gencomp_algs *a, const algswitch *asw, | |
80 | dstr *e, key_file *kf, key *k) | |
81 | { | |
82 | const char *p; | |
83 | char *q, *qq; | |
84 | unsigned long n; | |
85 | dstr d = DSTR_INIT; | |
86 | int rc = -1; | |
87 | ||
88 | /* --- Symmetric encryption --- */ | |
89 | ||
90 | if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc"; | |
91 | if ((a->c = gcipher_byname(p)) == 0) { | |
92 | a_format(e, "unknown-cipher", "%s", p, A_END); | |
93 | goto done; | |
94 | } | |
95 | ||
96 | /* --- Message authentication --- */ | |
97 | ||
98 | if ((p = key_getattr(kf, k, "mac")) != 0) { | |
99 | dstr_reset(&d); | |
100 | dstr_puts(&d, p); | |
101 | if ((q = strchr(d.buf, '/')) != 0) | |
102 | *q++ = 0; | |
103 | if ((a->m = gmac_byname(d.buf)) == 0) { | |
104 | a_format(e, "unknown-mac", "%s", d.buf, A_END); | |
105 | goto done; | |
106 | } | |
107 | if (!q) | |
108 | a->tagsz = a->m->hashsz; | |
109 | else { | |
110 | n = strtoul(q, &qq, 0); | |
111 | if (*qq) { | |
112 | a_format(e, "bad-tag-length-string", "%s", q, A_END); | |
113 | goto done; | |
114 | } | |
115 | if (n%8 || n/8 > a->m->hashsz) { | |
116 | a_format(e, "bad-tag-length", "%lu", n, A_END); | |
117 | goto done; | |
118 | } | |
119 | a->tagsz = n/8; | |
120 | } | |
121 | } else { | |
122 | dstr_reset(&d); | |
123 | dstr_putf(&d, "%s-hmac", asw->h->name); | |
124 | if ((a->m = gmac_byname(d.buf)) == 0) { | |
125 | a_format(e, "no-hmac-for-hash", "%s", asw->h->name, A_END); | |
126 | goto done; | |
127 | } | |
128 | a->tagsz = asw->h->hashsz/2; | |
129 | } | |
130 | ||
131 | rc = 0; | |
132 | done: | |
133 | dstr_destroy(&d); | |
134 | return (rc); | |
135 | } | |
136 | ||
137 | #ifndef NTRACE | |
138 | static void gencomp_tracealgs(const gencomp_algs *a) | |
139 | { | |
140 | trace(T_CRYPTO, "crypto: cipher = %s", a->c->name); | |
141 | trace(T_CRYPTO, "crypto: mac = %s/%lu", | |
142 | a->m->name, (unsigned long)a->tagsz * 8); | |
143 | } | |
144 | #endif | |
145 | ||
146 | static int gencomp_checkalgs(gencomp_algs *a, const algswitch *asw, dstr *e) | |
147 | { | |
148 | /* --- Derive the key sizes --- * | |
149 | * | |
150 | * Must ensure that we have non-empty keys. This isn't ideal, but it | |
151 | * provides a handy sanity check. Also must be based on a 64- or 128-bit | |
152 | * block cipher or we can't do the data expiry properly. | |
153 | */ | |
154 | ||
155 | if ((a->cksz = keysz(asw->hashsz, a->c->keysz)) == 0) { | |
156 | a_format(e, "cipher", "%s", a->c->name, | |
157 | "no-key-size", "%lu", (unsigned long)asw->hashsz, | |
158 | A_END); | |
159 | return (-1); | |
160 | } | |
161 | if ((a->mksz = keysz(asw->hashsz, a->m->keysz)) == 0) { | |
162 | a_format(e, "mac", "%s", a->m->name, | |
163 | "no-key-size", "%lu", (unsigned long)asw->hashsz, | |
164 | A_END); | |
165 | return (-1); | |
166 | } | |
167 | ||
168 | return (0); | |
169 | } | |
170 | ||
171 | static void gencomp_alginfo(const gencomp_algs *a, admin *adm) | |
172 | { | |
173 | a_info(adm, | |
174 | "cipher=%s", a->c->name, | |
175 | "cipher-keysz=%lu", (unsigned long)a->cksz, | |
176 | "cipher-blksz=%lu", (unsigned long)a->c->blksz, | |
177 | A_END); | |
178 | a_info(adm, | |
179 | "mac=%s", a->m->name, | |
180 | "mac-keysz=%lu", (unsigned long)a->mksz, | |
181 | "mac-tagsz=%lu", (unsigned long)a->tagsz, | |
182 | A_END); | |
183 | } | |
184 | ||
185 | static int gencomp_samealgsp(const gencomp_algs *a, const gencomp_algs *aa) | |
186 | { | |
187 | return (a->c == aa->c && | |
188 | a->m == aa->m && a->tagsz == aa->tagsz); | |
189 | } | |
190 | ||
191 | static size_t gencomp_expsz(const gencomp_algs *a) | |
192 | { return (a->c->blksz < 16 ? MEG(64) : MEG(2048)); } | |
193 | ||
194 | static bulkchal *gencomp_genchal(const gencomp_algs *a) | |
195 | { | |
196 | gencomp_chal *gc = CREATE(gencomp_chal); | |
197 | ||
198 | rand_get(RAND_GLOBAL, buf_t, a->mksz); | |
199 | gc->m = GM_KEY(a->m, buf_t, a->mksz); | |
200 | gc->_b.tagsz = a->tagsz; | |
201 | IF_TRACING(T_CHAL, { | |
202 | trace(T_CHAL, "chal: generated new challenge key"); | |
203 | trace_block(T_CRYPTO, "chal: new key", buf_t, a->mksz); | |
204 | }) | |
205 | return (&gc->_b); | |
206 | } | |
207 | ||
208 | static int gencomp_chaltag(bulkchal *bc, const void *m, size_t msz, void *t) | |
209 | { | |
210 | gencomp_chal *gc = (gencomp_chal *)bc; | |
211 | ghash *h = GM_INIT(gc->m); | |
212 | ||
213 | GH_HASH(h, m, msz); | |
214 | memcpy(t, GH_DONE(h, 0), bc->tagsz); | |
215 | GH_DESTROY(h); | |
216 | return (0); | |
217 | } | |
218 | ||
219 | static int gencomp_chalvrf(bulkchal *bc, const void *m, size_t msz, | |
220 | const void *t) | |
221 | { | |
222 | gencomp_chal *gc = (gencomp_chal *)bc; | |
223 | ghash *h = GM_INIT(gc->m); | |
224 | int ok; | |
225 | ||
226 | GH_HASH(h, m, msz); | |
227 | ok = ct_memeq(GH_DONE(h, 0), t, gc->_b.tagsz); | |
228 | GH_DESTROY(h); | |
229 | return (ok ? 0 : -1); | |
230 | } | |
231 | ||
232 | static void gencomp_freechal(bulkchal *bc) | |
233 | { gencomp_chal *gc = (gencomp_chal *)bc; GM_DESTROY(gc->m); DESTROY(gc); } | |
234 | ||
a93aacce MW |
235 | /*----- The original transform --------------------------------------------* |
236 | * | |
237 | * We generate a random initialization vector (if the cipher needs one). We | |
238 | * encrypt the input message with the cipher, and format the type, sequence | |
239 | * number, IV, and ciphertext as follows. | |
240 | * | |
241 | * +------+ +------+---...---+------...------+ | |
242 | * | type | | seq | iv | ciphertext | | |
243 | * +------+ +------+---...---+------...------+ | |
244 | * 32 32 blksz sz | |
245 | * | |
246 | * All of this is fed into the MAC to compute a tag. The type is not | |
247 | * transmitted: the other end knows what type of message it expects, and the | |
248 | * type is only here to prevent us from being confused because some other | |
249 | * kind of ciphertext has been substituted. The tag is prepended to the | |
250 | * remainder, to yield the finished cryptogram, as follows. | |
251 | * | |
252 | * +---...---+------+---...---+------...------+ | |
253 | * | tag | seq | iv | ciphertext | | |
254 | * +---...---+------+---...---+------...------+ | |
255 | * tagsz 32 blksz sz | |
256 | * | |
257 | * Decryption: checks the overall size, verifies the tag, then decrypts the | |
258 | * ciphertext and extracts the sequence number. | |
259 | */ | |
260 | ||
c70a7c5c MW |
261 | typedef struct v0_algs { |
262 | bulkalgs _b; | |
263 | gencomp_algs ga; | |
264 | } v0_algs; | |
265 | ||
266 | typedef struct v0_ctx { | |
267 | bulkctx _b; | |
268 | size_t tagsz; | |
269 | struct { | |
270 | gcipher *c; | |
271 | gmac *m; | |
272 | } d[NDIR]; | |
273 | } v0_ctx; | |
274 | ||
275 | static bulkalgs *v0_getalgs(const algswitch *asw, dstr *e, | |
276 | key_file *kf, key *k) | |
277 | { | |
278 | v0_algs *a = CREATE(v0_algs); | |
279 | if (gencomp_getalgs(&a->ga, asw, e, kf, k)) { DESTROY(a); return (0); } | |
280 | return (&a->_b); | |
281 | } | |
282 | ||
283 | #ifndef NTRACE | |
284 | static void v0_tracealgs(const bulkalgs *aa) | |
285 | { const v0_algs *a = (const v0_algs *)aa; gencomp_tracealgs(&a->ga); } | |
286 | #endif | |
287 | ||
288 | static int v0_checkalgs(bulkalgs *aa, const algswitch *asw, dstr *e) | |
289 | { | |
290 | v0_algs *a = (v0_algs *)aa; | |
291 | if (gencomp_checkalgs(&a->ga, asw, e)) return (-1); | |
292 | return (0); | |
293 | } | |
294 | ||
295 | static int v0_samealgsp(const bulkalgs *aa, const bulkalgs *bb) | |
296 | { | |
297 | const v0_algs *a = (const v0_algs *)aa, *b = (const v0_algs *)bb; | |
298 | return (gencomp_samealgsp(&a->ga, &b->ga)); | |
299 | } | |
300 | ||
301 | static void v0_alginfo(const bulkalgs *aa, admin *adm) | |
302 | { const v0_algs *a = (const v0_algs *)aa; gencomp_alginfo(&a->ga, adm); } | |
303 | ||
304 | static size_t v0_overhead(const bulkalgs *aa) | |
305 | { | |
306 | const v0_algs *a = (const v0_algs *)aa; | |
307 | return (a->ga.tagsz + SEQSZ + a->ga.c->blksz); | |
308 | } | |
a93aacce | 309 | |
c70a7c5c MW |
310 | static size_t v0_expsz(const bulkalgs *aa) |
311 | { const v0_algs *a = (const v0_algs *)aa; return (gencomp_expsz(&a->ga)); } | |
a93aacce | 312 | |
c70a7c5c | 313 | static bulkctx *v0_genkeys(const bulkalgs *aa, const struct rawkey *rk) |
a93aacce | 314 | { |
c70a7c5c MW |
315 | const v0_algs *a = (const v0_algs *)aa; |
316 | v0_ctx *bc = CREATE(v0_ctx); | |
317 | octet k[MAXHASHSZ]; | |
318 | int i; | |
319 | ||
320 | bc->tagsz = a->ga.tagsz; | |
321 | for (i = 0; i < NDIR; i++) { | |
322 | ks_derivekey(k, a->ga.cksz, rk, i, "encryption"); | |
323 | bc->d[i].c = GC_INIT(a->ga.c, k, a->ga.cksz); | |
324 | ks_derivekey(k, a->ga.mksz, rk, i, "integrity"); | |
325 | bc->d[i].m = GM_KEY(a->ga.m, k, a->ga.mksz); | |
326 | } | |
327 | return (&bc->_b); | |
328 | } | |
329 | ||
330 | static bulkchal *v0_genchal(const bulkalgs *aa) | |
331 | { | |
332 | const v0_algs *a = (const v0_algs *)aa; | |
333 | return (gencomp_genchal(&a->ga)); | |
334 | } | |
335 | #define v0_chaltag gencomp_chaltag | |
336 | #define v0_chalvrf gencomp_chalvrf | |
337 | #define v0_freechal gencomp_freechal | |
338 | ||
339 | static void v0_freealgs(bulkalgs *aa) | |
340 | { v0_algs *a = (v0_algs *)aa; DESTROY(a); } | |
341 | ||
342 | static void v0_freectx(bulkctx *bbc) | |
343 | { | |
344 | v0_ctx *bc = (v0_ctx *)bbc; | |
345 | int i; | |
346 | ||
347 | for (i = 0; i < NDIR; i++) { | |
348 | GC_DESTROY(bc->d[i].c); | |
349 | GM_DESTROY(bc->d[i].m); | |
350 | } | |
351 | DESTROY(bc); | |
352 | } | |
353 | ||
354 | static int v0_encrypt(bulkctx *bbc, unsigned ty, | |
355 | buf *b, buf *bb, uint32 seq) | |
356 | { | |
357 | v0_ctx *bc = (v0_ctx *)bbc; | |
a93aacce | 358 | ghash *h; |
c70a7c5c | 359 | gcipher *c = bc->d[DIR_OUT].c; |
a93aacce MW |
360 | const octet *p = BCUR(b); |
361 | size_t sz = BLEFT(b); | |
362 | octet *qmac, *qseq, *qiv, *qpk; | |
a93aacce | 363 | size_t ivsz = GC_CLASS(c)->blksz; |
c70a7c5c | 364 | size_t tagsz = bc->tagsz; |
a93aacce MW |
365 | octet t[4]; |
366 | ||
367 | /* --- Determine the ciphertext layout --- */ | |
368 | ||
369 | if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz)) return (0); | |
370 | qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz; | |
371 | BSTEP(bb, tagsz + SEQSZ + ivsz + sz); | |
372 | ||
373 | /* --- Store the type --- * | |
374 | * | |
375 | * This isn't transmitted, but it's covered by the MAC. | |
376 | */ | |
377 | ||
378 | STORE32(t, ty); | |
379 | ||
380 | /* --- Store the sequence number --- */ | |
381 | ||
c70a7c5c | 382 | STORE32(qseq, seq); |
a93aacce MW |
383 | |
384 | /* --- Establish an initialization vector if necessary --- */ | |
385 | ||
386 | if (ivsz) { | |
387 | rand_get(RAND_GLOBAL, qiv, ivsz); | |
388 | GC_SETIV(c, qiv); | |
389 | TRACE_IV(qiv, ivsz); | |
390 | } | |
391 | ||
392 | /* --- Encrypt the packet --- */ | |
393 | ||
394 | GC_ENCRYPT(c, p, qpk, sz); | |
395 | TRACE_CT(qpk, sz); | |
396 | ||
397 | /* --- Compute a MAC over type, sequence number, IV, and ciphertext --- */ | |
398 | ||
399 | if (tagsz) { | |
c70a7c5c | 400 | h = GM_INIT(bc->d[DIR_OUT].m); |
a93aacce MW |
401 | GH_HASH(h, t, sizeof(t)); |
402 | GH_HASH(h, qseq, SEQSZ + ivsz + sz); | |
403 | memcpy(qmac, GH_DONE(h, 0), tagsz); | |
404 | GH_DESTROY(h); | |
405 | TRACE_MAC(qmac, tagsz); | |
406 | } | |
407 | ||
408 | /* --- We're done --- */ | |
409 | ||
410 | return (0); | |
411 | } | |
412 | ||
c70a7c5c MW |
413 | static int v0_decrypt(bulkctx *bbc, unsigned ty, |
414 | buf *b, buf *bb, uint32 *seq) | |
a93aacce | 415 | { |
c70a7c5c | 416 | v0_ctx *bc = (v0_ctx *)bbc; |
a93aacce MW |
417 | const octet *pmac, *piv, *pseq, *ppk; |
418 | size_t psz = BLEFT(b); | |
419 | size_t sz; | |
420 | octet *q = BCUR(bb); | |
421 | ghash *h; | |
c70a7c5c | 422 | gcipher *c = bc->d[DIR_IN].c; |
a93aacce | 423 | size_t ivsz = GC_CLASS(c)->blksz; |
c70a7c5c | 424 | size_t tagsz = bc->tagsz; |
a93aacce MW |
425 | octet t[4]; |
426 | ||
427 | /* --- Break up the packet into its components --- */ | |
428 | ||
429 | if (psz < ivsz + SEQSZ + tagsz) { | |
c70a7c5c | 430 | T( trace(T_KEYSET, "keyset: block too small for keyset"); ) |
a93aacce MW |
431 | return (KSERR_MALFORMED); |
432 | } | |
433 | sz = psz - ivsz - SEQSZ - tagsz; | |
434 | pmac = BCUR(b); pseq = pmac + tagsz; piv = pseq + SEQSZ; ppk = piv + ivsz; | |
435 | STORE32(t, ty); | |
436 | ||
437 | /* --- Verify the MAC on the packet --- */ | |
438 | ||
439 | if (tagsz) { | |
c70a7c5c | 440 | h = GM_INIT(bc->d[DIR_IN].m); |
a93aacce MW |
441 | GH_HASH(h, t, sizeof(t)); |
442 | GH_HASH(h, pseq, SEQSZ + ivsz + sz); | |
443 | CHECK_MAC(h, pmac, tagsz); | |
444 | } | |
445 | ||
446 | /* --- Decrypt the packet --- */ | |
447 | ||
448 | if (ivsz) { | |
449 | TRACE_IV(piv, ivsz); | |
450 | GC_SETIV(c, piv); | |
451 | } | |
452 | GC_DECRYPT(c, ppk, q, sz); | |
453 | ||
454 | /* --- Finished --- */ | |
455 | ||
456 | *seq = LOAD32(pseq); | |
457 | BSTEP(bb, sz); | |
458 | return (0); | |
459 | } | |
460 | ||
b87bffcb MW |
461 | /*----- The implicit-IV transform -----------------------------------------* |
462 | * | |
463 | * The v0 transform makes everything explicit. There's an IV because the | |
464 | * cipher needs an IV; there's a sequence number because replay prevention | |
465 | * needs a sequence number. | |
466 | * | |
467 | * This new transform works rather differently. We make use of a block | |
468 | * cipher to encrypt the sequence number, and use that as the IV. We | |
469 | * transmit the sequence number in the clear, as before. This reduces | |
470 | * overhead; and it's not a significant privacy leak because the adversary | |
471 | * can see the order in which the messages are transmitted -- i.e., the | |
472 | * sequence numbers are almost completely predictable anyway. | |
473 | * | |
474 | * So, a MAC is computed over | |
475 | * | |
476 | * +------+ +------+------...------+ | |
477 | * | type | | seq | ciphertext | | |
478 | * +------+ +------+------...------+ | |
479 | * 32 32 sz | |
480 | * | |
481 | * and we actually transmit the following as the cryptogram. | |
482 | * | |
483 | * +---...---+------+------...------+ | |
484 | * | tag | seq | ciphertext | | |
485 | * +---...---+------+------...------+ | |
486 | * tagsz 32 sz | |
487 | */ | |
488 | ||
c70a7c5c MW |
489 | typedef struct iiv_algs { |
490 | bulkalgs _b; | |
491 | gencomp_algs ga; | |
492 | const gccipher *b; size_t bksz; | |
493 | } iiv_algs; | |
494 | ||
495 | typedef struct iiv_ctx { | |
496 | bulkctx _b; | |
497 | size_t tagsz; | |
498 | struct { | |
499 | gcipher *c, *b; | |
500 | gmac *m; | |
501 | } d[NDIR]; | |
502 | } iiv_ctx; | |
503 | ||
504 | ||
505 | static bulkalgs *iiv_getalgs(const algswitch *asw, dstr *e, | |
506 | key_file *kf, key *k) | |
507 | { | |
508 | iiv_algs *a = CREATE(iiv_algs); | |
509 | dstr d = DSTR_INIT, dd = DSTR_INIT; | |
510 | const char *p; | |
511 | char *q; | |
512 | ||
513 | if (gencomp_getalgs(&a->ga, asw, e, kf, k)) goto fail; | |
514 | ||
515 | if ((p = key_getattr(kf, k, "blkc")) == 0) { | |
516 | dstr_puts(&dd, a->ga.c->name); | |
517 | if ((q = strrchr(dd.buf, '-')) != 0) *q = 0; | |
518 | p = dd.buf; | |
519 | } | |
520 | dstr_putf(&d, "%s-ecb", p); | |
521 | if ((a->b = gcipher_byname(d.buf)) == 0) { | |
522 | a_format(e, "unknown-blkc", "%s", p, A_END); | |
523 | goto fail; | |
524 | } | |
525 | ||
526 | dstr_destroy(&d); dstr_destroy(&dd); | |
527 | return (&a->_b); | |
528 | fail: | |
529 | dstr_destroy(&d); dstr_destroy(&dd); | |
530 | DESTROY(a); | |
531 | return (0); | |
532 | } | |
533 | ||
534 | #ifndef NTRACE | |
535 | static void iiv_tracealgs(const bulkalgs *aa) | |
536 | { | |
537 | const iiv_algs *a = (const iiv_algs *)aa; | |
538 | ||
539 | gencomp_tracealgs(&a->ga); | |
540 | trace(T_CRYPTO, "crypto: blkc = %.*s", strlen(a->b->name) - 4, a->b->name); | |
541 | } | |
542 | #endif | |
543 | ||
544 | static int iiv_checkalgs(bulkalgs *aa, const algswitch *asw, dstr *e) | |
b87bffcb | 545 | { |
c70a7c5c MW |
546 | iiv_algs *a = (iiv_algs *)aa; |
547 | ||
548 | if (gencomp_checkalgs(&a->ga, asw, e)) return (-1); | |
549 | ||
550 | if ((a->bksz = keysz(asw->hashsz, a->b->keysz)) == 0) { | |
551 | a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name, | |
552 | "no-key-size", "%lu", (unsigned long)asw->hashsz, | |
553 | A_END); | |
554 | return (-1); | |
555 | } | |
556 | if (a->b->blksz < a->ga.c->blksz) { | |
b87bffcb MW |
557 | a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name, |
558 | "blksz-insufficient", A_END); | |
559 | return (-1); | |
560 | } | |
561 | return (0); | |
562 | } | |
563 | ||
c70a7c5c MW |
564 | static int iiv_samealgsp(const bulkalgs *aa, const bulkalgs *bb) |
565 | { | |
566 | const iiv_algs *a = (const iiv_algs *)aa, *b = (const iiv_algs *)bb; | |
567 | return (gencomp_samealgsp(&a->ga, &b->ga) && a->b == b->b); | |
568 | } | |
569 | ||
570 | static void iiv_alginfo(const bulkalgs *aa, admin *adm) | |
571 | { | |
572 | const iiv_algs *a = (const iiv_algs *)aa; | |
573 | gencomp_alginfo(&a->ga, adm); | |
574 | a_info(adm, | |
575 | "blkc=%.*s", strlen(a->b->name) - 4, a->b->name, | |
576 | "blkc-keysz=%lu", (unsigned long)a->bksz, | |
577 | "blkc-blksz=%lu", (unsigned long)a->b->blksz, | |
578 | A_END); | |
579 | } | |
580 | ||
581 | static size_t iiv_overhead(const bulkalgs *aa) | |
582 | { const iiv_algs *a = (const iiv_algs *)aa; return (a->ga.tagsz + SEQSZ); } | |
583 | ||
584 | static size_t iiv_expsz(const bulkalgs *aa) | |
585 | { | |
586 | const iiv_algs *a = (const iiv_algs *)aa; | |
587 | return (gencomp_expsz(&a->ga)); | |
588 | } | |
589 | ||
590 | static bulkctx *iiv_genkeys(const bulkalgs *aa, const struct rawkey *rk) | |
591 | { | |
592 | const iiv_algs *a = (const iiv_algs *)aa; | |
593 | iiv_ctx *bc = CREATE(iiv_ctx); | |
594 | octet k[MAXHASHSZ]; | |
595 | int i; | |
596 | ||
597 | bc->tagsz = a->ga.tagsz; | |
598 | for (i = 0; i < NDIR; i++) { | |
599 | ks_derivekey(k, a->ga.cksz, rk, i, "encryption"); | |
600 | bc->d[i].c = GC_INIT(a->ga.c, k, a->ga.cksz); | |
601 | ks_derivekey(k, a->bksz, rk, i, "blkc"); | |
602 | bc->d[i].b = GC_INIT(a->b, k, a->bksz); | |
603 | ks_derivekey(k, a->ga.mksz, rk, i, "integrity"); | |
604 | bc->d[i].m = GM_KEY(a->ga.m, k, a->ga.mksz); | |
605 | } | |
606 | return (&bc->_b); | |
607 | } | |
608 | ||
609 | static bulkchal *iiv_genchal(const bulkalgs *aa) | |
610 | { | |
611 | const iiv_algs *a = (const iiv_algs *)aa; | |
612 | return (gencomp_genchal(&a->ga)); | |
613 | } | |
614 | #define iiv_chaltag gencomp_chaltag | |
615 | #define iiv_chalvrf gencomp_chalvrf | |
616 | #define iiv_freechal gencomp_freechal | |
617 | ||
618 | static void iiv_freealgs(bulkalgs *aa) | |
619 | { iiv_algs *a = (iiv_algs *)aa; DESTROY(a); } | |
620 | ||
621 | static void iiv_freectx(bulkctx *bbc) | |
622 | { | |
623 | iiv_ctx *bc = (iiv_ctx *)bbc; | |
624 | int i; | |
625 | ||
626 | for (i = 0; i < NDIR; i++) { | |
627 | GC_DESTROY(bc->d[i].c); | |
628 | GC_DESTROY(bc->d[i].b); | |
629 | GM_DESTROY(bc->d[i].m); | |
630 | } | |
631 | DESTROY(bc); | |
632 | } | |
b87bffcb MW |
633 | |
634 | #define TRACE_PRESEQ(qseq, ivsz) do { IF_TRACING(T_KEYSET, { \ | |
635 | trace_block(T_CRYPTO, "crypto: IV derivation input", (qseq), (ivsz)); \ | |
636 | }) } while (0) | |
637 | ||
c70a7c5c MW |
638 | static int iiv_encrypt(bulkctx *bbc, unsigned ty, |
639 | buf *b, buf *bb, uint32 seq) | |
b87bffcb | 640 | { |
c70a7c5c | 641 | iiv_ctx *bc = (iiv_ctx *)bbc; |
b87bffcb | 642 | ghash *h; |
c70a7c5c | 643 | gcipher *c = bc->d[DIR_OUT].c, *blkc = bc->d[DIR_OUT].b; |
b87bffcb MW |
644 | const octet *p = BCUR(b); |
645 | size_t sz = BLEFT(b); | |
646 | octet *qmac, *qseq, *qpk; | |
b87bffcb | 647 | size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz; |
c70a7c5c | 648 | size_t tagsz = bc->tagsz; |
b87bffcb MW |
649 | octet t[4]; |
650 | ||
651 | /* --- Determine the ciphertext layout --- */ | |
652 | ||
653 | if (buf_ensure(bb, tagsz + SEQSZ + sz)) return (0); | |
654 | qmac = BCUR(bb); qseq = qmac + tagsz; qpk = qseq + SEQSZ; | |
655 | BSTEP(bb, tagsz + SEQSZ + sz); | |
656 | ||
657 | /* --- Store the type --- * | |
658 | * | |
659 | * This isn't transmitted, but it's covered by the MAC. | |
660 | */ | |
661 | ||
662 | STORE32(t, ty); | |
663 | ||
664 | /* --- Store the sequence number --- */ | |
665 | ||
c70a7c5c | 666 | STORE32(qseq, seq); |
b87bffcb MW |
667 | |
668 | /* --- Establish an initialization vector if necessary --- */ | |
669 | ||
670 | if (ivsz) { | |
671 | memset(buf_u, 0, blkcsz - SEQSZ); | |
672 | memcpy(buf_u + blkcsz - SEQSZ, qseq, SEQSZ); | |
673 | TRACE_PRESEQ(buf_u, ivsz); | |
674 | GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz); | |
675 | GC_SETIV(c, buf_u); | |
676 | TRACE_IV(buf_u, ivsz); | |
677 | } | |
678 | ||
679 | /* --- Encrypt the packet --- */ | |
680 | ||
681 | GC_ENCRYPT(c, p, qpk, sz); | |
682 | TRACE_CT(qpk, sz); | |
683 | ||
684 | /* --- Compute a MAC over type, sequence number, and ciphertext --- */ | |
685 | ||
686 | if (tagsz) { | |
c70a7c5c | 687 | h = GM_INIT(bc->d[DIR_OUT].m); |
b87bffcb MW |
688 | GH_HASH(h, t, sizeof(t)); |
689 | GH_HASH(h, qseq, SEQSZ + sz); | |
690 | memcpy(qmac, GH_DONE(h, 0), tagsz); | |
691 | GH_DESTROY(h); | |
692 | TRACE_MAC(qmac, tagsz); | |
693 | } | |
694 | ||
695 | /* --- We're done --- */ | |
696 | ||
697 | return (0); | |
698 | } | |
699 | ||
c70a7c5c MW |
700 | static int iiv_decrypt(bulkctx *bbc, unsigned ty, |
701 | buf *b, buf *bb, uint32 *seq) | |
b87bffcb | 702 | { |
c70a7c5c | 703 | iiv_ctx *bc = (iiv_ctx *)bbc; |
b87bffcb MW |
704 | const octet *pmac, *pseq, *ppk; |
705 | size_t psz = BLEFT(b); | |
706 | size_t sz; | |
707 | octet *q = BCUR(bb); | |
708 | ghash *h; | |
c70a7c5c | 709 | gcipher *c = bc->d[DIR_IN].c, *blkc = bc->d[DIR_IN].b; |
b87bffcb | 710 | size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz; |
c70a7c5c | 711 | size_t tagsz = bc->tagsz; |
b87bffcb MW |
712 | octet t[4]; |
713 | ||
714 | /* --- Break up the packet into its components --- */ | |
715 | ||
716 | if (psz < SEQSZ + tagsz) { | |
c70a7c5c | 717 | T( trace(T_KEYSET, "keyset: block too small for keyset"); ) |
b87bffcb MW |
718 | return (KSERR_MALFORMED); |
719 | } | |
720 | sz = psz - SEQSZ - tagsz; | |
721 | pmac = BCUR(b); pseq = pmac + tagsz; ppk = pseq + SEQSZ; | |
722 | STORE32(t, ty); | |
723 | ||
724 | /* --- Verify the MAC on the packet --- */ | |
725 | ||
726 | if (tagsz) { | |
c70a7c5c | 727 | h = GM_INIT(bc->d[DIR_IN].m); |
b87bffcb MW |
728 | GH_HASH(h, t, sizeof(t)); |
729 | GH_HASH(h, pseq, SEQSZ + sz); | |
730 | CHECK_MAC(h, pmac, tagsz); | |
731 | } | |
732 | ||
733 | /* --- Decrypt the packet --- */ | |
734 | ||
735 | if (ivsz) { | |
736 | memset(buf_u, 0, blkcsz - SEQSZ); | |
737 | memcpy(buf_u + blkcsz - SEQSZ, pseq, SEQSZ); | |
738 | TRACE_PRESEQ(buf_u, ivsz); | |
739 | GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz); | |
740 | GC_SETIV(c, buf_u); | |
741 | TRACE_IV(buf_u, ivsz); | |
742 | } | |
743 | GC_DECRYPT(c, ppk, q, sz); | |
744 | ||
745 | /* --- Finished --- */ | |
746 | ||
747 | *seq = LOAD32(pseq); | |
748 | BSTEP(bb, sz); | |
749 | return (0); | |
750 | } | |
751 | ||
a93aacce MW |
752 | /*----- Bulk crypto transform table ---------------------------------------*/ |
753 | ||
fddd7fb7 | 754 | const bulkops bulktab[] = { |
a93aacce | 755 | |
c70a7c5c MW |
756 | #define COMMA , |
757 | ||
758 | #define BULK(name, pre) \ | |
759 | { name, pre##_getalgs, T( pre##_tracealgs COMMA ) \ | |
760 | pre##_checkalgs, pre##_samealgsp, \ | |
761 | pre##_alginfo, pre##_overhead, pre##_expsz, \ | |
762 | pre##_genkeys, pre##_genchal, pre##_freealgs, \ | |
763 | pre##_encrypt, pre##_decrypt, pre##_freectx, \ | |
764 | pre##_chaltag, pre##_chalvrf, pre##_freechal } | |
a93aacce | 765 | |
c70a7c5c MW |
766 | BULK("v0", v0), |
767 | BULK("iiv", iiv), | |
a93aacce MW |
768 | |
769 | #undef BULK | |
770 | { 0 } | |
771 | }; | |
772 | ||
773 | /*----- That's all, folks -------------------------------------------------*/ |