package com.infendro.otp import com.infendro.bytearray.toByteArray import com.infendro.bytearray.toInt import com.infendro.hash.HashFunction import com.infendro.hash.SHA1 import com.infendro.mac.HMAC import kotlin.experimental.and import kotlin.math.pow class HOTP( val function: HashFunction = SHA1, val length: Int = 6, ) { fun verify( secret: ByteArray, counter: Long, otp: String, ): Boolean { return generate(secret, counter) == otp } fun generate( secret: ByteArray, counter: Long, ): String { if (counter < 0) throw Exception() val hash = generateHash( secret, counter.toByteArray() ) return otp(hash) } private fun generateHash( secret: ByteArray, value: ByteArray, ): ByteArray { return HMAC(function).hash(secret, value) } private fun otp( hash: ByteArray, ): String { val offset = (hash.last() and 0xF).toInt() var truncatedHash = hash .drop(offset) .take(4) .toInt() and 0x7FFFFFFF truncatedHash %= 10.0.pow(length).toInt() return truncatedHash.toString() .padStart(length, '0') } }