All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.turingcomplete.kotlinonetimepassword.TimeBasedOneTimePasswordGenerator.kt Maven / Gradle / Ivy

Go to download

A Kotlin one-time password library to generate "Google Authenticator", "Time-based One-time Password" (TOTP) and "HMAC-based One-time Password" (HOTP) codes based on RFC 4226 and 6238.

There is a newer version: 2.4.1
Show newest version
package dev.turingcomplete.kotlinonetimepassword

import java.time.Instant
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.floor

/**
 * Generator for the RFC 6238 "TOTP: Time-Based One-Time Password Algorithm"
 * (https://tools.ietf.org/html/rfc6238)
 *
 * @property secret the shared secret as a byte array.
 * @property config the [TimeBasedOneTimePasswordConfig] for this generator.
 */
open class TimeBasedOneTimePasswordGenerator(private val secret: ByteArray, private val config: TimeBasedOneTimePasswordConfig) {

  private val hmacOneTimePasswordGenerator: HmacOneTimePasswordGenerator = HmacOneTimePasswordGenerator(secret, config)

  /**
   * Calculate the current time slot.
   *
   * The timeslot is basically the number of `timeStep`s from
   * [TimeBasedOneTimePasswordConfig] which fits into the [timestamp].
   *
   * @param timestamp The Unix timestamp against the counting of the time
   * steps is calculated. The default value is the current system time from
   * [System.currentTimeMillis].
   */
  fun counter(timestamp: Long = System.currentTimeMillis()):Long = if (config.timeStep == 0L) {
    0 // To avoid a divide by zero exception
  }
  else {
    floor(timestamp.toDouble()
      .div(TimeUnit.MILLISECONDS.convert(config.timeStep, config.timeStepUnit).toDouble()))
      .toLong()
  }

  fun counter(date: Date):Long = counter(date.time)
  fun counter(instant: Instant):Long = counter(instant.toEpochMilli())

  /**
   * Calculates the start of the given time slot.
   *
   * This is basically the reverse calculation of counter(timestamp) method.
   *
   * @param counter The counter representing the time slot.
   * @return The Unix timestamp where the given time slot starts.
   */
  fun timeslotStart(counter:Long):Long {
    val timeStepMillis = TimeUnit.MILLISECONDS.convert(config.timeStep, config.timeStepUnit).toDouble()
    return (counter * timeStepMillis).toLong()
  }

  /**
   * Generates a code representing the time-based one-time password.
   *
   * The TOTP algorithm uses the HTOP algorithm via [HmacOneTimePasswordGenerator.generate],
   * with a counter parameter that represents the number of `timeStep`s from
   * [TimeBasedOneTimePasswordConfig] which fits into the [timestamp].
   *
   * The timestamp can be seen as the challenge to be solved. This should
   * normally be a continuous value over time (e.g. the current time).
   *
   * @param timestamp The Unix timestamp against the counting of the time
   * steps is calculated. The default value is the current system time from
   * [System.currentTimeMillis].
   */
  fun generate(timestamp: Long = System.currentTimeMillis()): String =
    hmacOneTimePasswordGenerator.generate(counter(timestamp))

  fun generate(date: Date = Date(System.currentTimeMillis())):String = generate(date.time)
  fun generate(instant: Instant = Instant.now()):String = generate(instant.toEpochMilli())

  /**
   * Validates the given code.
   *
   * @param code the code calculated from the challenge to validate.
   * @param timestamp the used challenge for the code. The default value is the
   *                  current system time from [System.currentTimeMillis].
   */
  fun isValid(code: String, timestamp: Long = System.currentTimeMillis()): Boolean {
    return code == generate(timestamp)
  }

  fun isValid(code: String, date: Date = Date(System.currentTimeMillis())) = isValid(code, date.time)
  fun isValid(code: String, instant: Instant = Instant.now()) = isValid(code, instant.toEpochMilli())
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy