1 /*
2 * totp.c - implementation of the OATH TOTP algorithm
3 * Copyright (C) 2011-2016 Simon Josefsson
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 *
20 */
21
22 #include <config.h>
23
24 #include "oath.h"
25 #include "hotp.h"
26 #include "aux.h" /* _oath_strcmp_callback */
27
28 /**
29 * oath_totp_generate:
30 * @secret: the shared secret string
31 * @secret_length: length of @secret
32 * @now: Unix time value to compute TOTP for
33 * @time_step_size: time step system parameter (typically 30)
34 * @start_offset: Unix time of when to start counting time steps (typically 0)
35 * @digits: number of requested digits in the OTP, excluding checksum
36 * @output_otp: output buffer, must have room for the output OTP plus zero
37 *
38 * Generate a one-time-password using the time-variant TOTP algorithm
39 * described in RFC 6238. The input parameters are taken as time
40 * values.
41 *
42 * The system parameter @time_step_size describes how long the time
43 * window for each OTP is. The recommended value is 30 seconds, and
44 * you can use the value 0 or the symbol
45 * %OATH_TOTP_DEFAULT_TIME_STEP_SIZE to indicate this.
46 *
47 * The system parameter @start_offset denote the Unix time when time
48 * steps are started to be counted. The recommended value is 0, to
49 * fall back on the Unix epoch) and you can use the symbol
50 * %OATH_TOTP_DEFAULT_START_TIME to indicate this.
51 *
52 * The @output_otp buffer must have room for at least @digits
53 * characters, plus one for the terminating NUL.
54 *
55 * Currently only values 6, 7 and 8 for @digits are supported. This
56 * restriction may be lifted in future versions.
57 *
58 * Returns: On success, %OATH_OK (zero) is returned, otherwise an
59 * error code is returned.
60 *
61 * Since: 1.4.0
62 **/
63 int
oath_totp_generate(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,unsigned digits,char * output_otp)64 oath_totp_generate (const char *secret,
65 size_t secret_length,
66 time_t now,
67 unsigned time_step_size,
68 time_t start_offset, unsigned digits, char *output_otp)
69 {
70 return oath_totp_generate2 (secret, secret_length, now, time_step_size,
71 start_offset, digits, 0, output_otp);
72 }
73
74 /**
75 * oath_totp_generate2:
76 * @secret: the shared secret string
77 * @secret_length: length of @secret
78 * @now: Unix time value to compute TOTP for
79 * @time_step_size: time step system parameter (typically 30)
80 * @start_offset: Unix time of when to start counting time steps (typically 0)
81 * @digits: number of requested digits in the OTP, excluding checksum
82 * @flags: flags indicating mode, one of #oath_totp_flags
83 * @output_otp: output buffer, must have room for the output OTP plus zero
84 *
85 * Generate a one-time-password using the time-variant TOTP algorithm
86 * described in RFC 6238. The input parameters are taken as time
87 * values.
88 *
89 * The system parameter @time_step_size describes how long the time
90 * window for each OTP is. The recommended value is 30 seconds, and
91 * you can use the value 0 or the symbol
92 * %OATH_TOTP_DEFAULT_TIME_STEP_SIZE to indicate this.
93 *
94 * The system parameter @start_offset denote the Unix time when time
95 * steps are started to be counted. The recommended value is 0, to
96 * fall back on the Unix epoch) and you can use the symbol
97 * %OATH_TOTP_DEFAULT_START_TIME to indicate this.
98 *
99 * The @output_otp buffer must have room for at least @digits
100 * characters, plus one for the terminating NUL.
101 *
102 * Currently only values 6, 7 and 8 for @digits are supported. This
103 * restriction may be lifted in future versions.
104 *
105 * The @flags parameter may be used to change the MAC function, for
106 * example %OATH_TOTP_HMAC_SHA256 or %OATH_TOTP_HMAC_SHA512.
107 *
108 * Returns: On success, %OATH_OK (zero) is returned, otherwise an
109 * error code is returned.
110 *
111 * Since: 2.6.0
112 **/
113 int
oath_totp_generate2(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,unsigned digits,int flags,char * output_otp)114 oath_totp_generate2 (const char *secret,
115 size_t secret_length,
116 time_t now,
117 unsigned time_step_size,
118 time_t start_offset,
119 unsigned digits, int flags, char *output_otp)
120 {
121 uint64_t nts;
122
123 if (time_step_size == 0)
124 time_step_size = OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
125
126 nts = (now - start_offset) / time_step_size;
127
128 return _oath_hotp_generate2 (secret,
129 secret_length,
130 nts,
131 digits,
132 false, OATH_HOTP_DYNAMIC_TRUNCATION, flags,
133 output_otp);
134 }
135
136 /**
137 * oath_totp_validate:
138 * @secret: the shared secret string
139 * @secret_length: length of @secret
140 * @now: Unix time value to validate TOTP for
141 * @time_step_size: time step system parameter (typically 30)
142 * @start_offset: Unix time of when to start counting time steps (typically 0)
143 * @window: how many OTPs after/before start OTP to test
144 * @otp: the OTP to validate.
145 *
146 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
147 *
148 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
149 * restrictions may be lifted in future versions, although some
150 * limitations are inherent in the protocol.
151 *
152 * Returns: Returns absolute value of position in OTP window (zero is
153 * first position), or %OATH_INVALID_OTP if no OTP was found in OTP
154 * window, or an error code.
155 *
156 * Since: 1.6.0
157 **/
158 int
oath_totp_validate(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,size_t window,const char * otp)159 oath_totp_validate (const char *secret,
160 size_t secret_length,
161 time_t now,
162 unsigned time_step_size,
163 time_t start_offset, size_t window, const char *otp)
164 {
165 return oath_totp_validate3 (secret, secret_length, now, time_step_size,
166 start_offset, window, NULL, NULL, otp);
167 }
168
169 /**
170 * oath_totp_validate_callback:
171 * @secret: the shared secret string
172 * @secret_length: length of @secret
173 * @now: Unix time value to compute TOTP for
174 * @time_step_size: time step system parameter (typically 30)
175 * @start_offset: Unix time of when to start counting time steps (typically 0)
176 * @window: how many OTPs after start counter to test
177 * @digits: number of requested digits in the OTP
178 * @strcmp_otp: function pointer to a strcmp-like function.
179 * @strcmp_handle: caller handle to be passed on to @strcmp_otp.
180 *
181 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
182 *
183 * Validation is implemented by generating a number of potential OTPs
184 * and performing a call to the @strcmp_otp function, to compare the
185 * potential OTP against the given @otp. It has the following
186 * prototype:
187 *
188 * int (*oath_validate_strcmp_function) (void *handle, const char *test_otp);
189 *
190 * The function should be similar to strcmp in that it return 0 only
191 * on matches. It differs by permitting use of negative return codes
192 * as indication of internal failures in the callback. Positive
193 * values indicate OTP mismatch.
194 *
195 * This callback interface is useful when you cannot compare OTPs
196 * directly using normal strcmp, but instead for example only have a
197 * hashed OTP. You would then typically pass in the hashed OTP in the
198 * @strcmp_handle and let your implementation of @strcmp_otp hash the
199 * test_otp OTP using the same hash, and then compare the results.
200 *
201 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
202 * restrictions may be lifted in future versions, although some
203 * limitations are inherent in the protocol.
204 *
205 * Returns: Returns position in OTP window (zero is first position),
206 * or %OATH_INVALID_OTP if no OTP was found in OTP window, or an
207 * error code.
208 *
209 * Since: 1.6.0
210 **/
211 int
oath_totp_validate_callback(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,unsigned digits,size_t window,oath_validate_strcmp_function strcmp_otp,void * strcmp_handle)212 oath_totp_validate_callback (const char *secret,
213 size_t secret_length,
214 time_t now,
215 unsigned time_step_size,
216 time_t start_offset,
217 unsigned digits,
218 size_t window,
219 oath_validate_strcmp_function strcmp_otp,
220 void *strcmp_handle)
221 {
222 return oath_totp_validate4_callback (secret, secret_length, now,
223 time_step_size, start_offset,
224 digits, window, NULL, NULL, 0,
225 strcmp_otp, strcmp_handle);
226 }
227
228 /**
229 * oath_totp_validate2:
230 * @secret: the shared secret string
231 * @secret_length: length of @secret
232 * @now: Unix time value to validate TOTP for
233 * @time_step_size: time step system parameter (typically 30)
234 * @start_offset: Unix time of when to start counting time steps (typically 0)
235 * @window: how many OTPs after/before start OTP to test
236 * @otp_pos: output search position in search window (may be NULL).
237 * @otp: the OTP to validate.
238 *
239 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
240 *
241 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
242 * restrictions may be lifted in future versions, although some
243 * limitations are inherent in the protocol.
244 *
245 * Returns: Returns absolute value of position in OTP window (zero is
246 * first position), or %OATH_INVALID_OTP if no OTP was found in OTP
247 * window, or an error code.
248 *
249 * Since: 1.10.0
250 **/
251 int
oath_totp_validate2(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,size_t window,int * otp_pos,const char * otp)252 oath_totp_validate2 (const char *secret,
253 size_t secret_length,
254 time_t now,
255 unsigned time_step_size,
256 time_t start_offset,
257 size_t window, int *otp_pos, const char *otp)
258 {
259 return oath_totp_validate4_callback (secret, secret_length, now,
260 time_step_size, start_offset,
261 strlen (otp), window, otp_pos,
262 NULL, 0, _oath_strcmp_callback,
263 (void *) otp);
264 }
265
266 /**
267 * oath_totp_validate2_callback:
268 * @secret: the shared secret string
269 * @secret_length: length of @secret
270 * @now: Unix time value to compute TOTP for
271 * @time_step_size: time step system parameter (typically 30)
272 * @start_offset: Unix time of when to start counting time steps (typically 0)
273 * @digits: number of requested digits in the OTP
274 * @window: how many OTPs after start counter to test
275 * @otp_pos: output search position in search window (may be NULL).
276 * @strcmp_otp: function pointer to a strcmp-like function.
277 * @strcmp_handle: caller handle to be passed on to @strcmp_otp.
278 *
279 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
280 *
281 * Validation is implemented by generating a number of potential OTPs
282 * and performing a call to the @strcmp_otp function, to compare the
283 * potential OTP against the given @otp. It has the following
284 * prototype:
285 *
286 * int (*oath_validate_strcmp_function) (void *handle, const char *test_otp);
287 *
288 * The function should be similar to strcmp in that it return 0 only
289 * on matches. It differs by permitting use of negative return codes
290 * as indication of internal failures in the callback. Positive
291 * values indicate OTP mismatch.
292 *
293 * This callback interface is useful when you cannot compare OTPs
294 * directly using normal strcmp, but instead for example only have a
295 * hashed OTP. You would then typically pass in the hashed OTP in the
296 * @strcmp_handle and let your implementation of @strcmp_otp hash the
297 * test_otp OTP using the same hash, and then compare the results.
298 *
299 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
300 * restrictions may be lifted in future versions, although some
301 * limitations are inherent in the protocol.
302 *
303 * Returns: Returns absolute value of position in OTP window (zero is
304 * first position), or %OATH_INVALID_OTP if no OTP was found in OTP
305 * window, or an error code.
306 *
307 * Since: 1.10.0
308 **/
309 int
oath_totp_validate2_callback(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,unsigned digits,size_t window,int * otp_pos,oath_validate_strcmp_function strcmp_otp,void * strcmp_handle)310 oath_totp_validate2_callback (const char *secret,
311 size_t secret_length,
312 time_t now,
313 unsigned time_step_size,
314 time_t start_offset,
315 unsigned digits,
316 size_t window,
317 int *otp_pos,
318 oath_validate_strcmp_function strcmp_otp,
319 void *strcmp_handle)
320 {
321 return oath_totp_validate4_callback (secret, secret_length, now,
322 time_step_size, start_offset,
323 digits, window, otp_pos, NULL,
324 0, strcmp_otp, strcmp_handle);
325 }
326
327 /**
328 * oath_totp_validate3:
329 * @secret: the shared secret string
330 * @secret_length: length of @secret
331 * @now: Unix time value to validate TOTP for
332 * @time_step_size: time step system parameter (typically 30)
333 * @start_offset: Unix time of when to start counting time steps (typically 0)
334 * @window: how many OTPs after/before start OTP to test
335 * @otp_pos: output search position in search window (may be NULL).
336 * @otp_counter: counter value used to calculate OTP value (may be NULL).
337 * @otp: the OTP to validate.
338 *
339 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
340 *
341 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
342 * restrictions may be lifted in future versions, although some
343 * limitations are inherent in the protocol.
344 *
345 * Returns: Returns absolute value of position in OTP window (zero is
346 * first position), or %OATH_INVALID_OTP if no OTP was found in OTP
347 * window, or an error code.
348 *
349 * Since: 2.4.0
350 **/
351 int
oath_totp_validate3(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,size_t window,int * otp_pos,uint64_t * otp_counter,const char * otp)352 oath_totp_validate3 (const char *secret,
353 size_t secret_length,
354 time_t now,
355 unsigned time_step_size,
356 time_t start_offset,
357 size_t window,
358 int *otp_pos, uint64_t * otp_counter, const char *otp)
359 {
360 return oath_totp_validate4_callback (secret, secret_length, now,
361 time_step_size, start_offset,
362 strlen (otp), window, otp_pos,
363 otp_counter, 0, _oath_strcmp_callback,
364 (void *) otp);
365 }
366
367 /**
368 * oath_totp_validate3_callback:
369 * @secret: the shared secret string
370 * @secret_length: length of @secret
371 * @now: Unix time value to compute TOTP for
372 * @time_step_size: time step system parameter (typically 30)
373 * @start_offset: Unix time of when to start counting time steps (typically 0)
374 * @digits: number of requested digits in the OTP
375 * @window: how many OTPs after start counter to test
376 * @otp_pos: output search position in search window (may be NULL).
377 * @otp_counter: counter value used to calculate OTP value (may be NULL).
378 * @strcmp_otp: function pointer to a strcmp-like function.
379 * @strcmp_handle: caller handle to be passed on to @strcmp_otp.
380 *
381 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
382 *
383 * Validation is implemented by generating a number of potential OTPs
384 * and performing a call to the @strcmp_otp function, to compare the
385 * potential OTP against the given @otp. It has the following
386 * prototype:
387 *
388 * int (*oath_validate_strcmp_function) (void *handle, const char *test_otp);
389 *
390 * The function should be similar to strcmp in that it return 0 only
391 * on matches. It differs by permitting use of negative return codes
392 * as indication of internal failures in the callback. Positive
393 * values indicate OTP mismatch.
394 *
395 * This callback interface is useful when you cannot compare OTPs
396 * directly using normal strcmp, but instead for example only have a
397 * hashed OTP. You would then typically pass in the hashed OTP in the
398 * @strcmp_handle and let your implementation of @strcmp_otp hash the
399 * test_otp OTP using the same hash, and then compare the results.
400 *
401 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
402 * restrictions may be lifted in future versions, although some
403 * limitations are inherent in the protocol.
404 *
405 * Returns: Returns absolute value of position in OTP window (zero is
406 * first position), or %OATH_INVALID_OTP if no OTP was found in OTP
407 * window, or an error code.
408 *
409 * Since: 2.4.0
410 **/
411 int
oath_totp_validate3_callback(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,unsigned digits,size_t window,int * otp_pos,uint64_t * otp_counter,oath_validate_strcmp_function strcmp_otp,void * strcmp_handle)412 oath_totp_validate3_callback (const char *secret,
413 size_t secret_length,
414 time_t now,
415 unsigned time_step_size,
416 time_t start_offset,
417 unsigned digits,
418 size_t window,
419 int *otp_pos,
420 uint64_t * otp_counter,
421 oath_validate_strcmp_function strcmp_otp,
422 void *strcmp_handle)
423 {
424 return oath_totp_validate4_callback (secret, secret_length, now,
425 time_step_size, start_offset,
426 digits, window, otp_pos,
427 otp_counter, 0, strcmp_otp,
428 strcmp_handle);
429 }
430
431 /**
432 * oath_totp_validate4:
433 * @secret: the shared secret string
434 * @secret_length: length of @secret
435 * @now: Unix time value to validate TOTP for
436 * @time_step_size: time step system parameter (typically 30)
437 * @start_offset: Unix time of when to start counting time steps (typically 0)
438 * @window: how many OTPs after/before start OTP to test
439 * @otp_pos: output search position in search window (may be NULL).
440 * @otp_counter: counter value used to calculate OTP value (may be NULL).
441 * @flags: flags indicating mode, one of #oath_totp_flags
442 * @otp: the OTP to validate.
443 *
444 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
445 *
446 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
447 * restrictions may be lifted in future versions, although some
448 * limitations are inherent in the protocol.
449 *
450 * The @flags parameter may be used to change the MAC function, for
451 * example %OATH_TOTP_HMAC_SHA256 or %OATH_TOTP_HMAC_SHA512.
452 *
453 * Returns: Returns absolute value of position in OTP window (zero is
454 * first position), or %OATH_INVALID_OTP if no OTP was found in OTP
455 * window, or an error code.
456 *
457 * Since: 2.6.0
458 **/
459 int
oath_totp_validate4(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,size_t window,int * otp_pos,uint64_t * otp_counter,int flags,const char * otp)460 oath_totp_validate4 (const char *secret,
461 size_t secret_length,
462 time_t now,
463 unsigned time_step_size,
464 time_t start_offset,
465 size_t window,
466 int *otp_pos,
467 uint64_t * otp_counter, int flags, const char *otp)
468 {
469 return oath_totp_validate4_callback (secret, secret_length, now,
470 time_step_size, start_offset,
471 strlen (otp), window, otp_pos,
472 otp_counter, flags,
473 _oath_strcmp_callback, (void *) otp);
474 }
475
476 /**
477 * oath_totp_validate4_callback:
478 * @secret: the shared secret string
479 * @secret_length: length of @secret
480 * @now: Unix time value to compute TOTP for
481 * @time_step_size: time step system parameter (typically 30)
482 * @start_offset: Unix time of when to start counting time steps (typically 0)
483 * @digits: number of requested digits in the OTP
484 * @window: how many OTPs after start counter to test
485 * @otp_pos: output search position in search window (may be NULL).
486 * @otp_counter: counter value used to calculate OTP value (may be NULL).
487 * @flags: flags indicating mode, one of #oath_totp_flags
488 * @strcmp_otp: function pointer to a strcmp-like function.
489 * @strcmp_handle: caller handle to be passed on to @strcmp_otp.
490 *
491 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
492 *
493 * Validation is implemented by generating a number of potential OTPs
494 * and performing a call to the @strcmp_otp function, to compare the
495 * potential OTP against the given @otp. It has the following
496 * prototype:
497 *
498 * int (*oath_validate_strcmp_function) (void *handle, const char *test_otp);
499 *
500 * The function should be similar to strcmp in that it return 0 only
501 * on matches. It differs by permitting use of negative return codes
502 * as indication of internal failures in the callback. Positive
503 * values indicate OTP mismatch.
504 *
505 * This callback interface is useful when you cannot compare OTPs
506 * directly using normal strcmp, but instead for example only have a
507 * hashed OTP. You would then typically pass in the hashed OTP in the
508 * @strcmp_handle and let your implementation of @strcmp_otp hash the
509 * test_otp OTP using the same hash, and then compare the results.
510 *
511 * Currently only OTP lengths of 6, 7 or 8 digits are supported. This
512 * restrictions may be lifted in future versions, although some
513 * limitations are inherent in the protocol.
514 *
515 * The @flags parameter may be used to change the MAC function, for
516 * example %OATH_TOTP_HMAC_SHA256 or %OATH_TOTP_HMAC_SHA512.
517 *
518 * Returns: Returns absolute value of position in OTP window (zero is
519 * first position), or %OATH_INVALID_OTP if no OTP was found in OTP
520 * window, or an error code.
521 *
522 * Since: 2.6.0
523 **/
524 int
oath_totp_validate4_callback(const char * secret,size_t secret_length,time_t now,unsigned time_step_size,time_t start_offset,unsigned digits,size_t window,int * otp_pos,uint64_t * otp_counter,int flags,oath_validate_strcmp_function strcmp_otp,void * strcmp_handle)525 oath_totp_validate4_callback (const char *secret,
526 size_t secret_length,
527 time_t now,
528 unsigned time_step_size,
529 time_t start_offset,
530 unsigned digits,
531 size_t window,
532 int *otp_pos,
533 uint64_t * otp_counter,
534 int flags,
535 oath_validate_strcmp_function strcmp_otp,
536 void *strcmp_handle)
537 {
538 unsigned iter = 0;
539 char tmp_otp[10];
540 int rc;
541 uint64_t nts;
542
543 if (time_step_size == 0)
544 time_step_size = OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
545
546 nts = (now - start_offset) / time_step_size;
547
548 do
549 {
550 rc = _oath_hotp_generate2 (secret,
551 secret_length,
552 nts + iter,
553 digits,
554 false,
555 OATH_HOTP_DYNAMIC_TRUNCATION,
556 flags, tmp_otp);
557 if (rc != OATH_OK)
558 return rc;
559
560 if ((rc = strcmp_otp (strcmp_handle, tmp_otp)) == 0)
561 {
562 if (otp_counter)
563 *otp_counter = nts + iter;
564 if (otp_pos)
565 *otp_pos = iter;
566 return iter;
567 }
568 if (rc < 0)
569 return OATH_STRCMP_ERROR;
570
571 if (iter > 0)
572 {
573 rc = _oath_hotp_generate2 (secret,
574 secret_length,
575 nts - iter,
576 digits,
577 false,
578 OATH_HOTP_DYNAMIC_TRUNCATION,
579 flags, tmp_otp);
580 if (rc != OATH_OK)
581 return rc;
582
583 if ((rc = strcmp_otp (strcmp_handle, tmp_otp)) == 0)
584 {
585 if (otp_counter)
586 *otp_counter = nts - iter;
587 if (otp_pos)
588 *otp_pos = -iter;
589 return iter;
590 }
591 if (rc < 0)
592 return OATH_STRCMP_ERROR;
593 }
594 }
595 while (window - iter++ > 0);
596
597 return OATH_INVALID_OTP;
598 }
599