Native Kotlin library for time-based TOTP and HMAC-based HOTP one-time passwords

Overview

totp-kt - Kotlin OTP Library

Visit the documentation at: https://robinohs.github.io/totp-kt/

CURRENTLY IN ALPHA v2

SonarCloud

License: MIT CircleCI codecov Vulnerabilities Security Rating Quality Gate Status Code Smells deploy-docu

Native Kotlin library for time-based TOTP and HMAC-based HOTP one-time passwords. Enables the developer to:

  • validate and generate TOTP (RFC 6238) and HOTP (RFC 4226) one-time passwords,
  • generate randomly secure secrets to use with authenticators,
  • generate randomly secure recovery codes.

Navigation

Installation

Only Jitpack is supported in the alpha phase.

Jitpack

If you are using Jitpack as a repository, you can follow one of the following sections to install using with your favorite package manager such as gradle or maven.

Kotlin DSL

Add Jitpack to repositories:

//build.gradle.kts
repositories {  
  mavenCentral()  
  maven { url = uri("https://jitpack.io") }  
}

Add the dependency:

//build.gradle.kts
dependencies {
  implementation("com.github.robinohs:totp-kt:v2.0.0-alpha")
}

Maven

Add Jitpack to repositories:


<repositories>
  <repository>
    <id>jitpack.ioid>
    <url>https://jitpack.iourl>
  repository>
repositories>

Add the dependency:


<dependency>
  <groupId>com.github.robinohsgroupId>
    <artifactId>totp-ktartifactId>
    <version>v2.0.0-alphaversion>
dependency>

Gradle

Add Jitpack to repositories:

//build.gradle
allprojects {
  repositories {
    //...
    maven { url 'https://jitpack.io' }
  }
}

Add the dependency:

//build.gradle
dependencies {
  implementation 'com.github.robinohs:totp-kt:v2.0.0-alpha'
}

Usage

TOTP (Time-based One-Time Password)

The time-based one-time password method, generates one-time passwords by using a shared secret in combination with a time window as the source of uniqueness. The TOTP algorithm is an extension of HOTP. The algorithm is used by commonly known authenticator apps, e.g. Google Authenticator, Mircrosoft Authenticator and others.

TOTP flow

sequenceDiagram
participant Client
participant Server
Client -> Server: shared secret
Client ->> Server: login (name: xy, password: xy)
Server ->> Client : 401 TOTP required
Client ->> Client: Client generates TOTP
Client ->> Server: login (name: xy, password: xy, totp: 564867)
Server ->> Server: Server generates TOTP
Server ->> Server: Is client TOTP the same?
Server -->> Client: If equal: JWT (session, ...)
Server -->> Client: If different: 401, BadCredentials

Create a TOTP generator

You can create an instance of the TotpGenerator in the following way:

val totpGenerator = TotpGenerator()

Use the TOTP generator

Method: Generate Code

After you created the totpGenerator instance you can generate a one-time password by calling the generatore code method with the secret as an argument. Optionally, if you want to specify a specific time and not have the generator to take the current time itself, you can pass a time as an argument.

val secret = some_base32_encoded_secret_as_bytearray
val code = totpGenerator.generateCode(secret)

If one would like to specify a time:

// with millis
totpGenerator.generateCode(secret, 1656459878681)
// with Instant
totpGenerator.generateCode(secret, Instant())
// with Date
totpGenerator.generateCode(secret, Date())

Method: Validate Code

There is a helper function to compare a currently generated code with a given code. Optionally, you can also use generateCode yourself and compare the resulting string to the client's code.

val secret = some_base32_encoded_secret_as_bytearray
val clientCode = given_client_code
totpGenerator.isCodeValid(secret, clientCode)

If one would like to specify a time:

// with millis
totpGenerator.isCodeValid(secret, 1656459878681, clientCode)
// with Instant
totpGenerator.isCodeValid(secret, Instant(), clientCode)
// with Date
totpGenerator.isCodeValid(secret, Date(), clientCode)

Method: Validate Code With Tolerance

Compares a generated code to a given code using a counter derived from the current timestamp and a given secret. In addition, the method considers a tolerance and also checks the given code against a number of previous tokens equal to the tolerance. Returns true if the given code matches any of these tokens.

val secret = some_base32_encoded_secret_as_bytearray
val clientCode = given_client_code
totpGenerator.isCodeValidWithTolerance(secret, clientCode)

If one would like to specify a time:

