/** * oath_totp_generate: * @secret: the shared secret string * @secret_length: length of @secret * @now: Unix time value to compute TOTP for * @time_step_size: time step system parameter (typically 30) * @start_offset: Unix time of when to start counting time steps (typically 0) * @digits: number of requested digits in the OTP, excluding checksum * @output_otp: output buffer, must have room for the output OTP plus zero * * Generate a one-time-password using the time-variant TOTP algorithm * described in RFC 6238. The input parameters are taken as time * values. * * The system parameter @time_step_size describes how long the time * window for each OTP is. The recommended value is 30 seconds, and * you can use the value 0 or the symbol * %OATH_TOTP_DEFAULT_TIME_STEP_SIZE to indicate this. * * The system parameter @start_offset denote the Unix time when time * steps are started to be counted. The recommended value is 0, to * fall back on the Unix epoch) and you can use the symbol * %OATH_TOTP_DEFAULT_START_TIME to indicate this. * * The @output_otp buffer must have room for at least @digits * characters, plus one for the terminating NUL. * * Currently only values 6, 7 and 8 for @digits are supported. This * restriction may be lifted in future versions. * * Returns: On success, %OATH_OK (zero) is returned, otherwise an * error code is returned. * * Since: 1.4.0 **/ 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) { uint64_t nts; if (time_step_size == 0) time_step_size = OATH_TOTP_DEFAULT_TIME_STEP_SIZE; nts = (now - start_offset) / time_step_size; return oath_hotp_generate (secret, secret_length, nts, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, output_otp); }
int main (void) { oath_rc rc; char nulls[1] = "\x00"; char secret[32] = "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30" "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30" "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32"; size_t secretlen = 20; char otp[10]; uint64_t moving_factor; unsigned digits; rc = oath_init (); if (rc != OATH_OK) { printf ("oath_init: %d\n", rc); return 1; } moving_factor = 1099511627776ULL; rc = oath_hotp_generate (nulls, 1, moving_factor, 6, false, OATH_HOTP_DYNAMIC_TRUNCATION, otp); if (rc != OATH_OK) { printf ("oath_hotp_generate %llu: %d\n", (long long unsigned) moving_factor, rc); return 1; } if (strcmp ("363425", otp) != 0) { printf ("oath_hotp_generate got otp %s\n", otp); return 1; } for (digits = 6; digits <= 8; digits++) for (moving_factor = 0; moving_factor < MAX_ITER; moving_factor++) { rc = oath_hotp_generate (secret, secretlen, moving_factor, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, otp); if (rc != OATH_OK) { printf ("oath_hotp_generate: %d\n", rc); return 1; } #if DEBUG printf ("otp[%d]: %s\n", (unsigned) moving_factor, otp); #endif if (!expect[digits][moving_factor]) { printf ("no test vector for digits %d counter %ld\n", digits, (long) moving_factor); return 1; } if (strcmp (otp, expect[digits][moving_factor]) != 0) { printf ("otp[%d][%ld] got %s expected %s\n", digits, (long) moving_factor, otp, expect[digits][moving_factor]); return 1; } rc = oath_hotp_generate (secret, 32, moving_factor, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, otp); if (rc != OATH_OK) { printf ("oath_hotp_generate: %d\n", rc); return 1; } } for (digits = 0; digits < 6; digits++) { rc = oath_hotp_generate (secret, secretlen, moving_factor, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, otp); if (rc != OATH_INVALID_DIGITS) { printf ("oath_hotp_generate %d digits %d\n", digits, rc); return 1; } } for (digits = 9; digits < 15; digits++) { rc = oath_hotp_generate (secret, secretlen, moving_factor, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, otp); if (rc != OATH_INVALID_DIGITS) { printf ("oath_hotp_generate %d digits %d\n", digits, rc); return 1; } } rc = oath_done (); if (rc != OATH_OK) { printf ("oath_done: %d\n", rc); return 1; } return 0; }
/** * oath_totp_validate2_callback: * @secret: the shared secret string * @secret_length: length of @secret * @now: Unix time value to compute TOTP for * @time_step_size: time step system parameter (typically 30) * @start_offset: Unix time of when to start counting time steps (typically 0) * @digits: number of requested digits in the OTP * @window: how many OTPs after start counter to test * @otp_pos: output search position in search window (may be NULL). * @strcmp_otp: function pointer to a strcmp-like function. * @strcmp_handle: caller handle to be passed on to @strcmp_otp. * * Validate an OTP according to OATH TOTP algorithm per RFC 6238. * * Validation is implemented by generating a number of potential OTPs * and performing a call to the @strcmp_otp function, to compare the * potential OTP against the given @otp. It has the following * prototype: * * int (*oath_validate_strcmp_function) (void *handle, const char *test_otp); * * The function should be similar to strcmp in that it return 0 only * on matches. It differs by permitting use of negative return codes * as indication of internal failures in the callback. Positive * values indicate OTP mismatch. * * This callback interface is useful when you cannot compare OTPs * directly using normal strcmp, but instead for example only have a * hashed OTP. You would then typically pass in the hashed OTP in the * @strcmp_handle and let your implementation of @strcmp_otp hash the * test_otp OTP using the same hash, and then compare the results. * * Currently only OTP lengths of 6, 7 or 8 digits are supported. This * restrictions may be lifted in future versions, although some * limitations are inherent in the protocol. * * Returns: Returns absolute value of position in OTP window (zero is * first position), or %OATH_INVALID_OTP if no OTP was found in OTP * window, or an error code. * * Since: 1.10.0 **/ 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) { unsigned iter = 0; char tmp_otp[10]; int rc; uint64_t nts; if (time_step_size == 0) time_step_size = OATH_TOTP_DEFAULT_TIME_STEP_SIZE; nts = (now - start_offset) / time_step_size; do { rc = oath_hotp_generate (secret, secret_length, nts + iter, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, tmp_otp); if (rc != OATH_OK) return rc; if ((rc = strcmp_otp (strcmp_handle, tmp_otp)) == 0) { if (otp_pos) *otp_pos = iter; return iter; } if (rc < 0) return OATH_STRCMP_ERROR; if (iter > 0) { rc = oath_hotp_generate (secret, secret_length, nts - iter, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, tmp_otp); if (rc != OATH_OK) return rc; if ((rc = strcmp_otp (strcmp_handle, tmp_otp)) == 0) { if (otp_pos) *otp_pos = -iter; return iter; } if (rc < 0) return OATH_STRCMP_ERROR; } } while (window - iter++ > 0); return OATH_INVALID_OTP; }
int main (int argc, char *argv[]) { struct gengetopt_args_info args_info; char *secret; size_t secretlen = 0; int rc; size_t window; uint64_t moving_factor; unsigned digits; char otp[10]; time_t now, when, t0, time_step_size; int totpflags = 0; set_program_name (argv[0]); if (cmdline_parser (argc, argv, &args_info) != 0) return EXIT_FAILURE; if (args_info.version_given) { char *p; int l = -1; if (strcmp (oath_check_version (NULL), OATH_VERSION) != 0) l = asprintf (&p, "OATH Toolkit liboath.so %s oath.h %s", oath_check_version (NULL), OATH_VERSION); else if (strcmp (OATH_VERSION, PACKAGE_VERSION) != 0) l = asprintf (&p, "OATH Toolkit %s", oath_check_version (NULL), OATH_VERSION); version_etc (stdout, "oathtool", l == -1 ? "OATH Toolkit" : p, PACKAGE_VERSION, "Simon Josefsson", (char *) NULL); if (l != -1) free (p); return EXIT_SUCCESS; } if (args_info.help_given) usage (EXIT_SUCCESS); if (args_info.inputs_num == 0) { cmdline_parser_print_help (); emit_bug_reporting_address (); return EXIT_SUCCESS; } rc = oath_init (); if (rc != OATH_OK) error (EXIT_FAILURE, 0, "liboath initialization failed: %s", oath_strerror (rc)); if (args_info.base32_flag) { rc = oath_base32_decode (args_info.inputs[0], strlen (args_info.inputs[0]), &secret, &secretlen); if (rc != OATH_OK) error (EXIT_FAILURE, 0, "base32 decoding failed: %s", oath_strerror (rc)); } else { secretlen = 1 + strlen (args_info.inputs[0]) / 2; secret = malloc (secretlen); if (!secret) error (EXIT_FAILURE, errno, "malloc"); rc = oath_hex2bin (args_info.inputs[0], secret, &secretlen); if (rc != OATH_OK) error (EXIT_FAILURE, 0, "hex decoding of secret key failed"); } if (args_info.counter_orig) moving_factor = args_info.counter_arg; else moving_factor = 0; if (args_info.digits_orig) digits = args_info.digits_arg; else digits = 6; if (args_info.window_orig) window = args_info.window_arg; else window = 0; if (digits != 6 && digits != 7 && digits != 8) error (EXIT_FAILURE, 0, "only digits 6, 7 and 8 are supported"); if (validate_otp_p (args_info.inputs_num) && !args_info.digits_orig) digits = strlen (args_info.inputs[1]); else if (validate_otp_p (args_info.inputs_num) && args_info.digits_orig && args_info.digits_arg != strlen (args_info.inputs[1])) error (EXIT_FAILURE, 0, "given one-time password has bad length %d != %ld", args_info.digits_arg, strlen (args_info.inputs[1])); if (args_info.inputs_num > 2) error (EXIT_FAILURE, 0, "too many parameters"); if (args_info.verbose_flag) { char *tmp; tmp = malloc (2 * secretlen + 1); if (!tmp) error (EXIT_FAILURE, errno, "malloc"); oath_bin2hex (secret, secretlen, tmp); printf ("Hex secret: %s\n", tmp); free (tmp); rc = oath_base32_encode (secret, secretlen, &tmp, NULL); if (rc != OATH_OK) error (EXIT_FAILURE, 0, "base32 encoding failed: %s", oath_strerror (rc)); printf ("Base32 secret: %s\n", tmp); free (tmp); if (args_info.inputs_num == 2) printf ("OTP: %s\n", args_info.inputs[1]); printf ("Digits: %d\n", digits); printf ("Window size: %ld\n", window); } if (args_info.totp_given) { now = time (NULL); when = parse_time (args_info.now_arg, now); t0 = parse_time (args_info.start_time_arg, now); time_step_size = parse_duration (args_info.time_step_size_arg); if (when == BAD_TIME) error (EXIT_FAILURE, 0, "cannot parse time `%s'", args_info.now_arg); if (t0 == BAD_TIME) error (EXIT_FAILURE, 0, "cannot parse time `%s'", args_info.start_time_arg); if (time_step_size == BAD_TIME) error (EXIT_FAILURE, 0, "cannot parse time `%s'", args_info.time_step_size_arg); if (strcmp (args_info.totp_arg, "sha256") == 0) totpflags = OATH_TOTP_HMAC_SHA256; else if (strcmp (args_info.totp_arg, "sha512") == 0) totpflags = OATH_TOTP_HMAC_SHA512; if (args_info.verbose_flag) verbose_totp (t0, time_step_size, when); } else { if (args_info.verbose_flag) verbose_hotp (moving_factor); } if (generate_otp_p (args_info.inputs_num) && !args_info.totp_given) { size_t iter = 0; do { rc = oath_hotp_generate (secret, secretlen, moving_factor + iter, digits, false, OATH_HOTP_DYNAMIC_TRUNCATION, otp); if (rc != OATH_OK) error (EXIT_FAILURE, 0, "generating one-time password failed (%d)", rc); printf ("%s\n", otp); } while (window - iter++ > 0); } else if (generate_otp_p (args_info.inputs_num) && args_info.totp_given) { size_t iter = 0; do { rc = oath_totp_generate2 (secret, secretlen, when + iter * time_step_size, time_step_size, t0, digits, totpflags, otp); if (rc != OATH_OK) error (EXIT_FAILURE, 0, "generating one-time password failed (%d)", rc); printf ("%s\n", otp); } while (window - iter++ > 0); } else if (validate_otp_p (args_info.inputs_num) && !args_info.totp_given) { rc = oath_hotp_validate (secret, secretlen, moving_factor, window, args_info.inputs[1]); if (rc == OATH_INVALID_OTP) error (EXIT_OTP_INVALID, 0, "password \"%s\" not found in range %ld .. %ld", args_info.inputs[1], (long) moving_factor, (long) moving_factor + window); else if (rc < 0) error (EXIT_FAILURE, 0, "validating one-time password failed (%d)", rc); printf ("%d\n", rc); } else if (validate_otp_p (args_info.inputs_num) && args_info.totp_given) { rc = oath_totp_validate4 (secret, secretlen, when, time_step_size, t0, window, NULL, NULL, totpflags, args_info.inputs[1]); if (rc == OATH_INVALID_OTP) error (EXIT_OTP_INVALID, 0, "password \"%s\" not found in range %ld .. %ld", args_info.inputs[1], (long) ((when - t0) / time_step_size - window / 2), (long) ((when - t0) / time_step_size + window / 2)); else if (rc < 0) error (EXIT_FAILURE, 0, "validating one-time password failed (%d)", rc); printf ("%d\n", rc); } free (secret); oath_done (); return EXIT_SUCCESS; }