isearly() should take int, not unsigned, for ewin arg
[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 <inttypes.h>
23 #include <string.h>
24 #include <time.h>
25
26 #include "../otp.h"
27 #include "../otp_cardops.h"
28 #include "cryptocard.h"
29
30 /* Card name to feature mask mappings */
31 static struct {
32   const char *name;
33   uint32_t fm;
34 } card[] = {
35   { "cryptocard-h8-rc", CRYPTOCARD_H8_RC },
36   { "cryptocard-d8-rc", CRYPTOCARD_D8_RC },
37   { "cryptocard-h7-rc", CRYPTOCARD_H7_RC },
38   { "cryptocard-d7-rc", CRYPTOCARD_D7_RC },
39   { "cryptocard-h8-es", CRYPTOCARD_H8_ES },
40   { "cryptocard-d8-es", CRYPTOCARD_D8_ES },
41   { "cryptocard-h7-es", CRYPTOCARD_H7_ES },
42   { "cryptocard-d7-es", CRYPTOCARD_D7_ES },
43   { "cryptocard-h8-rs", CRYPTOCARD_H8_RS },
44   { "cryptocard-d8-rs", CRYPTOCARD_D8_RS },
45   { "cryptocard-h7-rs", CRYPTOCARD_H7_RS },
46   { "cryptocard-d7-rs", CRYPTOCARD_D7_RS },
47
48   { NULL, 0 }                                   /* end of list */
49 };
50
51
52 /*
53  * Convert card name to feature mask.
54  * Returns 0 on success, non-zero otherwise.
55  */
56 static int
57 cryptocard_name2fm(const char *name, uint32_t *featuremask)
58 {
59   int i;
60
61   for (i = 0; card[i].name; ++i) {
62     if (!strcasecmp(name, card[i].name)) {
63       *featuremask = card[i].fm;
64       return 0;
65     }
66   }
67   return 1;
68 }
69
70
71 /*
72  * Convert an ASCII keystring to a keyblock.
73  * Returns 0 on success, non-zero otherwise.
74  */
75 static int
76 cryptocard_keystring2keyblock(const char *keystring,
77                               unsigned char keyblock[OTP_MAX_KEY_LEN])
78 {
79   /* 64-bit DES key with optional line ending */
80   if ((strlen(keystring) & ~1) != 16)
81     return 1;
82
83   return otp_keystring2keyblock(keystring, keyblock);
84 }
85
86
87 /*
88  * Set nullstate.
89  * We don't currently support nullstate for CRYPTOCard, so return -1.
90  */
91 static int
92 cryptocard_nullstate(
93 #ifdef __GNUC__
94 __attribute__ ((unused))
95 #endif
96                       const otp_option_t *opt,
97 #ifdef __GNUC__
98 __attribute__ ((unused))
99 #endif
100                       const otp_user_info_t *user_info,
101 #ifdef __GNUC__
102 __attribute__ ((unused))
103 #endif
104                       otp_user_state_t *user_state, 
105 #ifdef __GNUC__
106 __attribute__ ((unused))
107 #endif
108                       time_t when,
109                       const char *log_prefix)
110 {
111   otp_log(OTP_LOG_ERR, "%s: null state not supported for CRYPTOCard",
112           log_prefix);
113   return -1;
114 }
115
116
117 /*
118  * Return a synchronous challenge.
119  * Returns 0 on success, non-zero otherwise.
120  */
121 static int
122 cryptocard_challenge(const otp_user_info_t *user_info,
123 #ifdef __GNUC__
124 __attribute__ ((unused))
125 #endif
126                      const char csd[OTP_MAX_CSD_LEN + 1],
127 #ifdef __GNUC__
128 __attribute__ ((unused))
129 #endif
130                      time_t when,
131                      char challenge[OTP_MAX_CHALLENGE_LEN + 1],
132 #ifdef __GNUC__
133 __attribute__ ((unused))
134 #endif
135                      unsigned twin,
136 #ifdef __GNUC__
137 __attribute__ ((unused))
138 #endif
139                      unsigned ewin,
140                      const char *log_prefix)
141 {
142   unsigned char output[8];
143   int i;
144
145   /* CRYPTOCard sync challenges are always 8 bytes. */
146   if (strlen(challenge) != 8)
147     return -1;
148
149   /* run x99 once on the challenge */
150   if (otp_x99_mac(challenge, 8, output, user_info->keyblock, log_prefix))
151     return -1;
152
153   /* convert the mac into the next challenge */
154   for (i = 0; i < 8; ++i) {
155     output[i] &= 0x0f;
156     if (output[i] > 9)
157       output[i] -= 10;
158     output[i] |= 0x30;
159   }
160   (void) memcpy(challenge, output, 8);
161   challenge[8] = '\0';
162
163   return 0;
164 }
165
166
167 /*
168  * Return the expected card response for a given challenge.
169  * Returns 0 on success, non-zero otherwise.
170  *
171  * The X9.9 MAC is used by CRYPTOcard in the following manner:
172  *
173  * 1. Convert the challenge to ASCII (eg "12345" -> 0x3132333435).
174  *    We don't actually do a conversion, the challenge is already ASCII.
175  *    Note that Secure Computing SafeWord Gold/Platinum tokens can use
176  *    "raw" challenge bytes.
177  * 2. Use the challenge as the plaintext input to the X9.9 MAC algorithm.
178  *
179  * 3. Convert the 32 bit MAC to ASCII (eg 0x1234567f -> "1234567f").
180  *    Note that SafeWord Gold/Platinum tokens can display a 64 bit MAC.
181  * 4. Possibly apply transformations on chars "a" thru "f".
182  * 5. Truncate the response for 7 digit display modes.
183  */
184 static int
185 cryptocard_response(otp_user_info_t *user_info,
186 #ifdef __GNUC__
187 __attribute__ ((unused))
188 #endif
189                     char *csd,
190                     const char *challenge,
191                     char response[OTP_MAX_RESPONSE_LEN + 1],
192                     const char *log_prefix)
193 {
194   unsigned char output[8];
195   char l_response[17];
196   const char *conversion;
197
198   /* Step 1, 2. */
199   if (otp_x99_mac(challenge, strlen(challenge), output,
200                   user_info->keyblock, log_prefix) !=0)
201     return 1;
202
203   /* Setup for step 4. */
204   if (user_info->featuremask & OTP_CF_DD)
205     conversion = otp_cc_dec_conversion;
206   else
207     conversion = otp_hex_conversion;
208
209   /* Step 3, 4. */
210   otp_keyblock2keystring(l_response, output, conversion);
211   (void) memcpy(response, l_response, 8);
212   response[8] = '\0';
213
214   /* Step 5. */
215   if (user_info->featuremask & OTP_CF_R7)
216     (void) memmove(&response[3], &response[4], 5);
217
218   return 0;
219 }
220
221
222 /* no csd so just return success */
223 static int
224 cryptocard_updatecsd(
225 #ifdef __GNUC__
226 __attribute__ ((unused))
227 #endif
228                      const otp_user_info_t *user_info,
229 #ifdef __GNUC__
230 __attribute__ ((unused))
231 #endif
232                      otp_user_state_t *user_state,
233 #ifdef __GNUC__
234 __attribute__ ((unused))
235 #endif
236                      const char challenge[OTP_MAX_CHALLENGE_LEN + 1],
237 #ifdef __GNUC__
238 __attribute__ ((unused))
239 #endif
240                      unsigned twin,
241 #ifdef __GNUC__
242 __attribute__ ((unused))
243 #endif
244                      time_t when,
245 #ifdef __GNUC__
246 __attribute__ ((unused))
247 #endif
248                      int auth_rc,
249 #ifdef __GNUC__
250 __attribute__ ((unused))
251 #endif
252                      const char *log_prefix)
253 {
254   return 0;
255 }
256
257
258 /* events can only go forward, so an auth can never be too early */
259 static int
260 cryptocard_isearly(
261 #ifdef __GNUC__
262 __attribute__ ((unused))
263 #endif
264                    const otp_user_state_t *user_state,
265 #ifdef __GNUC__
266 __attribute__ ((unused))
267 #endif
268                    time_t authtime,
269 #ifdef __GNUC__
270 __attribute__ ((unused))
271 #endif
272                    int ewin,
273 #ifdef __GNUC__
274 __attribute__ ((unused))
275 #endif
276                    const char *log_prefix)
277 {
278   return 0;
279 }
280
281
282 /* no twin so just return 0 */
283 static int
284 cryptocard_nexttwin(
285 #ifdef __GNUC__
286 __attribute__ ((unused))
287 #endif
288                     int twin)
289 {
290   return 0;
291 }
292
293
294 /* no twin so just return 0 */
295 static int
296 cryptocard_maxtwin(
297 #ifdef __GNUC__
298 __attribute__ ((unused))
299 #endif
300                     const otp_user_info_t *user_info,
301 #ifdef __GNUC__
302 __attribute__ ((unused))
303 #endif
304                     const char csd[OTP_MAX_CSD_LEN + 1],
305 #ifdef __GNUC__
306 __attribute__ ((unused))
307 #endif
308                     time_t when)
309 {
310   return 0;
311 }
312
313
314 /* no twin so just return current time */
315 static time_t
316 cryptocard_twin2authtime(
317 #ifdef __GNUC__
318 __attribute__ ((unused))
319 #endif
320                          const char csd[OTP_MAX_CSD_LEN + 1],
321                          time_t when,
322 #ifdef __GNUC__
323 __attribute__ ((unused))
324 #endif
325                          int twin,
326 #ifdef __GNUC__
327 __attribute__ ((unused))
328 #endif
329                          const char *log_prefix)
330 {
331   return when;
332 }
333
334
335 /* cardops instance */
336 static cardops_t cryptocard_cardops = {
337   .prefix               = "cryptocard",
338   .prefix_len           = 10, /* strlen("cryptocard") */
339
340   .name2fm              = cryptocard_name2fm,
341   .keystring2keyblock   = cryptocard_keystring2keyblock,
342   .nullstate            = cryptocard_nullstate,
343   .challenge            = cryptocard_challenge,
344   .response             = cryptocard_response,
345   .updatecsd            = cryptocard_updatecsd,
346   .isearly              = cryptocard_isearly,
347   .nexttwin             = cryptocard_nexttwin,
348   .maxtwin              = cryptocard_maxtwin,
349   .twin2authtime        = cryptocard_twin2authtime
350 };
351
352
353 /* constructor */
354 void
355 cryptocard_init(void)
356 {
357   if (otp_num_cardops == OTP_MAX_VENDORS) {
358     otp_log(OTP_LOG_ERR, "cryptocard_init: module limit exceeded");
359     return;
360   }
361
362   otp_cardops[otp_num_cardops++] = cryptocard_cardops;
363   otp_log(OTP_LOG_DEBUG, "cryptocard_init: loaded");
364 }