Logo Search packages:      
Sourcecode: oath-toolkit version File versions  Download package

totp.c

/*
 * totp.c - implementation of the OATH TOTP algorithm
 * Copyright (C) 2011 Simon Josefsson
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include <config.h>

#include "oath.h"
#include "aux.h" /* _oath_strcmp_callback */

/**
 * 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);
}

/**
 * oath_totp_validate:
 * @secret: the shared secret string
 * @secret_length: length of @secret
 * @now: Unix time value to validate TOTP for
 * @time_step_size: time step system parameter (typically 30)
 * @start_offset: Unix time of when to start counting time steps (typically 0)
 * @window: how many OTPs after/before start OTP to test
 * @otp: the OTP to validate.
 *
 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
 *
 * 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.6.0
 **/
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)
{
  return oath_totp_validate2 (secret, secret_length, now, time_step_size,
                        start_offset, window, NULL, otp);
}

/**
 * oath_totp_validate_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)
 * @window: how many OTPs after start counter to test
 * @digits: number of requested digits in the OTP
 * @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 behave like strcmp, i.e., only ever return 0 on
 * matches.
 *
 * 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 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.6.0
 **/
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)
{
  return oath_totp_validate2_callback (secret, secret_length, now,
                               time_step_size, start_offset,
                               digits, window, NULL,
                               strcmp_otp, strcmp_handle);
}

/**
 * oath_totp_validate2:
 * @secret: the shared secret string
 * @secret_length: length of @secret
 * @now: Unix time value to validate TOTP for
 * @time_step_size: time step system parameter (typically 30)
 * @start_offset: Unix time of when to start counting time steps (typically 0)
 * @window: how many OTPs after/before start OTP to test
 * @otp_pos: output search position in search window (may be NULL).
 * @otp: the OTP to validate.
 *
 * Validate an OTP according to OATH TOTP algorithm per RFC 6238.
 *
 * 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 (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)
{
  return oath_totp_validate2_callback (secret, secret_length, now,
                               time_step_size, start_offset,
                               strlen (otp), window, otp_pos,
                               _oath_strcmp_callback, (void *) otp);
}

/**
 * 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 behave like strcmp, i.e., only ever return 0 on
 * matches.
 *
 * 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 (strcmp_otp (strcmp_handle, tmp_otp) == 0)
      {
        if (otp_pos)
          *otp_pos = iter;
        return iter;
      }

      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 (strcmp_otp (strcmp_handle, tmp_otp) == 0)
          {
            if (otp_pos)
            *otp_pos = -iter;
            return iter;
          }
      }
    }
  while (window - iter++ > 0);

  return OATH_INVALID_OTP;
}

Generated by  Doxygen 1.6.0   Back to index