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.
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.
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
18 * Copyright 2001,2002 Google, Inc.
24 #include <openssl/des.h>
27 * Convert the ASCII string representation of a DES key to raw octets.
28 * keyblock is filled in. Returns 0 on success, -1 otherwise.
31 int string_to_keyblock(const char *s, des_cblock keyblock)
35 if (s == NULL || strlen(s) < 16)
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!
42 for (i = 0; i < 8; ++i) {
48 n[0] -= 'a' - '9' - 1;
51 n[1] -= 'a' - '9' - 1;
54 keyblock[i] = n[0] << 4;
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";
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.
72 void keyblock_to_string(char *s, const des_cblock keyblock,
73 const char conversion[17])
77 for (i = 0; i < 8; ++i) {
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]];
90 main(int argc, char *argv[])
94 char challenge[10], response[9], response_long[17];
101 memset(ascii_key, 0, sizeof(ascii_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' */
110 if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X'))
118 if (*p < '0' || *p > '9') {
119 if (*p < 'a' || *p > 'f') {
120 if (*p < 'A' || *p > 'F') {
121 fprintf(stderr, "bad key\n");
127 fprintf(stderr, "key too long\n");
130 ascii_key[i++] = tolower((int) *p++);
132 if (strlen(ascii_key) < 16) {
133 fprintf(stderr, "key too short\n");
136 string_to_keyblock(ascii_key, keyblock);
138 /* verify the key. */
140 if ((rc = des_set_key_checked(&keyblock, ks)) != 0) {
141 fprintf(stderr, "key %s\n",
142 rc == -1 ? "has incorrect parity" : "is weak");
144 des_set_odd_parity(&keyblock);
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 */
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").
166 des_cblock input, output;
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));
173 des_ecb_encrypt(&input, &output, ks, 1);
176 keyblock_to_string(response_long, output, ascii_conversion);
177 (void) memcpy(response, response_long, 8);
179 memcpy(challenge, output, 8);
183 /* calculate the next challenge for cryptocard */
184 for (i = 0; i < 8; ++i) {
185 challenge[i] &= 0x0f;
186 if (challenge[i] > 9)
188 challenge[i] |= 0x30;
191 fprintf(stdout, "response is %s [%s]\n", response, &response_long[8]);
192 fprintf(stdout, "next challenge is %s\n", challenge);