86ed6058b11ba61c366c16408d18a8b30544e7bb
3 * $Id: crypt.c,v 1.1 1997/07/21 13:47:51 mdw Exp $
5 * Cryptographic transfer of `become' requests
10 /*----- Licencing 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
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.1 1997/07/21 13:47:51 mdw
37 /*----- Header files ------------------------------------------------------*/
39 /* --- ANSI headers --- */
48 /* --- Unix headers --- */
50 #include <sys/types.h>
55 /* --- Local headers --- */
65 /*----- Magic numbers -----------------------------------------------------*/
67 #define crypt__timeError 15 /* Seconds error to permit */
68 #define crypt__seedBits 512 /* Number of random seed bits */
70 /*----- Dump a block of data ----------------------------------------------*/
72 /* --- @crypt__dump@ --- *
74 * Arguments: @char *p@ = a string to display at the top of the message
75 * @unsigned char *buf@ = pointer to a buffer to display
76 * @size_t sz@ = size of the buffer
77 * @FILE *fp@ = file to dump on
81 * Use: Dumps a block of data to the terminal. This allows a
82 * programmer to examine the traffic between client and server,
83 * and possibly locate bugs.
88 static void crypt__dump(const char *p
, const unsigned char *buf
,
92 fprintf(stderr
,"+++ %s\n",p
);
95 fprintf(stderr
,"+++ ");
99 fprintf(stderr
,"%02x ",buf
[i
] & 0xff);
105 putc(i
<sz ?
(isprint(buf
[i
]) ? buf
[i
] : '.') : '*',stderr
);
115 #define crypt__dump(p, buf, sz, fp) ((void)0)
119 /*----- Main code ---------------------------------------------------------*/
121 /* --- @crypt__sessionKey@ --- *
123 * Arguments: @const char *seedfile@ = pointer to name of seed file
124 * @unsigned char *k@ = our secret key
125 * @time_t t@ = the current time
126 * @pid_t pid@ = our process id
127 * @unsigned *sk@ = where to store the session key
128 * @unsigned char *iv@ = where to store the IV
132 * Use: Decides on a random session key and initialisation vector.
135 static void crypt__sessionKey(const char *seedfile
, unsigned char *k
,
137 unsigned char *sk
, unsigned char *iv
)
139 FILE *fp
; /* File handle for reading */
140 unsigned char s
[crypt__seedBits
/ 8]; /* Random seed block */
141 unsigned char b
[4 + 4];
142 /* For building the hash block */
144 /* --- Read the old seed in --- *
146 * Interlock with other processes locking this file.
154 if ((fp
= fopen(seedfile
, "r+")) == 0)
155 die("couldn't open random seed file `%s': %s",
156 seedfile
, strerror(errno
));
160 l
.l_whence
= SEEK_SET
;
163 if (fcntl(fileno(fp
), F_SETLKW
, &l
) < 0)
164 die("couldn't lock random number file: %s", strerror(errno
));
167 tx_getBits(s
, crypt__seedBits
, fp
);
170 crypt__dump("Initial seed", s
, crypt__seedBits
/ 8, stdout
);
172 /* --- Build a block of data to hash --- */
177 /* --- Work out the session key --- */
183 md5_buffer(&md
, s
, sizeof(s
));
184 md5_buffer(&md
, b
, sizeof(b
));
185 md5_buffer(&md
, k
, IDEA_KEYSIZE
);
188 crypt__dump("Session key", sk
, 128 / 8, stdout
);
192 /* --- Build the new seed and write it back again --- */
194 if (crypt__seedBits
> 128) {
195 unsigned char tb
[MD5_HASHSIZE
];
198 memcpy(tb
, s
+ crypt__seedBits
/ 8 - sizeof(tb
), sizeof(tb
));
199 memmove(s
+ sizeof(tb
), s
, crypt__seedBits
/ 8 - sizeof(tb
));
200 memcpy(s
, tb
, sizeof(tb
));
201 for (i
= 0; i
< sizeof(sk
); i
++)
206 /* --- Take the seed we have and hash it again to get an IV --- */
209 unsigned char mdv
[MD5_HASHSIZE
];
213 md5_buffer(&md
, b
, sizeof(b
));
214 md5_buffer(&md
, k
, IDEA_KEYSIZE
);
215 md5_buffer(&md
, s
, sizeof(s
));
217 memcpy(iv
, mdv
, IDEA_BLKSIZE
);
218 crypt__dump("IV", iv
, IDEA_BLKSIZE
, stdout
);
223 /* --- Lock the file again --- *
225 * We're closing the file after we've finished, so we don't need to
226 * unlock it afterwards.
229 crypt__dump("Final seed", s
, crypt__seedBits
/ 8, stdout
);
232 tx_putBits(s
, crypt__seedBits
, fp
);
235 /* --- Destroy sensitive data --- */
240 /* --- @crypt_packRequest@ --- *
242 * Arguments: @request *rq@ = pointer to request block
243 * @unsigned char *buff@ = pointer to a buffer
244 * @time_t t@ = the current time
245 * @pid_t pid@ = my process ID
246 * @unsigned char *k@ = pointer to 128-bit key
247 * @unsigned char *sk@ = where to put the session key
251 * Use: Packs a request block into a buffer. The buffer should have
252 * space for at least @crq_size@ bytes. The buffer comes back
253 * encrypted and ready to send.
256 void crypt_packRequest(request
*rq
, unsigned char *buff
,
258 unsigned char *k
, unsigned char *sk
)
260 /* --- First, build the easy stuff in the block --- */
262 buff
[crq_cryptType
] = cryptType_idea
;
263 store32(buff
+ crq_time
, t
);
264 store32(buff
+ crq_pid
, pid
);
265 store32(buff
+ crq_from
, rq
->from
);
266 store32(buff
+ crq_to
, rq
->to
);
268 /* --- The string causes a few problems --- *
270 * There's a good chance that the string will be a good deal shorter than
271 * the space allowed for it. This will probably mean lots of zeroes, and a
272 * very easy known-plaintext job for a potential attacker. (An early
273 * version of this code used @strncpy@ which is even worse!)
275 * I'll fill the block with random (from @rand@(3) -- nothing too
276 * elaborate) and then encrypt it using IDEA in CFB mode, using the first
277 * few bytes as the key. This should provide a sufficiently unpredictable
278 * background for the block.
286 /* --- Initialise the buffer with junk --- */
288 srand((unsigned int)(t
^ pid
)); /* Seed the (bad) RNG */
289 for (p
= buff
+ crq_cmd
; p
< buff
+ crq_cmd
+ CMDLEN_MAX
; p
++) {
294 /* --- Now make the junk a whole lot harder to predict --- */
297 icrypt_init(&j
, p
, 0);
298 icrypt_encrypt(&j
, p
, p
, CMDLEN_MAX
);
301 /* --- Copy the string into here --- */
303 strcpy((char *)buff
+ crq_cmd
, rq
->cmd
);
306 /* --- Generate a session key --- */
309 crypt__sessionKey(file_RANDSEED
, k
, t
, pid
, sk
, buff
+ crq_iv
);
310 memcpy(buff
+ crq_session
, sk
, IDEA_KEYSIZE
);
313 /* --- Checksum the finished data --- */
317 unsigned char mdv
[MD5_HASHSIZE
];
320 md5_buffer(&md
, buff
+ crq_cipher
, crq_check
- crq_cipher
);
322 memcpy(buff
+ crq_check
, mdv
, 4);
326 /* --- Encrypt the block --- *
328 * First, encrypt the session key using the master key. Since the session
329 * key is effectively random, this makes cracking the master key much
330 * harder. The rest of the block is then encrypted with the session key,
331 * using the IV left over from encrypting the session key.
337 crypt__dump("request, before encryption", buff
, crq_size
, stdout
);
339 icrypt_init(&j
, k
, buff
+ crq_iv
);
340 icrypt_encrypt(&j
, buff
+ crq_session
, buff
+ crq_session
, IDEA_KEYSIZE
);
341 icrypt_reset(&j
, sk
, 0);
342 icrypt_encrypt(&j
, buff
+ crq_cipher
,
343 buff
+ crq_cipher
, crq_size
- crq_cipher
);
346 crypt__dump("request, after encryption", buff
, crq_size
, stdout
);
350 /* --- @crypt_unpackRequest@ --- *
352 * Arguments: @reqest *rq@ = pointer to destination request block
353 * @unsigned char *buff@ = pointer to source buffer
354 * @unsigned char *k@ = pointer to encryption key
355 * @unsigned char *sk@ = pointer to where to store session key
356 * @unsigned char *rpl@ = where to start building reply
358 * Returns: Nonzero if it was decrypted OK
360 * Use: Decrypts and unpacks a request buffer.
363 int crypt_unpackRequest(request
*rq
, unsigned char *buff
,
364 unsigned char *k
, unsigned char *sk
,
368 /* --- Check the encryption format --- */
370 if (buff
[crq_cryptType
] != cryptType_idea
)
375 /* --- First things first: decrypt the block --- */
379 crypt__dump("request, before decryption", buff
, crq_size
, stdout
);
381 icrypt_init(&j
, k
, buff
+ crq_iv
);
382 icrypt_decrypt(&j
, buff
+ crq_session
, buff
+ crq_session
, IDEA_KEYSIZE
);
383 memcpy(sk
, buff
+ crq_session
, IDEA_KEYSIZE
);
384 icrypt_reset(&j
, sk
, 0);
385 icrypt_decrypt(&j
, buff
+ crq_cipher
,
386 buff
+ crq_cipher
, crq_size
- crq_cipher
);
387 icrypt_saveIV(&j
, rpl
+ crp_iv
);
389 memset(buff
+ crq_session
, 0, IDEA_KEYSIZE
); /* Burn, baby, burn */
392 crypt__dump("request, after decryption", buff
, crq_size
, stdout
);
396 /* --- Check the validity of the data therein --- */
399 unsigned char mdv
[MD5_HASHSIZE
];
402 md5_buffer(&md
, buff
+ crq_cipher
, crq_check
- crq_cipher
);
404 if (memcmp(mdv
, buff
+ crq_check
, 4) != 0) {
405 syslog(LOG_INFO
, "packet rejected: bad checksum");
412 /* --- Extract fields from the block --- */
414 rq
->from
= load32(buff
+ crq_from
);
415 rq
->to
= load32(buff
+ crq_to
);
416 memcpy(rq
->cmd
, buff
+ crq_cmd
, CMDLEN_MAX
);
420 /* --- Fill in bits of the reply block --- */
422 long t
= (long)time(0);
423 long u
= (long)load32(buff
+ crq_time
);
425 if (t
- u
> crypt__timeError
|| u
- t
> crypt__timeError
) {
426 syslog(LOG_INFO
, "packet rejected: bad time");
429 memcpy(rpl
+ crp_time
, buff
+ crq_time
, 8);
437 /* --- @crypt_packReply@ --- *
439 * Arguments: @char *buff@ = pointer to reply block
440 * @unsigned char *sk@ = pointer to session key
441 * @int answer@ = yes or no
445 * Use: Packs and encrypts a reply block.
448 void crypt_packReply(unsigned char *buff
, unsigned char *sk
, int answer
)
451 /* --- Store the answer --- */
453 buff
[crp_answer
] = (answer
!= 0);
457 /* --- Build the checksum --- */
460 unsigned char mdv
[MD5_HASHSIZE
];
463 md5_buffer(&md
, buff
+ crp_cipher
, crp_check
- crp_cipher
);
465 memcpy(buff
+ crp_check
, mdv
, 4);
470 /* --- Encrypt the buffer --- */
473 icrypt_init(&j
, sk
, buff
+ crp_iv
);
474 crypt__dump("reply, before encryption", buff
, crp_size
, stdout
);
475 icrypt_encrypt(&j
, buff
+ crp_cipher
,
476 buff
+ crp_cipher
, crp_size
- crp_cipher
);
477 crypt__dump("reply, after encryption", buff
, crp_size
, stdout
);
482 /* --- @crypt_unpackReply@ --- *
484 * Arguments: @unsigned char *buff@ = pointer to reply buffer
485 * @unsigned char *sk@ = pointer to session key
486 * @time_t t@ = time at which request was sent
487 * @pid_t pid@ = my process ID
489 * Returns: >0 if request granted, zero if denied, <0 if reply rejected
491 * Use: Unpacks a reply block, and informs the caller of the outcome.
494 int crypt_unpackReply(unsigned char *buff
, unsigned char *sk
,
498 /* --- Decrypt my reply block --- */
501 icrypt_init(&j
, sk
, buff
+ crp_iv
);
502 crypt__dump("reply, before decryption", buff
, crp_size
, stdout
);
503 icrypt_decrypt(&j
, buff
+ crp_cipher
,
504 buff
+ crp_cipher
, crp_size
- crp_cipher
);
505 crypt__dump("reply, after decryption", buff
, crp_size
, stdout
);
510 /* --- Check validity --- */
513 unsigned char mdv
[MD5_HASHSIZE
];
516 /* --- Check the checksum --- */
519 md5_buffer(&md
, buff
+ crp_cipher
, crp_check
- crp_cipher
);
521 if (memcmp(buff
+ crp_check
, mdv
, 4) != 0) {
522 syslog(LOG_INFO
, "reply rejected: bad checksum");
526 /* --- Check the identifier --- */
528 store32(b
+ 0, t
); store32(b
+ 4, pid
);
529 if (memcmp(b
, buff
+ crp_time
, sizeof(b
)) != 0) {
530 syslog(LOG_INFO
, "reply rejected: bad identification marker");
535 /* --- Return the value --- */
537 return (buff
[crp_answer
]);
540 /*----- Test rig ----------------------------------------------------------*/
544 int main(int argc
, char *argv
[])
547 pid_t pid
= getpid();
548 unsigned char buff
[8];
549 unsigned char sk
[IDEA_KEYSIZE
], k
[IDEA_KEYSIZE
];
555 fp
= fopen(argv
[1], "r");
557 die("fopen: %s", strerror(errno
));
558 tx_getBits(k
, 128, fp
);
560 crypt__sessionKey(argv
[2], k
, t
, pid
, sk
, buff
);
566 /*----- That's all, folks -------------------------------------------------*/