Skip to content

Commit

Permalink
Fixing linting.
Browse files Browse the repository at this point in the history
  • Loading branch information
diegoocampoh committed Dec 7, 2023
1 parent 596444a commit b0da648
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 275 deletions.
15 changes: 8 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ tasks.withType<JavaCompile> {
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs += listOf(
"-progressive",
"-java-parameters",
"-opt-in=kotlin.time.ExperimentalTime",
"-opt-in=kotlin.RequiresOptIn"
)
freeCompilerArgs +=
listOf(
"-progressive",
"-java-parameters",
"-opt-in=kotlin.time.ExperimentalTime",
"-opt-in=kotlin.RequiresOptIn",
)

// https://youtrack.jetbrains.com/issue/KTIJ-1224
// This is really an IDE bug.
Expand Down Expand Up @@ -115,7 +116,7 @@ tasks {
signing {
useInMemoryPgpKeys(
System.getenv("SIGNING_KEY"),
System.getenv("SIGNING_PASSWORD")
System.getenv("SIGNING_PASSWORD"),
)
sign(publishing.publications["release"])
}
Expand Down
41 changes: 24 additions & 17 deletions src/main/kotlin/com/atlassian/onetime/core/HOTPGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,23 @@ data class HOTP(val value: String)
enum class HMACDigest(val value: HmacAlgorithms) {
SHA1(HmacAlgorithms.HMAC_SHA_1),
SHA256(HmacAlgorithms.HMAC_SHA_256),
SHA512(HmacAlgorithms.HMAC_SHA_512);
SHA512(HmacAlgorithms.HMAC_SHA_512),
;

fun toQueryParam(): String = when (this) {
SHA1 -> "SHA1"
SHA256 -> "SHA256"
SHA512 -> "SHA512"
}
fun toQueryParam(): String =
when (this) {
SHA1 -> "SHA1"
SHA256 -> "SHA256"
SHA512 -> "SHA512"
}
}

enum class OTPLength(val value: Int) {
SIX(6),
SEVEN(7),
EIGHT(8),
NINE(9),
TEN(10)
TEN(10),
}

/**
Expand All @@ -34,17 +36,20 @@ enum class OTPLength(val value: Int) {
*/
abstract class OTPGenerator(
open val otpLength: OTPLength,
open val digest: HMACDigest
open val digest: HMACDigest,
) {

private val modulusOperand: Int by lazy {
10.0.pow(otpLength.value).toInt()
}

protected fun generateOtp(key: ByteArray, counter: Long): HOTP {
val hmacOut = HmacUtils
.getInitializedMac(digest.value, key)
.doFinal(counter.toByteArray())
protected fun generateOtp(
key: ByteArray,
counter: Long,
): HOTP {
val hmacOut =
HmacUtils
.getInitializedMac(digest.value, key)
.doFinal(counter.toByteArray())
val intOtp = truncate(hmacOut).rem(modulusOperand)
return HOTP(String.format("%0${otpLength.value}d", intOtp))
}
Expand All @@ -54,7 +59,7 @@ abstract class OTPGenerator(
ByteBuffer.wrap(
hmac.slice(this..this + 3).toByteArray().apply {
set(0, get(0).and(0x7f))
}
},
).int
}
}
Expand All @@ -63,8 +68,10 @@ private fun Long.toByteArray(): ByteArray = ByteBuffer.allocate(8).putLong(0, th

class HOTPGenerator(
otpLength: OTPLength = OTPLength.SIX,
digest: HMACDigest = HMACDigest.SHA1
digest: HMACDigest = HMACDigest.SHA1,
) : OTPGenerator(otpLength, digest) {

fun generate(key: ByteArray, counter: Long): HOTP = generateOtp(key, counter)
fun generate(
key: ByteArray,
counter: Long,
): HOTP = generateOtp(key, counter)
}
9 changes: 6 additions & 3 deletions src/main/kotlin/com/atlassian/onetime/core/TOTPGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ class TOTPGenerator(
val startTime: Int = 0,
val timeStepSeconds: Int = 30,
override val otpLength: OTPLength = OTPLength.SIX,
override val digest: HMACDigest = HMACDigest.SHA1
override val digest: HMACDigest = HMACDigest.SHA1,
) : OTPGenerator(otpLength, digest) {

/**
* Generates the TOTPs given the [totpSecret] with delay steps and future steps.
* [delaySteps] (positive) number of past time steps that are to be generated
Expand All @@ -26,7 +25,11 @@ class TOTPGenerator(
* Returns a list of TOTPs. This will contain all past steps (if [delaySteps] > 0),
* followed by the current step and then possibly followed by all future TOTPs if specified.
*/
fun generate(totpSecret: TOTPSecret, delaySteps: Int = 0, futureSteps: Int = 0): List<TOTP> {
fun generate(
totpSecret: TOTPSecret,
delaySteps: Int = 0,
futureSteps: Int = 0,
): List<TOTP> {
val step: Long = ((clock.millis() / 1000) - startTime) / timeStepSeconds
return ((step - delaySteps until step) + step + (step + 1..step + futureSteps))
.map { generateOtp(totpSecret.value, it) }
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/com/atlassian/onetime/model/TOTPSecret.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.atlassian.onetime.model
import org.apache.commons.codec.binary.Base32

data class TOTPSecret(val value: ByteArray) {

val base32Encoded: String = Base32().encodeToString(this.value)

override fun equals(other: Any?): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,25 @@ import com.atlassian.onetime.model.TOTPSecret
import java.util.concurrent.CompletableFuture

class AsciiRangeSecretProvider : SecretProvider {

companion object {
private val ASCII_RANGE: CharRange = (' '..'z')

fun generateSecret() = TOTPSecret(
(1..20).map { ASCII_RANGE.random() }.joinToString("").toByteArray()
)
fun generateSecret() =
TOTPSecret(
(1..20).map { ASCII_RANGE.random() }.joinToString("").toByteArray(),
)
}

override fun generateSecret() = AsciiRangeSecretProvider.generateSecret()
}

class AsyncAsciiRangeSecretProvider : AsyncSecretProvider {

override fun generateSecret(): CompletableFuture<TOTPSecret> =
CompletableFuture.supplyAsync {
AsciiRangeSecretProvider.generateSecret()
}
}

class CPSAsciiRangeSecretProvider : CPSSecretProvider {

override suspend fun generateSecret(): TOTPSecret =
AsciiRangeSecretProvider.generateSecret()
override suspend fun generateSecret(): TOTPSecret = AsciiRangeSecretProvider.generateSecret()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import java.security.SecureRandom
import java.util.concurrent.CompletableFuture

class RandomSecretProvider : SecretProvider {

companion object {
fun generateSecret() =
SecureRandom().let {
Expand All @@ -19,14 +18,12 @@ class RandomSecretProvider : SecretProvider {
}

class AsyncRandomSecretProvider : AsyncSecretProvider {

override fun generateSecret(): CompletableFuture<TOTPSecret> =
CompletableFuture.supplyAsync {
RandomSecretProvider.generateSecret()
}
}

class CPSRandomSecretProvider : CPSSecretProvider {

override suspend fun generateSecret(): TOTPSecret = RandomSecretProvider.generateSecret()
}
37 changes: 19 additions & 18 deletions src/main/kotlin/com/atlassian/onetime/service/TOTPService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@ import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract

interface TOTPService {

fun generateTOTPUrl(
totpSecret: TOTPSecret,
emailAddress: EmailAddress,
issuer: Issuer
issuer: Issuer,
): URI

fun verify(
code: TOTP,
totpSecret: TOTPSecret
totpSecret: TOTPSecret,
): TOTPVerificationResult
}

data class TOTPConfiguration(
val allowedPastSteps: Int = 0,
val allowedFutureSteps: Int = 0
val allowedFutureSteps: Int = 0,
)

sealed class TOTPVerificationResult {
object InvalidTotp : TOTPVerificationResult()

data class Success(val index: Int) : TOTPVerificationResult()

@OptIn(ExperimentalContracts::class)
Expand All @@ -48,9 +48,8 @@ sealed class TOTPVerificationResult {

class DefaultTOTPService(
private val totpGenerator: TOTPGenerator = TOTPGenerator(),
private val totpConfiguration: TOTPConfiguration = TOTPConfiguration()
private val totpConfiguration: TOTPConfiguration = TOTPConfiguration(),
) : TOTPService {

companion object {
private const val SCHEME = "otpauth"
private const val TYPE = "totp"
Expand All @@ -64,22 +63,23 @@ class DefaultTOTPService(
override fun generateTOTPUrl(
totpSecret: TOTPSecret,
emailAddress: EmailAddress,
issuer: Issuer
issuer: Issuer,
): URI {
val encodedIssuer: String = issuer.value.urlEncode()
val encodedEmailAddress: String = emailAddress.value.urlEncode()
val template = "$SCHEME://$TYPE/$encodedIssuer:$encodedEmailAddress?" +
"$SECRET_QUERY_PARAM=${totpSecret.base32Encoded}" +
"&$ISSUER_QUERY_PARAM=$encodedIssuer" +
"&$ALGORITHM_QUERY_PARAM=${totpGenerator.digest.toQueryParam()}" +
"&$DIGITS_QUERY_PARAM=${totpGenerator.otpLength.value}" +
"&$PERIOD_QUERY_PARAM=${totpGenerator.timeStepSeconds}"
val template =
"$SCHEME://$TYPE/$encodedIssuer:$encodedEmailAddress?" +
"$SECRET_QUERY_PARAM=${totpSecret.base32Encoded}" +
"&$ISSUER_QUERY_PARAM=$encodedIssuer" +
"&$ALGORITHM_QUERY_PARAM=${totpGenerator.digest.toQueryParam()}" +
"&$DIGITS_QUERY_PARAM=${totpGenerator.otpLength.value}" +
"&$PERIOD_QUERY_PARAM=${totpGenerator.timeStepSeconds}"
return URI(template)
}

override fun verify(
code: TOTP,
totpSecret: TOTPSecret
totpSecret: TOTPSecret,
): TOTPVerificationResult {
val index = totpGenerator.generate(totpSecret, totpConfiguration.allowedPastSteps, totpConfiguration.allowedFutureSteps).indexOf(code)
return if (index == -1) {
Expand All @@ -90,7 +90,8 @@ class DefaultTOTPService(
}
}

fun String.urlEncode(): String = URLEncoder.encode(this, Charsets.UTF_8)
.replace("+", "%20")
.replace("*", "%2A")
.replace("%7E", "~")
fun String.urlEncode(): String =
URLEncoder.encode(this, Charsets.UTF_8)
.replace("+", "%20")
.replace("*", "%2A")
.replace("%7E", "~")
Loading

0 comments on commit b0da648

Please sign in to comment.