// with millis
totpGenerator.isCodeValidWithTolerance(secret, 1656459878681, clientCode)
// with Instant
totpGenerator.isCodeValidWithTolerance(secret, Instant(), clientCode)
// with Date
totpGenerator.isCodeValidWithTolerance(secret, Date(), clientCode)

Method: Timeslot beginning

Calculates the start timestamp of the time slot in which the actual or given timestamp lies.

// takes actual timestamp from clock, returns millis
totpGenerator.calculateTimeslotBeginning()

If one would like to specify a time:

// with millis, returns millis
totpGenerator.calculateTimeslotBeginning(1656459878681, clientCode)
// with Instant, returns Instant
totpGenerator.calculateTimeslotBeginning(Instant(), clientCode)
// with Date, returns Date
totpGenerator.calculateTimeslotBeginning(Date(), clientCode)

Method: Remaining time

Calculates the remaining duration of the time slot in which the actual or given timestamp lies.

// takes actual timestamp from clock
totpGenerator.calculateRemainingTime()

If one would like to specify a time:

// with millis
totpGenerator.calculateRemainingTime(1656459878681)
// with Instant
totpGenerator.calculateRemainingTime(Instant())
// with Date
totpGenerator.calculateRemainingTime(Date())

Customize properties

It is possible to customize the properties of the generator, either by setters or applying them in the constructor.

Clock

The clock is the time source for the generator if no time is passed as an argument to the generateCode or validateCode method.

val totpGenerator = TotpGenerator(clock = Clock.systemUTC())
// or
totpGenerator.clock = Clock.systemUTC()

For testing purposes, one could assign a Clock.fixed that always returns the same timestamp and thus the same TOTP code.

Time period

The time period is the duration of every time step in which the generated code is the same. This is needed as due to delays (e.g., network delay) the server will not generate the code with the same timestamp as the client. As a compromise between security and usability the default time step is set as 30 seconds.

A time period of 30 seconds is used by the Google or Mircrosoft Authenticator app.

Tolerance

The tolerance specifies a number of previous tokens that are also accepted as valid tokens in addition to the current valid token.

A tolerance of 1 token is set as default. In RFC6238#5.2 a time step is recommended to compensate for delays such as network delay.

Code length

The code length specifies how long a generated code will be. If the code length is changed, it is necessary that the user's authenticator app supports this as well.

A length of 6 digits is used by the Google or Microsoft Authenticator app.

HOTP (HMAC-based One-Time Password)

The HMAC-based one-time password method generates one-time passwords by using a shared secret in combination with a counter as the source of uniqueness. The major problem of this approach is the synchronization of the counter between the client and the server. Synchronization is out of scope for this library and therefore needs to be implemented by the consumer. A method for re-synchronization is described in the specification RFC4226#7.4.

HOTP flow

sequenceDiagram
participant Client
participant Server
Client -> Server: shared secret + counter
Client ->> Server: login (name: xy, password: xy)
Server ->> Client : 401 HOTP required
Client ->> Client: Client generates HOTP with counter
Client ->> Server: login (name: xy, password: xy, hotp: 564867)
Server ->> Server: Server generates HOTP with counter
Server ->> Server: Is client HOTP the same?
Server -->> Client: If equal: JWT (session, ...)
Server -->> Client: If different: 401, BadCredentials

Create a HOTP generator

You can create an instance of the HotpGenerator in the following way:

val hotpGenerator = HotpGenerator()

Use the HOTP generator

Method: Generate Code

After you created the hotpGenerator instance you can generate a one-time password by calling the generatore code method with the secret and the counter as arguments.

val secret = some_base32_encoded_secret_as_bytearray
val counter = some_number
val code = hotpGenerator.generateCode(secret, counter)

Method: Validate Code

There is a helper function to compare a generated code with a given code. Optionally, you could also use generateCode yourself and compare the resulting string to the client's code.

val secret = some_base32_encoded_secret_as_bytearray
val counter = some_number
val clientCode = given_client_code
totpGenerator.isCodeValid(secret, counter, clientCode)

Customize properties

It is possible to customize the properties of the generator, either by setters or applying them in the constructor.

Code length

The code length specifies how long a generated code will be. If the code length is changed, it is necessary that the user's authenticator app supports this as well.

Secret generator

The secret generator can be used to generate base32 encoded secrets as strings and bytearrays.

Create a Secret generator

You can create an instance of the SecretGenerator in the following way:

val secretGenerator = SecretGenerator()

