Merged blowfish branch into trunk.
[become] / src / crypt.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
9e5602f0 3 * $Id: crypt.c,v 1.3 1997/09/26 09:14:58 mdw Exp $
c4f2d992 4 *
5 * Cryptographic transfer of `become' requests
6 *
7 * (c) 1997 EBI
8 */
9
03f996bd 10/*----- Licensing notice --------------------------------------------------*
c4f2d992 11 *
12 * This file is part of `become'
13 *
14 * `Become' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `Become' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
03f996bd 25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
c4f2d992 27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: crypt.c,v $
9e5602f0 32 * Revision 1.3 1997/09/26 09:14:58 mdw
33 * Merged blowfish branch into trunk.
34 *
35 * Revision 1.2.2.1 1997/09/26 09:08:02 mdw
36 * Use the Blowfish encryption algorithm instead of IDEA. This is partly
37 * because I prefer Blowfish (without any particularly strong evidence) but
38 * mainly because IDEA is patented and Blowfish isn't.
39 *
03f996bd 40 * Revision 1.2 1997/08/04 10:24:21 mdw
41 * Sources placed under CVS control.
42 *
43 * Revision 1.1 1997/07/21 13:47:51 mdw
c4f2d992 44 * Initial revision
45 *
46 */
47
48/*----- Header files ------------------------------------------------------*/
49
50/* --- ANSI headers --- */
51
52#include <ctype.h>
53#include <errno.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <time.h>
58
59/* --- Unix headers --- */
60
61#include <sys/types.h>
03f996bd 62#include <sys/time.h>
c4f2d992 63#include <unistd.h>
64#include <syslog.h>
65#include <fcntl.h>
66
67/* --- Local headers --- */
68
03f996bd 69#include "become.h"
9e5602f0 70#include "blowfish.h"
c4f2d992 71#include "config.h"
72#include "crypt.h"
73#include "icrypt.h"
c4f2d992 74#include "md5.h"
03f996bd 75#include "noise.h"
76#include "rand.h"
c4f2d992 77#include "tx.h"
78#include "utils.h"
79
80/*----- Magic numbers -----------------------------------------------------*/
81
03f996bd 82#define crypt__timeError 60 /* Seconds error to permit */
c4f2d992 83
84/*----- Main code ---------------------------------------------------------*/
85
86/* --- @crypt__sessionKey@ --- *
87 *
88 * Arguments: @const char *seedfile@ = pointer to name of seed file
89 * @unsigned char *k@ = our secret key
c4f2d992 90 * @unsigned *sk@ = where to store the session key
91 * @unsigned char *iv@ = where to store the IV
92 *
93 * Returns: ---
94 *
95 * Use: Decides on a random session key and initialisation vector.
96 */
97
98static void crypt__sessionKey(const char *seedfile, unsigned char *k,
c4f2d992 99 unsigned char *sk, unsigned char *iv)
100{
101 FILE *fp; /* File handle for reading */
03f996bd 102 struct flock l;
103 int ok = 1;
c4f2d992 104
03f996bd 105 /* --- Open the random seed file --- *
c4f2d992 106 *
03f996bd 107 * If I can't manage that, create a new one.
c4f2d992 108 */
109
03f996bd 110 if ((fp = fopen(seedfile, "r+")) == 0) {
111 ok = 0;
112 if ((fp = fopen(seedfile, "w+")) == 0)
113 die("can't create random number file: %s", strerror(errno));
114 rand_clear();
c4f2d992 115 }
116
03f996bd 117 /* --- Lock the seed file against concurrency problems --- */
c4f2d992 118
03f996bd 119 l.l_type = F_WRLCK;
120 l.l_whence = SEEK_SET;
121 l.l_start = 0;
122 l.l_len = 0;
123 if (fcntl(fileno(fp), F_SETLKW, &l) < 0)
124 die("can't lock random number file: %s", strerror(errno));
c4f2d992 125
03f996bd 126 /* --- Now read the file, and launder the seed --- */
c4f2d992 127
03f996bd 128 if (ok)
129 rand_read(fp);
c4f2d992 130
03f996bd 131 /* --- Encrypt the pool using the secret key --- */
c4f2d992 132
133 {
03f996bd 134 icrypt_job j;
9e5602f0 135 icrypt_init(&j, k, BLOWFISH_KEYSIZE, 0);
03f996bd 136 rand_encrypt(&j);
137 burn(j);
c4f2d992 138 }
139
03f996bd 140 /* --- Generate the session key and IV --- */
c4f2d992 141
03f996bd 142 noise_acquire();
9e5602f0 143 rand_extract(sk, BLOWFISH_KEYSIZE);
144 rand_extract(iv, BLOWFISH_BLKSIZE);
03f996bd 145
146 IF_TRACING(TRACE_CRYPTO,
9e5602f0 147 traceblk(TRACE_CRYPTO, "crypto: session key:", sk, BLOWFISH_KEYSIZE);
03f996bd 148 traceblk(TRACE_CRYPTO, "crypto: initialisation vector:",
9e5602f0 149 iv, BLOWFISH_BLKSIZE);
03f996bd 150 );
c4f2d992 151
03f996bd 152 /* --- Write the seed back --- */
c4f2d992 153
154 rewind(fp);
03f996bd 155 rand_write(fp);
c4f2d992 156 fclose(fp);
157
c4f2d992 158}
159
160/* --- @crypt_packRequest@ --- *
161 *
162 * Arguments: @request *rq@ = pointer to request block
163 * @unsigned char *buff@ = pointer to a buffer
164 * @time_t t@ = the current time
165 * @pid_t pid@ = my process ID
166 * @unsigned char *k@ = pointer to 128-bit key
167 * @unsigned char *sk@ = where to put the session key
168 *
169 * Returns: ---
170 *
171 * Use: Packs a request block into a buffer. The buffer should have
172 * space for at least @crq_size@ bytes. The buffer comes back
173 * encrypted and ready to send.
174 */
175
176void crypt_packRequest(request *rq, unsigned char *buff,
177 time_t t, pid_t pid,
178 unsigned char *k, unsigned char *sk)
179{
180 /* --- First, build the easy stuff in the block --- */
181
9e5602f0 182 buff[crq_cryptType] = cryptType_blowfish;
c4f2d992 183 store32(buff + crq_time, t);
184 store32(buff + crq_pid, pid);
185 store32(buff + crq_from, rq->from);
186 store32(buff + crq_to, rq->to);
187
03f996bd 188 /* --- Now generate session keys and things --- */
189
190 crypt__sessionKey(file_RANDSEED, k, sk, buff + crq_iv);
9e5602f0 191 memcpy(buff + crq_session, sk, BLOWFISH_KEYSIZE);
03f996bd 192
c4f2d992 193 /* --- The string causes a few problems --- *
194 *
195 * There's a good chance that the string will be a good deal shorter than
196 * the space allowed for it. This will probably mean lots of zeroes, and a
197 * very easy known-plaintext job for a potential attacker. (An early
198 * version of this code used @strncpy@ which is even worse!)
199 *
200 * I'll fill the block with random (from @rand@(3) -- nothing too
9e5602f0 201 * elaborate) and then encrypt it using Blowfish in CFB mode, using the
202 * first few bytes as the key. This should provide a sufficiently
203 * unpredictable background for the block.
c4f2d992 204 */
205
206 {
207 icrypt_job j;
208 unsigned char *p;
209 unsigned u;
03f996bd 210 md5 md;
9e5602f0 211 unsigned char qk[BLOWFISH_KEYSIZE];
c4f2d992 212
213 /* --- Initialise the buffer with junk --- */
214
215 srand((unsigned int)(t ^ pid)); /* Seed the (bad) RNG */
216 for (p = buff + crq_cmd; p < buff + crq_cmd + CMDLEN_MAX; p++) {
03f996bd 217 u = rand(); *p = u ^ (u >> 8);
c4f2d992 218 }
219
220 /* --- Now make the junk a whole lot harder to predict --- */
221
222 p = buff + crq_cmd;
03f996bd 223 md5_init(&md); md5_buffer(&md, p, CMDLEN_MAX); md5_final(&md, qk);
9e5602f0 224 icrypt_init(&j, qk, BLOWFISH_KEYSIZE, 0);
225 icrypt_encrypt(&j, p, p, CMDLEN_MAX);
03f996bd 226 burn(j); burn(qk); burn(md);
c4f2d992 227
228 /* --- Copy the string into here --- */
229
230 strcpy((char *)buff + crq_cmd, rq->cmd);
231 }
232
c4f2d992 233 /* --- Checksum the finished data --- */
234
235 {
236 md5 md;
03f996bd 237 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 238
239 md5_init(&md);
240 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
03f996bd 241 md5_final(&md, mdbuf);
242 memcpy(buff + crq_check, mdbuf, 4);
243 burn(md); burn(mdbuf);
c4f2d992 244 }
245
246 /* --- Encrypt the block --- *
247 *
248 * First, encrypt the session key using the master key. Since the session
249 * key is effectively random, this makes cracking the master key much
250 * harder. The rest of the block is then encrypted with the session key,
251 * using the IV left over from encrypting the session key.
252 */
253
254 {
255 icrypt_job j;
256
03f996bd 257 T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
258 buff, crq_size); )
c4f2d992 259
9e5602f0 260 T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
261 T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
262 buff + crq_iv, BLOWFISH_BLKSIZE); )
263 T( traceblk(TRACE_CRYPTO, "crypto: session key:",
264 sk, BLOWFISH_KEYSIZE); )
265
266 icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
267
268 icrypt_encrypt(&j, buff + crq_session,
269 buff + crq_session, BLOWFISH_KEYSIZE);
270 T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
271 buff + crq_session, BLOWFISH_KEYSIZE); )
272
273 icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
274
275 T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
276 j.iv, BLOWFISH_BLKSIZE); )
277
c4f2d992 278 icrypt_encrypt(&j, buff + crq_cipher,
279 buff + crq_cipher, crq_size - crq_cipher);
280 burn(j);
281
03f996bd 282 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
283 buff, crq_size); )
c4f2d992 284 }
285}
286
287/* --- @crypt_unpackRequest@ --- *
288 *
289 * Arguments: @reqest *rq@ = pointer to destination request block
290 * @unsigned char *buff@ = pointer to source buffer
291 * @unsigned char *k@ = pointer to encryption key
292 * @unsigned char *sk@ = pointer to where to store session key
293 * @unsigned char *rpl@ = where to start building reply
294 *
295 * Returns: Nonzero if it was decrypted OK
296 *
297 * Use: Decrypts and unpacks a request buffer.
298 */
299
300int crypt_unpackRequest(request *rq, unsigned char *buff,
301 unsigned char *k, unsigned char *sk,
302 unsigned char *rpl)
303{
304 {
305 /* --- Check the encryption format --- */
306
9e5602f0 307 if (buff[crq_cryptType] != cryptType_blowfish)
c4f2d992 308 return (0);
309 }
310
311 {
312 /* --- First things first: decrypt the block --- */
313
314 icrypt_job j;
315
03f996bd 316 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
317 buff, crq_size); )
c4f2d992 318
9e5602f0 319 T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
320 T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
321 buff + crq_iv, BLOWFISH_BLKSIZE); )
322
323 icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
324 T( traceblk(TRACE_CRYPTO, "crypto: job block:", &j, sizeof(j)); )
325
326 T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
327 buff + crq_session, BLOWFISH_KEYSIZE); )
328 icrypt_decrypt(&j, buff + crq_session,
329 buff + crq_session, BLOWFISH_KEYSIZE);
330 memcpy(sk, buff + crq_session, BLOWFISH_KEYSIZE);
331 T( traceblk(TRACE_CRYPTO, "crypto: session key:",
332 sk, BLOWFISH_KEYSIZE); )
333
334 icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
335
336 T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
337 j.iv, BLOWFISH_BLKSIZE); )
338
c4f2d992 339 icrypt_decrypt(&j, buff + crq_cipher,
340 buff + crq_cipher, crq_size - crq_cipher);
341 icrypt_saveIV(&j, rpl + crp_iv);
342
03f996bd 343 T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
344 buff, crq_size); )
9e5602f0 345
346 memset(buff + crq_session, 0, BLOWFISH_KEYSIZE); /* Burn, baby, burn */
347 burn(j);
c4f2d992 348 }
349
350 {
351 /* --- Check the validity of the data therein --- */
352
353 md5 md;
03f996bd 354 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 355
356 md5_init(&md);
357 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
03f996bd 358 md5_final(&md, mdbuf);
359 if (memcmp(mdbuf, buff + crq_check, 4) != 0) {
c4f2d992 360 syslog(LOG_INFO, "packet rejected: bad checksum");
03f996bd 361 T( trace(TRACE_CRYPTO, "crypto: bad checksum on incoming request"); )
c4f2d992 362 return (0);
363 }
03f996bd 364 burn(md); burn(mdbuf);
c4f2d992 365 }
366
367 {
368 /* --- Extract fields from the block --- */
369
370 rq->from = load32(buff + crq_from);
371 rq->to = load32(buff + crq_to);
372 memcpy(rq->cmd, buff + crq_cmd, CMDLEN_MAX);
373 }
374
375 {
376 /* --- Fill in bits of the reply block --- */
377
378 long t = (long)time(0);
379 long u = (long)load32(buff + crq_time);
380
381 if (t - u > crypt__timeError || u - t > crypt__timeError) {
382 syslog(LOG_INFO, "packet rejected: bad time");
03f996bd 383 T( trace(TRACE_CRYPTO, "crypto: bad time on incoming request"); )
c4f2d992 384 return (0);
385 }
386 memcpy(rpl + crp_time, buff + crq_time, 8);
387 }
388
389 /* --- Done --- */
390
03f996bd 391 T( trace(TRACE_CRYPTO, "crypto: valid request received"); )
c4f2d992 392 return (1);
393}
394
395/* --- @crypt_packReply@ --- *
396 *
397 * Arguments: @char *buff@ = pointer to reply block
398 * @unsigned char *sk@ = pointer to session key
399 * @int answer@ = yes or no
400 *
401 * Returns: ---
402 *
403 * Use: Packs and encrypts a reply block.
404 */
405
406void crypt_packReply(unsigned char *buff, unsigned char *sk, int answer)
407{
408 {
409 /* --- Store the answer --- */
410
411 buff[crp_answer] = (answer != 0);
412 }
413
414 {
415 /* --- Build the checksum --- */
416
417 md5 md;
03f996bd 418 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 419
420 md5_init(&md);
421 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
03f996bd 422 md5_final(&md, mdbuf);
423 memcpy(buff + crp_check, mdbuf, 4);
424 burn(md); burn(mdbuf);
c4f2d992 425 }
426
427 {
428 /* --- Encrypt the buffer --- */
429
430 icrypt_job j;
03f996bd 431
432 T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
433
9e5602f0 434 icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
c4f2d992 435 icrypt_encrypt(&j, buff + crp_cipher,
436 buff + crp_cipher, crp_size - crp_cipher);
c4f2d992 437 burn(j);
03f996bd 438
439 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
c4f2d992 440 }
441}
442
443/* --- @crypt_unpackReply@ --- *
444 *
445 * Arguments: @unsigned char *buff@ = pointer to reply buffer
446 * @unsigned char *sk@ = pointer to session key
447 * @time_t t@ = time at which request was sent
448 * @pid_t pid@ = my process ID
449 *
450 * Returns: >0 if request granted, zero if denied, <0 if reply rejected
451 *
452 * Use: Unpacks a reply block, and informs the caller of the outcome.
453 */
454
455int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
456 time_t t, pid_t pid)
457{
458 {
459 /* --- Decrypt my reply block --- */
460
461 icrypt_job j;
03f996bd 462
463 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
464
9e5602f0 465 icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
c4f2d992 466 icrypt_decrypt(&j, buff + crp_cipher,
467 buff + crp_cipher, crp_size - crp_cipher);
c4f2d992 468 burn(j);
03f996bd 469
470 T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
c4f2d992 471 }
472
473 {
474 /* --- Check validity --- */
475
476 md5 md;
03f996bd 477 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 478 char b[8];
479
480 /* --- Check the checksum --- */
481
482 md5_init(&md);
483 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
03f996bd 484 md5_final(&md, mdbuf);
485 if (memcmp(buff + crp_check, mdbuf, 4) != 0) {
c4f2d992 486 syslog(LOG_INFO, "reply rejected: bad checksum");
03f996bd 487 T( trace(TRACE_CRYPTO, "crypto: bad checksum on reply"); )
c4f2d992 488 return (-1);
489 }
490
491 /* --- Check the identifier --- */
492
493 store32(b + 0, t); store32(b + 4, pid);
494 if (memcmp(b, buff + crp_time, sizeof(b)) != 0) {
495 syslog(LOG_INFO, "reply rejected: bad identification marker");
03f996bd 496 T( trace(TRACE_CRYPTO, "crypto: bad id on reply"); )
c4f2d992 497 return (-1);
498 }
499 }
500
501 /* --- Return the value --- */
502
03f996bd 503 T( trace(TRACE_CRYPTO, "crypto: valid reply received"); )
c4f2d992 504 return (buff[crp_answer]);
505}
506
507/*----- Test rig ----------------------------------------------------------*/
508
509#ifdef TEST_RIG
510
511int main(int argc, char *argv[])
512{
c4f2d992 513 unsigned char buff[8];
9e5602f0 514 unsigned char sk[BLOWFISH_KEYSIZE], k[BLOWFISH_KEYSIZE];
c4f2d992 515 FILE *fp;
516
517 ego(argv[0]);
03f996bd 518 traceon(stdout, TRACE_CRYPTO);
c4f2d992 519 if (argc < 3)
520 die("bad args");
521 fp = fopen(argv[1], "r");
522 if (!fp)
523 die("fopen: %s", strerror(errno));
524 tx_getBits(k, 128, fp);
525 fclose(fp);
03f996bd 526 crypt__sessionKey(argv[2], k, sk, buff);
c4f2d992 527 return (0);
528}
529
530#endif
531
532/*----- That's all, folks -------------------------------------------------*/