summaryrefslogtreecommitdiffstats
path: root/src/lib/crypto/des/f_cksum.c
blob: 05f09263226dc7807b28f1984ef25f7d0291303a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
 * Copyright (c) 1990 Dennis Ferguson.  All rights reserved.
 *
 * Commercial use is permitted only if products which are derived from
 * or include this software are made available for purchase and/or use
 * in Canada.  Otherwise, redistribution and use in source and binary
 * forms are permitted.
 */

/*
 * des_cbc_cksum.c - compute an 8 byte checksum using DES in CBC mode
 */
#include "des.h"
#include "f_tables.h"

/*
 * This routine performs DES cipher-block-chaining checksum operation,
 * a.k.a.  Message Authentication Code.  It ALWAYS encrypts from input
 * to a single 64 bit output MAC checksum.
 *
 * The key schedule is passed as an arg, as well as the cleartext or
 * ciphertext. The cleartext and ciphertext should be in host order.
 *
 * NOTE-- the output is ALWAYS 8 bytes long.  If not enough space was
 * provided, your program will get trashed.
 *
 * The input is null padded, at the end (highest addr), to an integral
 * multiple of eight bytes.
 */

unsigned long
mit_des_cbc_cksum(in, out, length, schedule, ivec)
	des_cblock *in;
	des_cblock *out;
	long length;
	des_key_schedule schedule;
	des_cblock ivec;
{
	register unsigned KRB_INT32 left, right;
	register unsigned KRB_INT32 temp;
	register unsigned KRB_INT32 *kp;
	register unsigned char *ip;
	register KRB_INT32 len;

	/*
	 * Initialize left and right with the contents of the initial
	 * vector.
	 */
	ip = (unsigned char *)ivec;
	GET_HALF_BLOCK(left, ip);
	GET_HALF_BLOCK(right, ip);

	/*
	 * Suitably initialized, now work the length down 8 bytes
	 * at a time.
	 */
	ip = (unsigned char *)in;
	len = length;
	while (len > 0) {
		/*
		 * Get more input, xor it in.  If the length is
		 * greater than or equal to 8 this is straight
		 * forward.  Otherwise we have to fart around.
		 */
		if (len >= 8) {
			left ^= ((*ip++) & 0xff) << 24;
			left ^= ((*ip++) & 0xff) << 16;
			left ^= ((*ip++) & 0xff) << 8;
			left ^= (*ip++) & 0xff;
			right ^= ((*ip++) & 0xff) << 24;
			right ^= ((*ip++) & 0xff) << 16;
			right ^= ((*ip++) & 0xff) << 8;
			right ^= (*ip++) & 0xff;
			len -= 8;
		} else {
			/*
			 * Oh, shoot.  We need to pad the
			 * end with zeroes.  Work backwards
			 * to do this.
			 */
			ip += (int) len;
			switch(len) {
			case 7:
				right ^= (*(--ip) & 0xff) << 8;
			case 6:
				right ^= (*(--ip) & 0xff) << 16;
			case 5:
				right ^= (*(--ip) & 0xff) << 24;
			case 4:
				left ^= *(--ip) & 0xff;
			case 3:
				left ^= (*(--ip) & 0xff) << 8;
			case 2:
				left ^= (*(--ip) & 0xff) << 16;
			case 1:
				left ^= (*(--ip) & 0xff) << 24;
				break;
			}
			len = 0;
		}

		/*
		 * Encrypt what we have
		 */
		kp = (unsigned KRB_INT32 *)schedule;
		DES_DO_ENCRYPT(left, right, temp, kp);
	}

	/*
	 * Done.  Left and right have the checksum.  Put it into
	 * the output.
	 */
	ip = (unsigned char *)out;
	PUT_HALF_BLOCK(left, ip);
	PUT_HALF_BLOCK(right, ip);

	/*
	 * Return right.  I'll bet the MIT code returns this
	 * inconsistantly (with the low order byte of the checksum
	 * not always in the low order byte of the KRB_INT32).  We won't.
	 */
	return right;
}