update copyright (assign to TRI-D instead of myself)
[freeradius.git] / src / modules / rlm_otp / cardops / cryptocard.c
1 /*
2  * cryptocard.c
3  * $Id$
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * Copyright 2005 TRI-D Systems, Inc.
20  */
21
22 #include <string.h>
23
24 #include "../otp.h"
25 #include "../otp_cardops.h"
26 #include "cryptocard.h"
27
28 /* Card name to feature mask mappings */
29 static struct {
30   const char *name;
31   uint32_t fm;
32 } card[] = {
33   { "cryptocard-h8-rc", CRYPTOCARD_H8_RC },
34   { "cryptocard-d8-rc", CRYPTOCARD_D8_RC },
35   { "cryptocard-h7-rc", CRYPTOCARD_H7_RC },
36   { "cryptocard-d7-rc", CRYPTOCARD_D7_RC },
37   { "cryptocard-h8-es", CRYPTOCARD_H8_ES },
38   { "cryptocard-d8-es", CRYPTOCARD_D8_ES },
39   { "cryptocard-h7-es", CRYPTOCARD_H7_ES },
40   { "cryptocard-d7-es", CRYPTOCARD_D7_ES },
41   { "cryptocard-h8-rs", CRYPTOCARD_H8_RS },
42   { "cryptocard-d8-rs", CRYPTOCARD_D8_RS },
43   { "cryptocard-h7-rs", CRYPTOCARD_H7_RS },
44   { "cryptocard-d7-rs", CRYPTOCARD_D7_RS },
45
46   { NULL, 0 }                                   /* end of list */
47 };
48
49
50 /*
51  * Convert card name to feature mask.
52  * Returns 0 on success, non-zero otherwise.
53  */
54 static int
55 cryptocard_name2fm(const char *name, uint32_t *featuremask)
56 {
57   int i;
58
59   for (i = 0; card[i].name; ++i) {
60     if (!strcasecmp(name, card[i].name)) {
61       *featuremask = card[i].fm;
62       return 0;
63     }
64   }
65   return 1;
66 }
67
68
69 /*
70  * Convert an ASCII keystring to a keyblock.
71  * Returns 0 on success, non-zero otherwise.
72  */
73 static int
74 cryptocard_keystring2keyblock(const char *keystring, unsigned char keyblock[])
75 {
76   /* 64-bit DES key with optional line ending */
77   if ((strlen(keystring) & ~1) != 16)
78     return 1;
79
80   return otp_keystring2keyblock(keystring, keyblock);
81 }
82
83
84 /*
85  * Return a synchronous challenge.
86  * Returns 0 on success, non-zero otherwise.
87  */
88 static int
89 cryptocard_challenge(const otp_user_info_t *user_info, unsigned int ewin,
90 #ifdef __GNUC__
91 __attribute__ ((unused))
92 #endif
93                      int twin,
94                      char challenge[OTP_MAX_CHALLENGE_LEN + 1])
95 {
96   unsigned char output[8];
97   int i, rc = 0;
98
99   /* CRYPTOCard sync challenges are always 8 bytes. */
100   if (strlen(challenge) != 8)
101     return -1;
102
103   /* iterate ewin times on the challenge */
104   while (ewin--) {
105     if ((rc = otp_x99_mac(challenge, 8, output, user_info->keyblock)) == 0) {
106       /* convert the mac into the next challenge */
107       for (i = 0; i < 8; ++i) {
108         output[i] &= 0x0f;
109         if (output[i] > 9)
110           output[i] -= 10;
111         output[i] |= 0x30;
112       }
113       (void) memcpy(challenge, output, 8);
114       challenge[8] = '\0';
115     } else {
116       /* something went wrong */
117       break;
118     }
119   }
120
121   return rc;
122 }
123
124
125 /*
126  * Return the expected card response for a given challenge.
127  * Returns 0 on success, non-zero otherwise.
128  *
129  * The X9.9 MAC is used by CRYPTOcard in the following manner:
130  *
131  * 1. Convert the challenge to ASCII (eg "12345" -> 0x3132333435).
132  *    We don't actually do a conversion, the challenge is already ASCII.
133  *    Note that Secure Computing SafeWord Gold/Platinum tokens can use
134  *    "raw" challenge bytes.
135  * 2. Use the challenge as the plaintext input to the X9.9 MAC algorithm.
136  *
137  * 3. Convert the 32 bit MAC to ASCII (eg 0x1234567f -> "1234567f").
138  *    Note that SafeWord Gold/Platinum tokens can display a 64 bit MAC.
139  * 4. Possibly apply transformations on chars "a" thru "f".
140  * 5. Truncate the response for 7 digit display modes.
141  */
142 static int
143 cryptocard_response(otp_user_info_t *user_info, const char *challenge,
144                     char response[OTP_MAX_RESPONSE_LEN + 1])
145 {
146   unsigned char output[8];
147   char l_response[17];
148   const char *conversion;
149
150   /* Step 1, 2. */
151   if (otp_x99_mac(challenge, strlen(challenge), output,
152                   user_info->keyblock) !=0)
153     return 1;
154
155   /* Setup for step 4. */
156   if (user_info->featuremask & OTP_CF_DD)
157     conversion = otp_cc_dec_conversion;
158   else
159     conversion = otp_hex_conversion;
160
161   /* Step 3, 4. */
162   otp_keyblock2keystring(l_response, output, conversion);
163   (void) memcpy(response, l_response, 8);
164   response[8] = '\0';
165
166   /* Step 5. */
167   if (user_info->featuremask & OTP_CF_R7)
168     (void) memmove(&response[3], &response[4], 5);
169
170   return 0;
171 }
172
173
174 /* cardops instance */
175 static cardops_t cryptocard_cardops = {
176   .prefix               = "cryptocard",
177   .prefix_len           = 10, /* strlen("cryptocard") */
178
179   .name2fm              = cryptocard_name2fm,
180   .keystring2keyblock   = cryptocard_keystring2keyblock,
181   .challenge            = cryptocard_challenge,
182   .response             = cryptocard_response
183 };
184
185 /* constructor */
186 void
187 cryptocard_init(void)
188 {
189   if (otp_num_cardops == OTP_MAX_VENDORS) {
190     otp_log(OTP_LOG_ERR, "cryptocard_init: module limit exceeded");
191     return;
192   }
193
194   otp_cardops[otp_num_cardops++] = cryptocard_cardops;
195   otp_log(OTP_LOG_DEBUG, "cryptocard_init: loaded");
196 }