diff options
Diffstat (limited to 'format-common/src/main')
-rw-r--r-- | format-common/src/main/kotlin/app/passwordstore/data/passfile/PasswordEntry.kt | 14 | ||||
-rw-r--r-- | format-common/src/main/kotlin/app/passwordstore/util/time/UserClock.kt (renamed from format-common/src/main/kotlin/app/passwordstore/util/time/Clocks.kt) | 0 | ||||
-rw-r--r-- | format-common/src/main/kotlin/app/passwordstore/util/totp/Otp.kt | 33 |
3 files changed, 33 insertions, 14 deletions
diff --git a/format-common/src/main/kotlin/app/passwordstore/data/passfile/PasswordEntry.kt b/format-common/src/main/kotlin/app/passwordstore/data/passfile/PasswordEntry.kt index 9543eb8c..38aa4a20 100644 --- a/format-common/src/main/kotlin/app/passwordstore/data/passfile/PasswordEntry.kt +++ b/format-common/src/main/kotlin/app/passwordstore/data/passfile/PasswordEntry.kt @@ -62,7 +62,7 @@ constructor( do { val otp = calculateTotp() emit(otp) - delay(1000L) + delay(ONE_SECOND.seconds) } while (coroutineContext.isActive) } else { awaitCancellation() @@ -90,6 +90,7 @@ constructor( return totpSecret != null } + @Suppress("ReturnCount") private fun findAndStripPassword(passContent: List<String>): Pair<String?, List<String>> { if (TotpFinder.TOTP_FIELDS.any { passContent[0].startsWith(it) }) return Pair(null, passContent) for (line in passContent) { @@ -180,8 +181,14 @@ constructor( val totpAlgorithm = totpFinder.findAlgorithm(content) val issuer = totpFinder.findIssuer(content) val millis = clock.millis() - val remainingTime = (totpPeriod - ((millis / 1000) % totpPeriod)).seconds - Otp.calculateCode(totpSecret!!, millis / (1000 * totpPeriod), totpAlgorithm, digits, issuer) + val remainingTime = (totpPeriod - ((millis / ONE_SECOND) % totpPeriod)).seconds + Otp.calculateCode( + totpSecret!!, + millis / (ONE_SECOND * totpPeriod), + totpAlgorithm, + digits, + issuer + ) .mapBoth( { code -> return Totp(code, remainingTime) @@ -217,5 +224,6 @@ constructor( "secret:", "pass:", ) + private const val ONE_SECOND = 1000 } } diff --git a/format-common/src/main/kotlin/app/passwordstore/util/time/Clocks.kt b/format-common/src/main/kotlin/app/passwordstore/util/time/UserClock.kt index 4ffeb6a6..4ffeb6a6 100644 --- a/format-common/src/main/kotlin/app/passwordstore/util/time/Clocks.kt +++ b/format-common/src/main/kotlin/app/passwordstore/util/time/UserClock.kt diff --git a/format-common/src/main/kotlin/app/passwordstore/util/totp/Otp.kt b/format-common/src/main/kotlin/app/passwordstore/util/totp/Otp.kt index 72d5cffe..5abc0337 100644 --- a/format-common/src/main/kotlin/app/passwordstore/util/totp/Otp.kt +++ b/format-common/src/main/kotlin/app/passwordstore/util/totp/Otp.kt @@ -18,6 +18,13 @@ internal object Otp { private val BASE_32 = Base32() private val STEAM_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY".toCharArray() + private const val BYTE_BUFFER_CAPACITY = 8 + private const val END_INDEX_OFFSET = 4 + private const val STEAM_GUARD_DIGITS = 5 + private const val MINIMUM_DIGITS = 6 + private const val MAXIMUM_DIGITS = 10 + private const val ALPHABET_LENGTH = 26 + private const val MOST_SIGNIFICANT_BYTE = 0x7f fun calculateCode( secret: String, @@ -32,13 +39,13 @@ internal object Otp { val digest = Mac.getInstance(algo).run { init(secretKey) - doFinal(ByteBuffer.allocate(8).putLong(counter).array()) + doFinal(ByteBuffer.allocate(BYTE_BUFFER_CAPACITY).putLong(counter).array()) } // Least significant 4 bits are used as an offset into the digest. val offset = (digest.last() and 0xf).toInt() // Extract 32 bits at the offset and clear the most significant bit. - val code = digest.copyOfRange(offset, offset + 4) - code[0] = (0x7f and code[0].toInt()).toByte() + val code = digest.copyOfRange(offset, offset.plus(END_INDEX_OFFSET)) + code[0] = (MOST_SIGNIFICANT_BYTE and code[0].toInt()).toByte() val codeInt = ByteBuffer.wrap(code).int check(codeInt > 0) // SteamGuard is a horrible OTP implementation that generates non-standard 5 digit OTPs as @@ -47,9 +54,9 @@ internal object Otp { if (digits == "s" || issuer == "Steam") { var remainingCodeInt = codeInt buildString { - repeat(5) { + repeat(STEAM_GUARD_DIGITS) { append(STEAM_ALPHABET[remainingCodeInt % STEAM_ALPHABET.size]) - remainingCodeInt /= 26 + remainingCodeInt /= ALPHABET_LENGTH } } } else { @@ -59,17 +66,21 @@ internal object Otp { numDigits == null -> { return Err(IllegalArgumentException("Digits specifier has to be either 's' or numeric")) } - numDigits < 6 -> { - return Err(IllegalArgumentException("TOTP codes have to be at least 6 digits long")) + numDigits < MINIMUM_DIGITS -> { + return Err( + IllegalArgumentException("TOTP codes have to be at least $MINIMUM_DIGITS digits long") + ) } - numDigits > 10 -> { - return Err(IllegalArgumentException("TOTP codes can be at most 10 digits long")) + numDigits > MAXIMUM_DIGITS -> { + return Err( + IllegalArgumentException("TOTP codes can be at most $MAXIMUM_DIGITS digits long") + ) } else -> { // 2^31 = 2_147_483_648, so we can extract at most 10 digits with the first one // always being 0, 1, or 2. Pad with leading zeroes. - val codeStringBase10 = codeInt.toString(10).padStart(10, '0') - check(codeStringBase10.length == 10) + val codeStringBase10 = codeInt.toString(MAXIMUM_DIGITS).padStart(MAXIMUM_DIGITS, '0') + check(codeStringBase10.length == MAXIMUM_DIGITS) codeStringBase10.takeLast(numDigits) } } |