3 * $Id: crypt.c,v 1.5 1998/06/18 15:08:49 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.5 1998/06/18 15:08:49 mdw
33 * Paranoia: set close-on-exec flag for seed file.
35 * Revision 1.4 1998/01/12 16:45:55 mdw
38 * Revision 1.3 1997/09/26 09:14:58 mdw
39 * Merged blowfish branch into trunk.
41 * Revision 1.2.2.1 1997/09/26 09:08:02 mdw
42 * Use the Blowfish encryption algorithm instead of IDEA. This is partly
43 * because I prefer Blowfish (without any particularly strong evidence) but
44 * mainly because IDEA is patented and Blowfish isn't.
46 * Revision 1.2 1997/08/04 10:24:21 mdw
47 * Sources placed under CVS control.
49 * Revision 1.1 1997/07/21 13:47:51 mdw
54 /*----- Header files ------------------------------------------------------*/
56 /* --- ANSI headers --- */
65 /* --- Unix headers --- */
67 #include <sys/types.h>
73 /* --- Local headers --- */
86 /*----- Magic numbers -----------------------------------------------------*/
88 #define crypt__timeError 60 /* Seconds error to permit */
90 /*----- Main code ---------------------------------------------------------*/
92 /* --- @crypt__sessionKey@ --- *
94 * Arguments: @const char *seedfile@ = pointer to name of seed file
95 * @unsigned char *k@ = our secret key
96 * @unsigned *sk@ = where to store the session key
97 * @unsigned char *iv@ = where to store the IV
101 * Use: Decides on a random session key and initialisation vector.
104 static void crypt__sessionKey(const char *seedfile
, unsigned char *k
,
105 unsigned char *sk
, unsigned char *iv
)
107 FILE *fp
; /* File handle for reading */
111 /* --- Open the random seed file --- *
113 * If I can't manage that, create a new one.
116 if ((fp
= fopen(seedfile
, "r+")) == 0) {
118 if ((fp
= fopen(seedfile
, "w+")) == 0)
119 die("can't create random number file: %s", strerror(errno
));
122 if (fcntl(fileno(fp
), F_SETFD
, 1) < 0) {
123 die("can't set close-on-exec for random number file: %s",
127 /* --- Lock the seed file against concurrency problems --- */
130 l
.l_whence
= SEEK_SET
;
133 if (fcntl(fileno(fp
), F_SETLKW
, &l
) < 0)
134 die("can't lock random number file: %s", strerror(errno
));
136 /* --- Now read the file, and launder the seed --- */
141 /* --- Encrypt the pool using the secret key --- */
145 icrypt_init(&j
, k
, BLOWFISH_KEYSIZE
, 0);
150 /* --- Generate the session key and IV --- */
153 rand_extract(sk
, BLOWFISH_KEYSIZE
);
154 rand_extract(iv
, BLOWFISH_BLKSIZE
);
156 IF_TRACING(TRACE_CRYPTO
,
157 traceblk(TRACE_CRYPTO
, "crypto: session key:", sk
, BLOWFISH_KEYSIZE
);
158 traceblk(TRACE_CRYPTO
, "crypto: initialisation vector:",
159 iv
, BLOWFISH_BLKSIZE
);
162 /* --- Write the seed back --- */
170 /* --- @crypt_packRequest@ --- *
172 * Arguments: @request *rq@ = pointer to request block
173 * @unsigned char *buff@ = pointer to a buffer
174 * @time_t t@ = the current time
175 * @pid_t pid@ = my process ID
176 * @unsigned char *k@ = pointer to 128-bit key
177 * @unsigned char *sk@ = where to put the session key
181 * Use: Packs a request block into a buffer. The buffer should have
182 * space for at least @crq_size@ bytes. The buffer comes back
183 * encrypted and ready to send.
186 void crypt_packRequest(request
*rq
, unsigned char *buff
,
188 unsigned char *k
, unsigned char *sk
)
190 /* --- First, build the easy stuff in the block --- */
192 buff
[crq_cryptType
] = cryptType_blowfish
;
193 store32(buff
+ crq_time
, t
);
194 store32(buff
+ crq_pid
, pid
);
195 store32(buff
+ crq_from
, rq
->from
);
196 store32(buff
+ crq_to
, rq
->to
);
198 /* --- Now generate session keys and things --- */
200 crypt__sessionKey(file_RANDSEED
, k
, sk
, buff
+ crq_iv
);
201 memcpy(buff
+ crq_session
, sk
, BLOWFISH_KEYSIZE
);
203 /* --- The string causes a few problems --- *
205 * There's a good chance that the string will be a good deal shorter than
206 * the space allowed for it. This will probably mean lots of zeroes, and a
207 * very easy known-plaintext job for a potential attacker. (An early
208 * version of this code used @strncpy@ which is even worse!)
210 * I'll fill the block with random (from @rand@(3) -- nothing too
211 * elaborate) and then encrypt it using Blowfish in CFB mode, using the
212 * first few bytes as the key. This should provide a sufficiently
213 * unpredictable background for the block.
221 unsigned char qk
[BLOWFISH_KEYSIZE
];
223 /* --- Initialise the buffer with junk --- */
225 srand((unsigned int)(t
^ pid
)); /* Seed the (bad) RNG */
226 for (p
= buff
+ crq_cmd
; p
< buff
+ crq_cmd
+ CMDLEN_MAX
; p
++) {
227 u
= rand(); *p
= u
^ (u
>> 8);
230 /* --- Now make the junk a whole lot harder to predict --- */
233 md5_init(&md
); md5_buffer(&md
, p
, CMDLEN_MAX
); md5_final(&md
, qk
);
234 icrypt_init(&j
, qk
, BLOWFISH_KEYSIZE
, 0);
235 icrypt_encrypt(&j
, p
, p
, CMDLEN_MAX
);
236 burn(j
); burn(qk
); burn(md
);
238 /* --- Copy the string into here --- */
240 strcpy((char *)buff
+ crq_cmd
, rq
->cmd
);
243 /* --- Checksum the finished data --- */
247 unsigned char mdbuf
[MD5_HASHSIZE
];
250 md5_buffer(&md
, buff
+ crq_cipher
, crq_check
- crq_cipher
);
251 md5_final(&md
, mdbuf
);
252 memcpy(buff
+ crq_check
, mdbuf
, 4);
253 burn(md
); burn(mdbuf
);
256 /* --- Encrypt the block --- *
258 * First, encrypt the session key using the master key. Since the session
259 * key is effectively random, this makes cracking the master key much
260 * harder. The rest of the block is then encrypted with the session key,
261 * using the IV left over from encrypting the session key.
267 T( traceblk(TRACE_CRYPTO
, "crypto: plaintext request:",
270 T( traceblk(TRACE_CRYPTO
, "crypto: master key:", k
, BLOWFISH_KEYSIZE
); )
271 T( traceblk(TRACE_CRYPTO
, "crypto: initial iv:",
272 buff
+ crq_iv
, BLOWFISH_BLKSIZE
); )
273 T( traceblk(TRACE_CRYPTO
, "crypto: session key:",
274 sk
, BLOWFISH_KEYSIZE
); )
276 icrypt_init(&j
, k
, BLOWFISH_KEYSIZE
, buff
+ crq_iv
);
278 icrypt_encrypt(&j
, buff
+ crq_session
,
279 buff
+ crq_session
, BLOWFISH_KEYSIZE
);
280 T( traceblk(TRACE_CRYPTO
, "crypto: encrypted session key:",
281 buff
+ crq_session
, BLOWFISH_KEYSIZE
); )
283 icrypt_reset(&j
, sk
, BLOWFISH_KEYSIZE
, 0);
285 T( traceblk(TRACE_CRYPTO
, "crypto: partial iv:",
286 j
.iv
, BLOWFISH_BLKSIZE
); )
288 icrypt_encrypt(&j
, buff
+ crq_cipher
,
289 buff
+ crq_cipher
, crq_size
- crq_cipher
);
292 T( traceblk(TRACE_CRYPTO
, "crypto: ciphertext request:",
297 /* --- @crypt_unpackRequest@ --- *
299 * Arguments: @reqest *rq@ = pointer to destination request block
300 * @unsigned char *buff@ = pointer to source buffer
301 * @unsigned char *k@ = pointer to encryption key
302 * @unsigned char *sk@ = pointer to where to store session key
303 * @unsigned char *rpl@ = where to start building reply
305 * Returns: Nonzero if it was decrypted OK
307 * Use: Decrypts and unpacks a request buffer.
310 int crypt_unpackRequest(request
*rq
, unsigned char *buff
,
311 unsigned char *k
, unsigned char *sk
,
315 /* --- Check the encryption format --- */
317 if (buff
[crq_cryptType
] != cryptType_blowfish
)
322 /* --- First things first: decrypt the block --- */
326 T( traceblk(TRACE_CRYPTO
, "crypto: ciphertext request:",
329 T( traceblk(TRACE_CRYPTO
, "crypto: master key:", k
, BLOWFISH_KEYSIZE
); )
330 T( traceblk(TRACE_CRYPTO
, "crypto: initial iv:",
331 buff
+ crq_iv
, BLOWFISH_BLKSIZE
); )
333 icrypt_init(&j
, k
, BLOWFISH_KEYSIZE
, buff
+ crq_iv
);
334 T( traceblk(TRACE_CRYPTO
, "crypto: job block:", &j
, sizeof(j
)); )
336 T( traceblk(TRACE_CRYPTO
, "crypto: encrypted session key:",
337 buff
+ crq_session
, BLOWFISH_KEYSIZE
); )
338 icrypt_decrypt(&j
, buff
+ crq_session
,
339 buff
+ crq_session
, BLOWFISH_KEYSIZE
);
340 memcpy(sk
, buff
+ crq_session
, BLOWFISH_KEYSIZE
);
341 T( traceblk(TRACE_CRYPTO
, "crypto: session key:",
342 sk
, BLOWFISH_KEYSIZE
); )
344 icrypt_reset(&j
, sk
, BLOWFISH_KEYSIZE
, 0);
346 T( traceblk(TRACE_CRYPTO
, "crypto: partial iv:",
347 j
.iv
, BLOWFISH_BLKSIZE
); )
349 icrypt_decrypt(&j
, buff
+ crq_cipher
,
350 buff
+ crq_cipher
, crq_size
- crq_cipher
);
351 icrypt_saveIV(&j
, rpl
+ crp_iv
);
353 T( traceblk(TRACE_CRYPTO
, "crypto: plaintext request:",
356 memset(buff
+ crq_session
, 0, BLOWFISH_KEYSIZE
); /* Burn, baby, burn */
361 /* --- Check the validity of the data therein --- */
364 unsigned char mdbuf
[MD5_HASHSIZE
];
367 md5_buffer(&md
, buff
+ crq_cipher
, crq_check
- crq_cipher
);
368 md5_final(&md
, mdbuf
);
369 if (memcmp(mdbuf
, buff
+ crq_check
, 4) != 0) {
370 syslog(LOG_INFO
, "packet rejected: bad checksum");
371 T( trace(TRACE_CRYPTO
, "crypto: bad checksum on incoming request"); )
374 burn(md
); burn(mdbuf
);
378 /* --- Extract fields from the block --- */
380 rq
->from
= load32(buff
+ crq_from
);
381 rq
->to
= load32(buff
+ crq_to
);
382 memcpy(rq
->cmd
, buff
+ crq_cmd
, CMDLEN_MAX
);
386 /* --- Fill in bits of the reply block --- */
388 long t
= (long)time(0);
389 long u
= (long)load32(buff
+ crq_time
);
391 if (t
- u
> crypt__timeError
|| u
- t
> crypt__timeError
) {
392 syslog(LOG_INFO
, "packet rejected: bad time");
393 T( trace(TRACE_CRYPTO
, "crypto: bad time on incoming request"); )
396 memcpy(rpl
+ crp_time
, buff
+ crq_time
, 8);
401 T( trace(TRACE_CRYPTO
, "crypto: valid request received"); )
405 /* --- @crypt_packReply@ --- *
407 * Arguments: @char *buff@ = pointer to reply block
408 * @unsigned char *sk@ = pointer to session key
409 * @int answer@ = yes or no
413 * Use: Packs and encrypts a reply block.
416 void crypt_packReply(unsigned char *buff
, unsigned char *sk
, int answer
)
419 /* --- Store the answer --- */
421 buff
[crp_answer
] = (answer
!= 0);
425 /* --- Build the checksum --- */
428 unsigned char mdbuf
[MD5_HASHSIZE
];
431 md5_buffer(&md
, buff
+ crp_cipher
, crp_check
- crp_cipher
);
432 md5_final(&md
, mdbuf
);
433 memcpy(buff
+ crp_check
, mdbuf
, 4);
434 burn(md
); burn(mdbuf
);
438 /* --- Encrypt the buffer --- */
442 T( traceblk(TRACE_CRYPTO
, "crypto: plaintext reply:", buff
, crp_size
); )
444 icrypt_init(&j
, sk
, BLOWFISH_KEYSIZE
, buff
+ crp_iv
);
445 icrypt_encrypt(&j
, buff
+ crp_cipher
,
446 buff
+ crp_cipher
, crp_size
- crp_cipher
);
449 T( traceblk(TRACE_CRYPTO
, "crypto: ciphertext reply:", buff
, crp_size
); )
453 /* --- @crypt_unpackReply@ --- *
455 * Arguments: @unsigned char *buff@ = pointer to reply buffer
456 * @unsigned char *sk@ = pointer to session key
457 * @time_t t@ = time at which request was sent
458 * @pid_t pid@ = my process ID
460 * Returns: >0 if request granted, zero if denied, <0 if reply rejected
462 * Use: Unpacks a reply block, and informs the caller of the outcome.
465 int crypt_unpackReply(unsigned char *buff
, unsigned char *sk
,
469 /* --- Decrypt my reply block --- */
473 T( traceblk(TRACE_CRYPTO
, "crypto: ciphertext reply:", buff
, crp_size
); )
475 icrypt_init(&j
, sk
, BLOWFISH_KEYSIZE
, buff
+ crp_iv
);
476 icrypt_decrypt(&j
, buff
+ crp_cipher
,
477 buff
+ crp_cipher
, crp_size
- crp_cipher
);
480 T( traceblk(TRACE_CRYPTO
, "crypto: plaintext reply:", buff
, crp_size
); )
484 /* --- Check validity --- */
487 unsigned char mdbuf
[MD5_HASHSIZE
];
490 /* --- Check the checksum --- */
493 md5_buffer(&md
, buff
+ crp_cipher
, crp_check
- crp_cipher
);
494 md5_final(&md
, mdbuf
);
495 if (memcmp(buff
+ crp_check
, mdbuf
, 4) != 0) {
496 syslog(LOG_INFO
, "reply rejected: bad checksum");
497 T( trace(TRACE_CRYPTO
, "crypto: bad checksum on reply"); )
501 /* --- Check the identifier --- */
503 store32(b
+ 0, t
); store32(b
+ 4, pid
);
504 if (memcmp(b
, buff
+ crp_time
, sizeof(b
)) != 0) {
505 syslog(LOG_INFO
, "reply rejected: bad identification marker");
506 T( trace(TRACE_CRYPTO
, "crypto: bad id on reply"); )
511 /* --- Return the value --- */
513 T( trace(TRACE_CRYPTO
, "crypto: valid reply received"); )
514 return (buff
[crp_answer
]);
517 /*----- Test rig ----------------------------------------------------------*/
521 int main(int argc
, char *argv
[])
523 unsigned char buff
[8];
524 unsigned char sk
[BLOWFISH_KEYSIZE
], k
[BLOWFISH_KEYSIZE
];
528 traceon(stdout
, TRACE_CRYPTO
);
531 fp
= fopen(argv
[1], "r");
533 die("fopen: %s", strerror(errno
));
534 tx_getBits(k
, 128, fp
);
536 crypt__sessionKey(argv
[2], k
, sk
, buff
);
542 /*----- That's all, folks -------------------------------------------------*/