/* * crcalc.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2001,2002 Google, Inc. */ #include #include #include #include /* * Convert the ASCII string representation of a DES key to raw octets. * keyblock is filled in. Returns 0 on success, -1 otherwise. */ static int string_to_keyblock(const char *s, des_cblock keyblock) { int i; if (s == NULL || strlen(s) < 16) return -1; /* * We could just use sscanf, but we do this a lot, and have very * specific needs, and it's easy to implement, so let's go for it! */ for (i = 0; i < 8; ++i) { unsigned int n[2]; n[0] = *s++ - '0'; n[1] = *s++ - '0'; if (n[0] > 9) { n[0] -= 'a' - '9' - 1; } if (n[1] > 9) { n[1] -= 'a' - '9' - 1; } keyblock[i] = n[0] << 4; keyblock[i] += n[1]; } return 0; } /* Character maps for generic hex and vendor specific decimal modes */ static const char ascii_conversion[] = "0123456789abcdef"; static const char cc_dec_conversion[] = "0123456789012345"; /* * Convert a DES keyblock to an ASCII string. * Fills in s, which must point to at least 17 bytes of space. * Note that each octet expands into 2 hex digits in ASCII (0xAA -> 0x4141); * add a NULL string terminator and you get the 17 byte requirement. */ static void keyblock_to_string(char *s, const des_cblock keyblock, const char conversion[17]) { int i; for (i = 0; i < 8; ++i) { unsigned n[2]; n[0] = (keyblock[i] >> 4) & 0x0f; n[1] = keyblock[i] & 0x0f; s[2 * i + 0] = conversion[n[0]]; s[2 * i + 1] = conversion[n[1]]; } s[16] = '\0'; } int main(int argc, char *argv[]) { /* ARGSUSED */ char ascii_key[17]; char challenge[10], response[9], response_long[17]; char buf[BUFSIZ]; des_cblock keyblock; des_key_schedule ks; char *p; int i, rc; memset(ascii_key, 0, sizeof(ascii_key)); /* get the key */ fprintf(stdout, "Enter a DES key as 16 hex digits (spaces allowed): "); fgets(buf, sizeof(buf), stdin); buf[strlen(buf) - 1] = '\0'; /* strip '\n' */ p = buf; /* setup key */ if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) p += 2; i = 0; while (*p) { if (*p == ' ') { p++; continue; } if (*p < '0' || *p > '9') { if (*p < 'a' || *p > 'f') { if (*p < 'A' || *p > 'F') { fprintf(stderr, "bad key\n"); exit(1); } } } if (i > 15) { fprintf(stderr, "key too long\n"); exit(1); } ascii_key[i++] = tolower((int) *p++); } if (strlen(ascii_key) < 16) { fprintf(stderr, "key too short\n"); exit(1); } string_to_keyblock(ascii_key, keyblock); /* verify the key. */ key_verify: if ((rc = des_set_key_checked(&keyblock, ks)) != 0) { fprintf(stderr, "key %s\n", rc == -1 ? "has incorrect parity" : "is weak"); if (rc == -1) { des_set_odd_parity(&keyblock); goto key_verify; } else { exit(1); } } fprintf(stdout, "Enter the challenge: "); fgets(challenge, sizeof(challenge), stdin); challenge[strlen(challenge) - 1] = '\0'; /* strip '\n' */ /* encrypt null block if no challenge */ /* * Calculate the response. The algorithm is: * 1. Convert the challenge to ASCII bytes (eg "12345" -> 0x3132333435). * 2. Pad LSB of a 64-bit block w/ 0 bytes if challenge < 8 bytes (digits). * 3. Encrypt w/ DES (whose block size is 64 bits). * 4. Convert the most significant 32 bits of the ciphertext * to 8 hex digits as a string (eg 0x1234567f -> "1234567f"). */ { des_cblock input, output; /* Step 1, 2 (conversion is already done, just copy and pad) */ (void) memset(input, 0, sizeof(input)); (void) memcpy(input, challenge, strlen(challenge)); /* Step 3 */ des_ecb_encrypt(&input, &output, ks, 1); /* Step 4, 5 */ keyblock_to_string(response_long, output, ascii_conversion); (void) memcpy(response, response_long, 8); response[8] = '\0'; memcpy(challenge, output, 8); challenge[8] = '\0'; } /* calculate the next challenge for cryptocard */ for (i = 0; i < 8; ++i) { challenge[i] &= 0x0f; if (challenge[i] > 9) challenge[i] -= 10; challenge[i] |= 0x30; } fprintf(stdout, "response is %s [%s]\n", response, &response_long[8]); fprintf(stdout, "next challenge is %s\n", challenge); exit(0); }