| 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 | |
| 48 | #define CHECK_MAC(h, pmac, tagsz) do { \ |
| 49 | ghash *_h = (h); \ |
| 50 | const octet *_pmac = (pmac); \ |
| 51 | size_t _tagsz = (tagsz); \ |
| 52 | octet *_mac = GH_DONE(_h, 0); \ |
| 53 | int _eq = ct_memeq(_mac, _pmac, _tagsz); \ |
| 54 | TRACE_MAC(_mac, _tagsz); \ |
| 55 | GH_DESTROY(_h); \ |
| 56 | if (!_eq) { \ |
| 57 | IF_TRACING(T_KEYSET, { \ |
| 58 | trace(T_KEYSET, "keyset: incorrect MAC: decryption failed"); \ |
| 59 | trace_block(T_CRYPTO, "crypto: expected MAC", _pmac, _tagsz); \ |
| 60 | }) \ |
| 61 | return (KSERR_DECRYPT); \ |
| 62 | } \ |
| 63 | } while (0) |
| 64 | |
| 65 | /*----- The original transform --------------------------------------------* |
| 66 | * |
| 67 | * We generate a random initialization vector (if the cipher needs one). We |
| 68 | * encrypt the input message with the cipher, and format the type, sequence |
| 69 | * number, IV, and ciphertext as follows. |
| 70 | * |
| 71 | * +------+ +------+---...---+------...------+ |
| 72 | * | type | | seq | iv | ciphertext | |
| 73 | * +------+ +------+---...---+------...------+ |
| 74 | * 32 32 blksz sz |
| 75 | * |
| 76 | * All of this is fed into the MAC to compute a tag. The type is not |
| 77 | * transmitted: the other end knows what type of message it expects, and the |
| 78 | * type is only here to prevent us from being confused because some other |
| 79 | * kind of ciphertext has been substituted. The tag is prepended to the |
| 80 | * remainder, to yield the finished cryptogram, as follows. |
| 81 | * |
| 82 | * +---...---+------+---...---+------...------+ |
| 83 | * | tag | seq | iv | ciphertext | |
| 84 | * +---...---+------+---...---+------...------+ |
| 85 | * tagsz 32 blksz sz |
| 86 | * |
| 87 | * Decryption: checks the overall size, verifies the tag, then decrypts the |
| 88 | * ciphertext and extracts the sequence number. |
| 89 | */ |
| 90 | |
| 91 | static int v0_check(const algswitch *a, dstr *e) |
| 92 | { return (0); } |
| 93 | |
| 94 | static size_t v0_overhead(const algswitch *a) |
| 95 | { return a->tagsz + SEQSZ + a->c->blksz; } |
| 96 | |
| 97 | static int v0_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb) |
| 98 | { |
| 99 | ghash *h; |
| 100 | gcipher *c = ks->out.c; |
| 101 | const octet *p = BCUR(b); |
| 102 | size_t sz = BLEFT(b); |
| 103 | octet *qmac, *qseq, *qiv, *qpk; |
| 104 | uint32 oseq; |
| 105 | size_t ivsz = GC_CLASS(c)->blksz; |
| 106 | size_t tagsz = ks->tagsz; |
| 107 | octet t[4]; |
| 108 | |
| 109 | /* --- Determine the ciphertext layout --- */ |
| 110 | |
| 111 | if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz)) return (0); |
| 112 | qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz; |
| 113 | BSTEP(bb, tagsz + SEQSZ + ivsz + sz); |
| 114 | |
| 115 | /* --- Store the type --- * |
| 116 | * |
| 117 | * This isn't transmitted, but it's covered by the MAC. |
| 118 | */ |
| 119 | |
| 120 | STORE32(t, ty); |
| 121 | |
| 122 | /* --- Store the sequence number --- */ |
| 123 | |
| 124 | oseq = ks->oseq++; |
| 125 | STORE32(qseq, oseq); |
| 126 | |
| 127 | /* --- Establish an initialization vector if necessary --- */ |
| 128 | |
| 129 | if (ivsz) { |
| 130 | rand_get(RAND_GLOBAL, qiv, ivsz); |
| 131 | GC_SETIV(c, qiv); |
| 132 | TRACE_IV(qiv, ivsz); |
| 133 | } |
| 134 | |
| 135 | /* --- Encrypt the packet --- */ |
| 136 | |
| 137 | GC_ENCRYPT(c, p, qpk, sz); |
| 138 | TRACE_CT(qpk, sz); |
| 139 | |
| 140 | /* --- Compute a MAC over type, sequence number, IV, and ciphertext --- */ |
| 141 | |
| 142 | if (tagsz) { |
| 143 | h = GM_INIT(ks->out.m); |
| 144 | GH_HASH(h, t, sizeof(t)); |
| 145 | GH_HASH(h, qseq, SEQSZ + ivsz + sz); |
| 146 | memcpy(qmac, GH_DONE(h, 0), tagsz); |
| 147 | GH_DESTROY(h); |
| 148 | TRACE_MAC(qmac, tagsz); |
| 149 | } |
| 150 | |
| 151 | /* --- We're done --- */ |
| 152 | |
| 153 | return (0); |
| 154 | } |
| 155 | |
| 156 | static int v0_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq) |
| 157 | { |
| 158 | const octet *pmac, *piv, *pseq, *ppk; |
| 159 | size_t psz = BLEFT(b); |
| 160 | size_t sz; |
| 161 | octet *q = BCUR(bb); |
| 162 | ghash *h; |
| 163 | gcipher *c = ks->in.c; |
| 164 | size_t ivsz = GC_CLASS(c)->blksz; |
| 165 | size_t tagsz = ks->tagsz; |
| 166 | octet t[4]; |
| 167 | |
| 168 | /* --- Break up the packet into its components --- */ |
| 169 | |
| 170 | if (psz < ivsz + SEQSZ + tagsz) { |
| 171 | T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); ) |
| 172 | return (KSERR_MALFORMED); |
| 173 | } |
| 174 | sz = psz - ivsz - SEQSZ - tagsz; |
| 175 | pmac = BCUR(b); pseq = pmac + tagsz; piv = pseq + SEQSZ; ppk = piv + ivsz; |
| 176 | STORE32(t, ty); |
| 177 | |
| 178 | /* --- Verify the MAC on the packet --- */ |
| 179 | |
| 180 | if (tagsz) { |
| 181 | h = GM_INIT(ks->in.m); |
| 182 | GH_HASH(h, t, sizeof(t)); |
| 183 | GH_HASH(h, pseq, SEQSZ + ivsz + sz); |
| 184 | CHECK_MAC(h, pmac, tagsz); |
| 185 | } |
| 186 | |
| 187 | /* --- Decrypt the packet --- */ |
| 188 | |
| 189 | if (ivsz) { |
| 190 | TRACE_IV(piv, ivsz); |
| 191 | GC_SETIV(c, piv); |
| 192 | } |
| 193 | GC_DECRYPT(c, ppk, q, sz); |
| 194 | |
| 195 | /* --- Finished --- */ |
| 196 | |
| 197 | *seq = LOAD32(pseq); |
| 198 | BSTEP(bb, sz); |
| 199 | return (0); |
| 200 | } |
| 201 | |
| 202 | /*----- The implicit-IV transform -----------------------------------------* |
| 203 | * |
| 204 | * The v0 transform makes everything explicit. There's an IV because the |
| 205 | * cipher needs an IV; there's a sequence number because replay prevention |
| 206 | * needs a sequence number. |
| 207 | * |
| 208 | * This new transform works rather differently. We make use of a block |
| 209 | * cipher to encrypt the sequence number, and use that as the IV. We |
| 210 | * transmit the sequence number in the clear, as before. This reduces |
| 211 | * overhead; and it's not a significant privacy leak because the adversary |
| 212 | * can see the order in which the messages are transmitted -- i.e., the |
| 213 | * sequence numbers are almost completely predictable anyway. |
| 214 | * |
| 215 | * So, a MAC is computed over |
| 216 | * |
| 217 | * +------+ +------+------...------+ |
| 218 | * | type | | seq | ciphertext | |
| 219 | * +------+ +------+------...------+ |
| 220 | * 32 32 sz |
| 221 | * |
| 222 | * and we actually transmit the following as the cryptogram. |
| 223 | * |
| 224 | * +---...---+------+------...------+ |
| 225 | * | tag | seq | ciphertext | |
| 226 | * +---...---+------+------...------+ |
| 227 | * tagsz 32 sz |
| 228 | */ |
| 229 | |
| 230 | static int iiv_check(const algswitch *a, dstr *e) |
| 231 | { |
| 232 | if (a->b->blksz < a->c->blksz) { |
| 233 | a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name, |
| 234 | "blksz-insufficient", A_END); |
| 235 | return (-1); |
| 236 | } |
| 237 | return (0); |
| 238 | } |
| 239 | |
| 240 | static size_t iiv_overhead(const algswitch *a) |
| 241 | { return a->tagsz + SEQSZ; } |
| 242 | |
| 243 | #define TRACE_PRESEQ(qseq, ivsz) do { IF_TRACING(T_KEYSET, { \ |
| 244 | trace_block(T_CRYPTO, "crypto: IV derivation input", (qseq), (ivsz)); \ |
| 245 | }) } while (0) |
| 246 | |
| 247 | static int iiv_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb) |
| 248 | { |
| 249 | ghash *h; |
| 250 | gcipher *c = ks->out.c, *blkc = ks->out.b; |
| 251 | const octet *p = BCUR(b); |
| 252 | size_t sz = BLEFT(b); |
| 253 | octet *qmac, *qseq, *qpk; |
| 254 | uint32 oseq; |
| 255 | size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz; |
| 256 | size_t tagsz = ks->tagsz; |
| 257 | octet t[4]; |
| 258 | |
| 259 | /* --- Determine the ciphertext layout --- */ |
| 260 | |
| 261 | if (buf_ensure(bb, tagsz + SEQSZ + sz)) return (0); |
| 262 | qmac = BCUR(bb); qseq = qmac + tagsz; qpk = qseq + SEQSZ; |
| 263 | BSTEP(bb, tagsz + SEQSZ + sz); |
| 264 | |
| 265 | /* --- Store the type --- * |
| 266 | * |
| 267 | * This isn't transmitted, but it's covered by the MAC. |
| 268 | */ |
| 269 | |
| 270 | STORE32(t, ty); |
| 271 | |
| 272 | /* --- Store the sequence number --- */ |
| 273 | |
| 274 | oseq = ks->oseq++; |
| 275 | STORE32(qseq, oseq); |
| 276 | |
| 277 | /* --- Establish an initialization vector if necessary --- */ |
| 278 | |
| 279 | if (ivsz) { |
| 280 | memset(buf_u, 0, blkcsz - SEQSZ); |
| 281 | memcpy(buf_u + blkcsz - SEQSZ, qseq, SEQSZ); |
| 282 | TRACE_PRESEQ(buf_u, ivsz); |
| 283 | GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz); |
| 284 | GC_SETIV(c, buf_u); |
| 285 | TRACE_IV(buf_u, ivsz); |
| 286 | } |
| 287 | |
| 288 | /* --- Encrypt the packet --- */ |
| 289 | |
| 290 | GC_ENCRYPT(c, p, qpk, sz); |
| 291 | TRACE_CT(qpk, sz); |
| 292 | |
| 293 | /* --- Compute a MAC over type, sequence number, and ciphertext --- */ |
| 294 | |
| 295 | if (tagsz) { |
| 296 | h = GM_INIT(ks->out.m); |
| 297 | GH_HASH(h, t, sizeof(t)); |
| 298 | GH_HASH(h, qseq, SEQSZ + sz); |
| 299 | memcpy(qmac, GH_DONE(h, 0), tagsz); |
| 300 | GH_DESTROY(h); |
| 301 | TRACE_MAC(qmac, tagsz); |
| 302 | } |
| 303 | |
| 304 | /* --- We're done --- */ |
| 305 | |
| 306 | return (0); |
| 307 | } |
| 308 | |
| 309 | static int iiv_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq) |
| 310 | { |
| 311 | const octet *pmac, *pseq, *ppk; |
| 312 | size_t psz = BLEFT(b); |
| 313 | size_t sz; |
| 314 | octet *q = BCUR(bb); |
| 315 | ghash *h; |
| 316 | gcipher *c = ks->in.c, *blkc = ks->in.b; |
| 317 | size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz; |
| 318 | size_t tagsz = ks->tagsz; |
| 319 | octet t[4]; |
| 320 | |
| 321 | /* --- Break up the packet into its components --- */ |
| 322 | |
| 323 | if (psz < SEQSZ + tagsz) { |
| 324 | T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); ) |
| 325 | return (KSERR_MALFORMED); |
| 326 | } |
| 327 | sz = psz - SEQSZ - tagsz; |
| 328 | pmac = BCUR(b); pseq = pmac + tagsz; ppk = pseq + SEQSZ; |
| 329 | STORE32(t, ty); |
| 330 | |
| 331 | /* --- Verify the MAC on the packet --- */ |
| 332 | |
| 333 | if (tagsz) { |
| 334 | h = GM_INIT(ks->in.m); |
| 335 | GH_HASH(h, t, sizeof(t)); |
| 336 | GH_HASH(h, pseq, SEQSZ + sz); |
| 337 | CHECK_MAC(h, pmac, tagsz); |
| 338 | } |
| 339 | |
| 340 | /* --- Decrypt the packet --- */ |
| 341 | |
| 342 | if (ivsz) { |
| 343 | memset(buf_u, 0, blkcsz - SEQSZ); |
| 344 | memcpy(buf_u + blkcsz - SEQSZ, pseq, SEQSZ); |
| 345 | TRACE_PRESEQ(buf_u, ivsz); |
| 346 | GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz); |
| 347 | GC_SETIV(c, buf_u); |
| 348 | TRACE_IV(buf_u, ivsz); |
| 349 | } |
| 350 | GC_DECRYPT(c, ppk, q, sz); |
| 351 | |
| 352 | /* --- Finished --- */ |
| 353 | |
| 354 | *seq = LOAD32(pseq); |
| 355 | BSTEP(bb, sz); |
| 356 | return (0); |
| 357 | } |
| 358 | |
| 359 | /*----- Bulk crypto transform table ---------------------------------------*/ |
| 360 | |
| 361 | const bulkcrypto bulktab[] = { |
| 362 | |
| 363 | #define BULK(name, pre, prim) \ |
| 364 | { name, prim, pre##_check, pre##_overhead, pre##_encrypt, pre##_decrypt } |
| 365 | |
| 366 | BULK("v0", v0, BCP_CIPHER | BCP_MAC), |
| 367 | BULK("iiv", iiv, BCP_CIPHER | BCP_MAC | BCP_BLKC), |
| 368 | |
| 369 | #undef BULK |
| 370 | { 0 } |
| 371 | }; |
| 372 | |
| 373 | /*----- That's all, folks -------------------------------------------------*/ |