Use the Secret generator

Method: Generate Secret

If you want to generate a secret that can be used as a shared secret between the client and the server, there is the generateSecret function. The default behavior of the function is to generate a 10 character secret and convert it to a Base32Secret instance. Optionally you can specify the length of the plain input to the base32 encoding secret.

val base32Secret: Base32Secret = secretGenerator.generateSecret()

Data Class: Base32Secret

The Base32Secret data class contains a secret in the form of a bytearray and a string.

val base32Secret: Base32Secret = secretGenerator.generateSecret()
val (secretAsString, secretAsByteArray) = base32Secret

Customize properties

It is possible to customize the properties of the generator, either by setters or applying them in the constructor.

Used Random generator

The RandomGenerator instance used internally to generate random strings.

Recovery-Code generator

This generator can be used to create a randomly generated string in block form.

Create a Recovery-Code generator

You can create an instance of the RecoveryCodeGenerator in the following way:

val recoveryCodeGenerator = RecoveryCodeGenerator()

Use the Recovery-Code generator

Method: Generate Recovery-Code

This method generates a single recovery-code.

val recoveryCode = recoveryCodeGenerator.generateRecoveryCode()
println(recoveryCode)
"AAAA-BBBB-CCCC-DDDD"

Method: Generate a list of Recovery-Codes

This method generates a list of recovery-codes.

val recoveryCodes = recoveryCodeGenerator.generateRecoveryCodes(3)
println(recoveryCodes)
["AAAA-BBBB-CCCC-DDDD", "BBBB-AAAA-CCCC-DDDD", "BBBB-AAAA-DDDD-CCCC"]

Customize properties

It is possible to customize the properties of the generator, either by setters or applying them in the constructor.

Number of blocks

Specifies the number of blocks that make up each recovery code.

Block length

Specifies the length of each block in each recovery code.

Used Random generator

The RandomGenerator instance used internally to generate random strings.

Random generator

The random generator is internally used by the SecretGenerator and RecoveryCodeGenerator to create randomly secure strings.

Customize properties

It is possible to customize the properties of the generator, either by setters or applying them in the constructor.

Random (Dangerous)

The generator accepts any class implementing the java random interface.

The default is the SecureRandom implementation and should not be changed unless you know what you are doing!

Charpool (Dangerous)

The char pool specifies the list of characters that can be used to generate the string.

If the char pool gets too small, the security is weakend. For example a 10-character long password with the default charset of the library has the following properties:$$ Combinations: 62^{10} = 8.3929937e^{17}\ Entropy: log_2(62^{10}) = 59.542 $$ Passwords with a entropy >50 are considered to be secure.

Spring Boot

Instead of creating a new instance of a generator each time a token is checked, it is also possible to create a bean within Spring. This allows to configure the generator once and this configuration is maintained each time the bean is injected into a component.

@Bean  
fun totpGenerator(): TotpGenerator {  
    val generator = TotpGenerator()  
    generator.codeLength = 9
    generator.timePeriod = Duration.ofSeconds(20)
    return generator
}  
  
@Bean  
fun recoveryCodeGenerator(): RecoveryCodeGenerator {  
    val generator = RecoveryCodeGenerator()
    generator.blockLength = 5
    return generator
}

This bean can then be injected in the constructor of any class marked with @Component (@Service, ...).

@Component  
class CustomComponent(  
  private val totpGenerator: TotpGenerator,
  private val recoveryCodeGenerator: RecoveryCodeGenerator
) {
	//...
}

License

MIT

You might also like...
React Native wrapper to bridge our iOS and Android SDK
React Native wrapper to bridge our iOS and Android SDK

React Native wrapper to bridge our iOS and Android SDK

Routable, an in-app native URL router, for Android

Routable Routable is an in-app native URL router, for Android. Also available for iOS. Usage Set up your app's router and URLs: import com.usepropelle

SoLoader is a native code loader for Android.

SoLoader is a native code loader for Android. It takes care of unpacking your native libraries and recursively loads dependencies on platforms that don't support that out of the box.

Joda-Time library with Android specialization

joda-time-android This library is a version of Joda-Time built with Android in mind. Why Joda-Time? Android has built-in date and time handling - why

A Kotlin-based testing/scraping/parsing library providing the ability to analyze and extract data from HTML
A Kotlin-based testing/scraping/parsing library providing the ability to analyze and extract data from HTML

