3 * $Id: crypt.c,v 1.2.2.1 1997/09/26 09:08:02 mdw Exp $
5 * Cryptographic transfer of `become' requests
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of `become'
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.
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.
24 * You should have received a copy of the GNU General Public License
25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.2.2.1 1997/09/26 09:08:02 mdw
33 * Use the Blowfish encryption algorithm instead of IDEA. This is partly
34 * because I prefer Blowfish (without any particularly strong evidence) but
35 * mainly because IDEA is patented and Blowfish isn't.
37 * Revision 1.2 1997/08/04 10:24:21 mdw
38 * Sources placed under CVS control.
40 * Revision 1.1 1997/07/21 13:47:51 mdw
45 /*----- Header files ------------------------------------------------------*/
47 /* --- ANSI headers --- */
56 /* --- Unix headers --- */
58 #include <sys/types.h>
64 /* --- Local headers --- */
77 /*----- Magic numbers -----------------------------------------------------*/
79 #define crypt__timeError 60 /* Seconds error to permit */
81 /*----- Main code ---------------------------------------------------------*/
83 /* --- @crypt__sessionKey@ --- *
85 * Arguments: @const char *seedfile@ = pointer to name of seed file
86 * @unsigned char *k@ = our secret key
87 * @unsigned *sk@ = where to store the session key
88 * @unsigned char *iv@ = where to store the IV
92 * Use: Decides on a random session key and initialisation vector.
95 static void crypt__sessionKey(const char *seedfile, unsigned char *k,
96 unsigned char *sk, unsigned char *iv)
98 FILE *fp; /* File handle for reading */
102 /* --- Open the random seed file --- *
104 * If I can't manage that, create a new one.
107 if ((fp = fopen(seedfile, "r+")) == 0) {
109 if ((fp = fopen(seedfile, "w+")) == 0)
110 die("can't create random number file: %s", strerror(errno));
114 /* --- Lock the seed file against concurrency problems --- */
117 l.l_whence = SEEK_SET;
120 if (fcntl(fileno(fp), F_SETLKW, &l) < 0)
121 die("can't lock random number file: %s", strerror(errno));
123 /* --- Now read the file, and launder the seed --- */
128 /* --- Encrypt the pool using the secret key --- */
132 icrypt_init(&j, k, BLOWFISH_KEYSIZE, 0);
137 /* --- Generate the session key and IV --- */
140 rand_extract(sk, BLOWFISH_KEYSIZE);
141 rand_extract(iv, BLOWFISH_BLKSIZE);
143 IF_TRACING(TRACE_CRYPTO,
144 traceblk(TRACE_CRYPTO, "crypto: session key:", sk, BLOWFISH_KEYSIZE);
145 traceblk(TRACE_CRYPTO, "crypto: initialisation vector:",
146 iv, BLOWFISH_BLKSIZE);
149 /* --- Write the seed back --- */
157 /* --- @crypt_packRequest@ --- *
159 * Arguments: @request *rq@ = pointer to request block
160 * @unsigned char *buff@ = pointer to a buffer
161 * @time_t t@ = the current time
162 * @pid_t pid@ = my process ID
163 * @unsigned char *k@ = pointer to 128-bit key
164 * @unsigned char *sk@ = where to put the session key
168 * Use: Packs a request block into a buffer. The buffer should have
169 * space for at least @crq_size@ bytes. The buffer comes back
170 * encrypted and ready to send.
173 void crypt_packRequest(request *rq, unsigned char *buff,
175 unsigned char *k, unsigned char *sk)
177 /* --- First, build the easy stuff in the block --- */
179 buff[crq_cryptType] = cryptType_blowfish;
180 store32(buff + crq_time, t);
181 store32(buff + crq_pid, pid);
182 store32(buff + crq_from, rq->from);
183 store32(buff + crq_to, rq->to);
185 /* --- Now generate session keys and things --- */
187 crypt__sessionKey(file_RANDSEED, k, sk, buff + crq_iv);
188 memcpy(buff + crq_session, sk, BLOWFISH_KEYSIZE);
190 /* --- The string causes a few problems --- *
192 * There's a good chance that the string will be a good deal shorter than
193 * the space allowed for it. This will probably mean lots of zeroes, and a
194 * very easy known-plaintext job for a potential attacker. (An early
195 * version of this code used @strncpy@ which is even worse!)
197 * I'll fill the block with random (from @rand@(3) -- nothing too
198 * elaborate) and then encrypt it using Blowfish in CFB mode, using the
199 * first few bytes as the key. This should provide a sufficiently
200 * unpredictable background for the block.
208 unsigned char qk[BLOWFISH_KEYSIZE];
210 /* --- Initialise the buffer with junk --- */
212 srand((unsigned int)(t ^ pid)); /* Seed the (bad) RNG */
213 for (p = buff + crq_cmd; p < buff + crq_cmd + CMDLEN_MAX; p++) {
214 u = rand(); *p = u ^ (u >> 8);
217 /* --- Now make the junk a whole lot harder to predict --- */
220 md5_init(&md); md5_buffer(&md, p, CMDLEN_MAX); md5_final(&md, qk);
221 icrypt_init(&j, qk, BLOWFISH_KEYSIZE, 0);
222 icrypt_encrypt(&j, p, p, CMDLEN_MAX);
223 burn(j); burn(qk); burn(md);
225 /* --- Copy the string into here --- */
227 strcpy((char *)buff + crq_cmd, rq->cmd);
230 /* --- Checksum the finished data --- */
234 unsigned char mdbuf[MD5_HASHSIZE];
237 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
238 md5_final(&md, mdbuf);
239 memcpy(buff + crq_check, mdbuf, 4);
240 burn(md); burn(mdbuf);
243 /* --- Encrypt the block --- *
245 * First, encrypt the session key using the master key. Since the session
246 * key is effectively random, this makes cracking the master key much
247 * harder. The rest of the block is then encrypted with the session key,
248 * using the IV left over from encrypting the session key.
254 T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
257 T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
258 T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
259 buff + crq_iv, BLOWFISH_BLKSIZE); )
260 T( traceblk(TRACE_CRYPTO, "crypto: session key:",
261 sk, BLOWFISH_KEYSIZE); )
263 icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
265 icrypt_encrypt(&j, buff + crq_session,
266 buff + crq_session, BLOWFISH_KEYSIZE);
267 T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
268 buff + crq_session, BLOWFISH_KEYSIZE); )
270 icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
272 T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
273 j.iv, BLOWFISH_BLKSIZE); )
275 icrypt_encrypt(&j, buff + crq_cipher,
276 buff + crq_cipher, crq_size - crq_cipher);
279 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
284 /* --- @crypt_unpackRequest@ --- *
286 * Arguments: @reqest *rq@ = pointer to destination request block
287 * @unsigned char *buff@ = pointer to source buffer
288 * @unsigned char *k@ = pointer to encryption key
289 * @unsigned char *sk@ = pointer to where to store session key
290 * @unsigned char *rpl@ = where to start building reply
292 * Returns: Nonzero if it was decrypted OK
294 * Use: Decrypts and unpacks a request buffer.
297 int crypt_unpackRequest(request *rq, unsigned char *buff,
298 unsigned char *k, unsigned char *sk,
302 /* --- Check the encryption format --- */
304 if (buff[crq_cryptType] != cryptType_blowfish)
309 /* --- First things first: decrypt the block --- */
313 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
316 T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
317 T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
318 buff + crq_iv, BLOWFISH_BLKSIZE); )
320 icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
321 T( traceblk(TRACE_CRYPTO, "crypto: job block:", &j, sizeof(j)); )
323 T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
324 buff + crq_session, BLOWFISH_KEYSIZE); )
325 icrypt_decrypt(&j, buff + crq_session,
326 buff + crq_session, BLOWFISH_KEYSIZE);
327 memcpy(sk, buff + crq_session, BLOWFISH_KEYSIZE);
328 T( traceblk(TRACE_CRYPTO, "crypto: session key:",
329 sk, BLOWFISH_KEYSIZE); )
331 icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
333 T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
334 j.iv, BLOWFISH_BLKSIZE); )
336 icrypt_decrypt(&j, buff + crq_cipher,
337 buff + crq_cipher, crq_size - crq_cipher);
338 icrypt_saveIV(&j, rpl + crp_iv);
340 T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
343 memset(buff + crq_session, 0, BLOWFISH_KEYSIZE); /* Burn, baby, burn */
348 /* --- Check the validity of the data therein --- */
351 unsigned char mdbuf[MD5_HASHSIZE];
354 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
355 md5_final(&md, mdbuf);
356 if (memcmp(mdbuf, buff + crq_check, 4) != 0) {
357 syslog(LOG_INFO, "packet rejected: bad checksum");
358 T( trace(TRACE_CRYPTO, "crypto: bad checksum on incoming request"); )
361 burn(md); burn(mdbuf);
365 /* --- Extract fields from the block --- */
367 rq->from = load32(buff + crq_from);
368 rq->to = load32(buff + crq_to);
369 memcpy(rq->cmd, buff + crq_cmd, CMDLEN_MAX);
373 /* --- Fill in bits of the reply block --- */
375 long t = (long)time(0);
376 long u = (long)load32(buff + crq_time);
378 if (t - u > crypt__timeError || u - t > crypt__timeError) {
379 syslog(LOG_INFO, "packet rejected: bad time");
380 T( trace(TRACE_CRYPTO, "crypto: bad time on incoming request"); )
383 memcpy(rpl + crp_time, buff + crq_time, 8);
388 T( trace(TRACE_CRYPTO, "crypto: valid request received"); )
392 /* --- @crypt_packReply@ --- *
394 * Arguments: @char *buff@ = pointer to reply block
395 * @unsigned char *sk@ = pointer to session key
396 * @int answer@ = yes or no
400 * Use: Packs and encrypts a reply block.
403 void crypt_packReply(unsigned char *buff, unsigned char *sk, int answer)
406 /* --- Store the answer --- */
408 buff[crp_answer] = (answer != 0);
412 /* --- Build the checksum --- */
415 unsigned char mdbuf[MD5_HASHSIZE];
418 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
419 md5_final(&md, mdbuf);
420 memcpy(buff + crp_check, mdbuf, 4);
421 burn(md); burn(mdbuf);
425 /* --- Encrypt the buffer --- */
429 T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
431 icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
432 icrypt_encrypt(&j, buff + crp_cipher,
433 buff + crp_cipher, crp_size - crp_cipher);
436 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
440 /* --- @crypt_unpackReply@ --- *
442 * Arguments: @unsigned char *buff@ = pointer to reply buffer
443 * @unsigned char *sk@ = pointer to session key
444 * @time_t t@ = time at which request was sent
445 * @pid_t pid@ = my process ID
447 * Returns: >0 if request granted, zero if denied, <0 if reply rejected
449 * Use: Unpacks a reply block, and informs the caller of the outcome.
452 int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
456 /* --- Decrypt my reply block --- */
460 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
462 icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
463 icrypt_decrypt(&j, buff + crp_cipher,
464 buff + crp_cipher, crp_size - crp_cipher);
467 T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
471 /* --- Check validity --- */
474 unsigned char mdbuf[MD5_HASHSIZE];
477 /* --- Check the checksum --- */
480 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
481 md5_final(&md, mdbuf);
482 if (memcmp(buff + crp_check, mdbuf, 4) != 0) {
483 syslog(LOG_INFO, "reply rejected: bad checksum");
484 T( trace(TRACE_CRYPTO, "crypto: bad checksum on reply"); )
488 /* --- Check the identifier --- */
490 store32(b + 0, t); store32(b + 4, pid);
491 if (memcmp(b, buff + crp_time, sizeof(b)) != 0) {
492 syslog(LOG_INFO, "reply rejected: bad identification marker");
493 T( trace(TRACE_CRYPTO, "crypto: bad id on reply"); )
498 /* --- Return the value --- */
500 T( trace(TRACE_CRYPTO, "crypto: valid reply received"); )
501 return (buff[crp_answer]);
504 /*----- Test rig ----------------------------------------------------------*/
508 int main(int argc, char *argv[])
510 unsigned char buff[8];
511 unsigned char sk[BLOWFISH_KEYSIZE], k[BLOWFISH_KEYSIZE];
515 traceon(stdout, TRACE_CRYPTO);
518 fp = fopen(argv[1], "r");
520 die("fopen: %s", strerror(errno));
521 tx_getBits(k, 128, fp);
523 crypt__sessionKey(argv[2], k, sk, buff);
529 /*----- That's all, folks -------------------------------------------------*/