669303859a5a62490cd0b25a658ef4717ebef543
[freeradius.git] / src / modules / rlm_x99_token / crcalc.c
1 /*
2  * crcalc.c     
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the Free Software
16  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Copyright 2001,2002 Google, Inc.
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <openssl/des.h>
25
26 /*
27  * Convert the ASCII string representation of a DES key to raw octets.
28  * keyblock is filled in.  Returns 0 on success, -1 otherwise.
29  */
30 static
31 int string_to_keyblock(const char *s, des_cblock keyblock)
32 {
33     int i;
34
35     if (s == NULL || strlen(s) < 16)
36         return -1;
37
38     /*
39      * We could just use sscanf, but we do this a lot, and have very
40      * specific needs, and it's easy to implement, so let's go for it!
41      */
42     for (i = 0; i < 8; ++i) {
43         unsigned int n[2];
44
45         n[0] = *s++ - '0';
46         n[1] = *s++ - '0';
47         if (n[0] > 9) {
48             n[0] -= 'a' - '9' - 1;
49         }
50         if (n[1] > 9) {
51             n[1] -= 'a' - '9' - 1;
52         }
53
54         keyblock[i]  = n[0] << 4;
55         keyblock[i] += n[1];
56     }
57     return 0;
58 }
59
60
61 /* Character maps for generic hex and vendor specific decimal modes */
62 static const char ascii_conversion[]  = "0123456789abcdef";
63 static const char cc_dec_conversion[] = "0123456789012345";
64
65 /*
66  * Convert a DES keyblock to an ASCII string.
67  * Fills in s, which must point to at least 17 bytes of space.
68  * Note that each octet expands into 2 hex digits in ASCII (0xAA -> 0x4141);
69  * add a NULL string terminator and you get the 17 byte requirement.
70  */
71 static
72 void keyblock_to_string(char *s, const des_cblock keyblock,
73                         const char conversion[17])
74 {
75     int i;
76
77     for (i = 0; i < 8; ++i) {
78         unsigned n[2];
79
80         n[0] = (keyblock[i] >> 4) & 0x0f;
81         n[1] = keyblock[i] & 0x0f;
82         s[2 * i + 0] = conversion[n[0]];
83         s[2 * i + 1] = conversion[n[1]];
84     }
85     s[16] = '\0';
86 }
87
88
89 int
90 main(int argc, char *argv[])
91 {
92     /* ARGSUSED */
93     char ascii_key[17];
94     char challenge[10], response[9], response_long[17];
95     char buf[BUFSIZ];
96     des_cblock keyblock;
97     des_key_schedule ks;
98     char *p;
99     int i, rc;
100
101     memset(ascii_key, 0, sizeof(ascii_key));
102
103     /* get the key */
104     fprintf(stdout, "Enter a DES key as 16 hex digits (spaces allowed): ");
105     fgets(buf, sizeof(buf), stdin);
106     buf[strlen(buf) - 1] = '\0'; /* strip '\n' */
107     p = buf;
108
109     /* setup key */
110     if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X'))
111         p += 2;
112     i = 0;
113     while (*p) {
114         if (*p == ' ') {
115             p++;
116             continue;
117         }
118         if (*p < '0' || *p > '9') {
119             if (*p < 'a' || *p > 'f') {
120                 if (*p < 'A' || *p > 'F') {
121                     fprintf(stderr, "bad key\n");
122                     exit(1);
123                 }
124             }
125         }
126         if (i > 15) {
127             fprintf(stderr, "key too long\n");
128             exit(1);
129         }
130         ascii_key[i++] = tolower((int) *p++);
131     }
132     if (strlen(ascii_key) < 16) {
133         fprintf(stderr, "key too short\n");
134         exit(1);
135     }
136     string_to_keyblock(ascii_key, keyblock);
137
138     /* verify the key. */
139 key_verify:
140     if ((rc = des_set_key_checked(&keyblock, ks)) != 0) {
141         fprintf(stderr, "key %s\n",
142                rc == -1 ? "has incorrect parity" : "is weak");
143         if (rc == -1) {
144             des_set_odd_parity(&keyblock);
145             goto key_verify;
146         }
147         else {
148             exit(1);
149         }
150     }
151
152     fprintf(stdout, "Enter the challenge: ");
153     fgets(challenge, sizeof(challenge), stdin);
154     challenge[strlen(challenge) - 1] = '\0'; /* strip '\n' */
155     /* encrypt null block if no challenge */
156
157     /*
158      * Calculate the response.  The algorithm is:
159      * 1. Convert the challenge to ASCII bytes (eg "12345" -> 0x3132333435).
160      * 2. Pad LSB of a 64-bit block w/ 0 bytes if challenge < 8 bytes (digits).
161      * 3. Encrypt w/ DES (whose block size is 64 bits).
162      * 4. Convert the most significant 32 bits of the ciphertext
163      *    to 8 hex digits as a string (eg 0x1234567f -> "1234567f").
164      */
165     {
166         des_cblock input, output;
167
168         /* Step 1, 2 (conversion is already done, just copy and pad) */
169         (void) memset(input, 0, sizeof(input));
170         (void) memcpy(input, challenge, strlen(challenge));
171
172         /* Step 3 */
173         des_ecb_encrypt(&input, &output, ks, 1);
174
175         /* Step 4, 5 */
176         keyblock_to_string(response_long, output, ascii_conversion);
177         (void) memcpy(response, response_long, 8);
178         response[8] = '\0';
179         memcpy(challenge, output, 8);
180         challenge[8] = '\0';
181     }
182
183     /* calculate the next challenge for cryptocard */
184     for (i = 0; i < 8; ++i) {
185         challenge[i] &= 0x0f;
186         if (challenge[i] > 9)
187             challenge[i] -= 10;
188         challenge[i] |= 0x30;
189     }
190
191     fprintf(stdout, "response is %s [%s]\n", response, &response_long[8]);
192     fprintf(stdout, "next challenge is %s\n", challenge);
193     exit(0);
194 }
195
196