A Kotlin-based testing/scraping/parsing library providing the ability to analyze and extract data from HTML (server & client-side rendered). It places particular emphasis on ease of use and a high level of readability by providing an intuitive DSL. It aims to be a testing lib, but can also be used to scrape websites in a convenient fashion.

A lightning fast, transactional, file-based FIFO for Android and Java.

Tape by Square, Inc. Tape is a collection of queue-related classes for Android and Java. QueueFile is a lightning-fast, transactional, file-based FIFO

🐫🐍🍢🅿 Multiplatform Kotlin library to convert strings between various case formats including Camel Case, Snake Case, Pascal Case and Kebab Case

KaseChange Multiplatform Kotlin library to convert strings between various case formats Supported Case Formats SCREAMING_SNAKE_CASE snake_case PascalC

A library for fast and safe delivery of parameters for Activities and Fragments.

MorbidMask - 吸血面具 Read this in other languages: 中文, English, Change Log A library for fast and safe delivery of parameters for Activities and Fragment

A simple and easy to use stopwatch and timer library for android

TimeIt Now with Timer support! A simple and easy to use stopwatch and timer library for android Introduction A stopwatch can be a very important widge

Comments
  • Release of support for HashingAlgorithms.

    Release of support for HashingAlgorithms.

    Merge dev into main:

    • add support to specify used hashing algorithm for HMAC creation (SHA1, SHA256, SHA512)
    • add method to secret generator to generate keys with preferred length for the different algorithms.
    enhancement 
    opened by robinohs 1
Releases(v1.0.0)
  • v1.0.0(Jul 15, 2022)

    First major release.

    What's Changed

    • Release of support for HashingAlgorithms. by @robinohs in https://github.com/robinohs/totp-kt/pull/4

    Full Changelog: https://github.com/robinohs/totp-kt/compare/v2.0.0-alpha...v1.0.0

    Source code(tar.gz)
    Source code(zip)
Owner
Robin Ohs
Saarland University // Master program Computer Science // B.Sc. in Cybersecurity
Robin Ohs
Native solution for common React Native problem of focused views being covered by soft input view.

react-native-avoid-softinput Native solution for common React Native problem of focused views being covered by soft input view. It is solved by listen

Mateusz Mędrek 312 Jan 2, 2023
Multiplaform kotlin library for calculating text differences. Based on java-diff-utils, supports JVM, JS and native targets.

kotlin-multiplatform-diff This is a port of java-diff-utils to kotlin with multiplatform support. All credit for the implementation goes to original a

Peter Trifanov 51 Jan 3, 2023
Gitversion - A native console application to calculate a version based on git commits and tags

GitCommit A native console application to calculate a version based on git commi

Solugo 5 Sep 13, 2022
Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Shahid Iqbal 4 Nov 29, 2022
A robust native library loader for Android.

ReLinker A robust native library loader for Android. More information can be found in our blog post Min SDK: 9 JavaDoc Overview The Android PackageMan

Keepsafe 2.9k Dec 27, 2022
Fuzzy string matching for Kotlin (JVM, native, JS, Web Assembly) - port of Fuzzy Wuzzy Python lib

FuzzyWuzzy-Kotlin Fuzzy string matching for Kotlin (JVM, iOS) - fork of the Java fork of of Fuzzy Wuzzy Python lib. For use in on JVM, Android, or Kot

WillowTree, LLC 54 Nov 8, 2022
A Kotlin native Postgres driver for SqlDelight.

Module postgres-native-sqldelight A native Postgres driver for SqlDelight. Source code Install This package is uploaded to MavenCentral and supports m

Philip Wedemann 19 Dec 30, 2022
Desafio da SantanderDevWeek, junto com a Digital Innovation One,

Layout's em Português e Inglês SantanderDevWeek A Santander Dev-Week, foi uma semana imersiva em que aborda assuntos sobre Full-Stack e Mobile, tecnol

Mateus de Mendonça Pereira 4 Jul 24, 2021
Migrating from one PostgreSQL to another via S3

Migrating from one PostgreSQL to another via S3 In one terminal start initial setup. ./gradlew buildDockerImage docker-compose up --build dbmig-s3 dbm

Stefan Bissell 1 May 19, 2022
Small command-line utility to safely merge multiple WhatsApp backups (msgstore.db) into one.

whatsapp-database-merger A small command-line utility that can be used to merge multiple WhatsApp databases (msgstore.db) into one. A few notes: input

Mattia Iavarone 31 Dec 27, 2022