Fix detekt warnings in format-common (#2167)
* fix warning ktlint to format-common folder * update to code review * adjusted spotless
This commit is contained in:
parent
224d956e28
commit
6d73b63435
6 changed files with 35 additions and 28 deletions
|
@ -1,18 +1,5 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<SmellBaseline>
|
<SmellBaseline>
|
||||||
<ManuallySuppressedIssues/>
|
<ManuallySuppressedIssues/>
|
||||||
<CurrentIssues>
|
<CurrentIssues/>
|
||||||
<ID>MagicNumber:Otp.kt$Otp$0x7f</ID>
|
|
||||||
<ID>MagicNumber:Otp.kt$Otp$10</ID>
|
|
||||||
<ID>MagicNumber:Otp.kt$Otp$26</ID>
|
|
||||||
<ID>MagicNumber:Otp.kt$Otp$4</ID>
|
|
||||||
<ID>MagicNumber:Otp.kt$Otp$5</ID>
|
|
||||||
<ID>MagicNumber:Otp.kt$Otp$6</ID>
|
|
||||||
<ID>MagicNumber:Otp.kt$Otp$8</ID>
|
|
||||||
<ID>MagicNumber:PasswordEntry.kt$PasswordEntry$1000</ID>
|
|
||||||
<ID>MatchingDeclarationName:Clocks.kt$UserClock : Clock</ID>
|
|
||||||
<ID>MatchingDeclarationName:TestClocks.kt$TestUserClock : UserClock</ID>
|
|
||||||
<ID>MaxLineLength:PasswordEntryTest.kt$PasswordEntryTest.Companion$"otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30"</ID>
|
|
||||||
<ID>ReturnCount:PasswordEntry.kt$PasswordEntry$private fun findAndStripPassword(passContent: List<String>): Pair<String?, List<String>></ID>
|
|
||||||
</CurrentIssues>
|
|
||||||
</SmellBaseline>
|
</SmellBaseline>
|
||||||
|
|
|
@ -62,7 +62,7 @@ constructor(
|
||||||
do {
|
do {
|
||||||
val otp = calculateTotp()
|
val otp = calculateTotp()
|
||||||
emit(otp)
|
emit(otp)
|
||||||
delay(1000L)
|
delay(ONE_SECOND.seconds)
|
||||||
} while (coroutineContext.isActive)
|
} while (coroutineContext.isActive)
|
||||||
} else {
|
} else {
|
||||||
awaitCancellation()
|
awaitCancellation()
|
||||||
|
@ -90,6 +90,7 @@ constructor(
|
||||||
return totpSecret != null
|
return totpSecret != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("ReturnCount")
|
||||||
private fun findAndStripPassword(passContent: List<String>): Pair<String?, List<String>> {
|
private fun findAndStripPassword(passContent: List<String>): Pair<String?, List<String>> {
|
||||||
if (TotpFinder.TOTP_FIELDS.any { passContent[0].startsWith(it) }) return Pair(null, passContent)
|
if (TotpFinder.TOTP_FIELDS.any { passContent[0].startsWith(it) }) return Pair(null, passContent)
|
||||||
for (line in passContent) {
|
for (line in passContent) {
|
||||||
|
@ -180,8 +181,14 @@ constructor(
|
||||||
val totpAlgorithm = totpFinder.findAlgorithm(content)
|
val totpAlgorithm = totpFinder.findAlgorithm(content)
|
||||||
val issuer = totpFinder.findIssuer(content)
|
val issuer = totpFinder.findIssuer(content)
|
||||||
val millis = clock.millis()
|
val millis = clock.millis()
|
||||||
val remainingTime = (totpPeriod - ((millis / 1000) % totpPeriod)).seconds
|
val remainingTime = (totpPeriod - ((millis / ONE_SECOND) % totpPeriod)).seconds
|
||||||
Otp.calculateCode(totpSecret!!, millis / (1000 * totpPeriod), totpAlgorithm, digits, issuer)
|
Otp.calculateCode(
|
||||||
|
totpSecret!!,
|
||||||
|
millis / (ONE_SECOND * totpPeriod),
|
||||||
|
totpAlgorithm,
|
||||||
|
digits,
|
||||||
|
issuer
|
||||||
|
)
|
||||||
.mapBoth(
|
.mapBoth(
|
||||||
{ code ->
|
{ code ->
|
||||||
return Totp(code, remainingTime)
|
return Totp(code, remainingTime)
|
||||||
|
@ -217,5 +224,6 @@ constructor(
|
||||||
"secret:",
|
"secret:",
|
||||||
"pass:",
|
"pass:",
|
||||||
)
|
)
|
||||||
|
private const val ONE_SECOND = 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,13 @@ internal object Otp {
|
||||||
|
|
||||||
private val BASE_32 = Base32()
|
private val BASE_32 = Base32()
|
||||||
private val STEAM_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY".toCharArray()
|
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(
|
fun calculateCode(
|
||||||
secret: String,
|
secret: String,
|
||||||
|
@ -32,13 +39,13 @@ internal object Otp {
|
||||||
val digest =
|
val digest =
|
||||||
Mac.getInstance(algo).run {
|
Mac.getInstance(algo).run {
|
||||||
init(secretKey)
|
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.
|
// Least significant 4 bits are used as an offset into the digest.
|
||||||
val offset = (digest.last() and 0xf).toInt()
|
val offset = (digest.last() and 0xf).toInt()
|
||||||
// Extract 32 bits at the offset and clear the most significant bit.
|
// Extract 32 bits at the offset and clear the most significant bit.
|
||||||
val code = digest.copyOfRange(offset, offset + 4)
|
val code = digest.copyOfRange(offset, offset.plus(END_INDEX_OFFSET))
|
||||||
code[0] = (0x7f and code[0].toInt()).toByte()
|
code[0] = (MOST_SIGNIFICANT_BYTE and code[0].toInt()).toByte()
|
||||||
val codeInt = ByteBuffer.wrap(code).int
|
val codeInt = ByteBuffer.wrap(code).int
|
||||||
check(codeInt > 0)
|
check(codeInt > 0)
|
||||||
// SteamGuard is a horrible OTP implementation that generates non-standard 5 digit OTPs as
|
// 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") {
|
if (digits == "s" || issuer == "Steam") {
|
||||||
var remainingCodeInt = codeInt
|
var remainingCodeInt = codeInt
|
||||||
buildString {
|
buildString {
|
||||||
repeat(5) {
|
repeat(STEAM_GUARD_DIGITS) {
|
||||||
append(STEAM_ALPHABET[remainingCodeInt % STEAM_ALPHABET.size])
|
append(STEAM_ALPHABET[remainingCodeInt % STEAM_ALPHABET.size])
|
||||||
remainingCodeInt /= 26
|
remainingCodeInt /= ALPHABET_LENGTH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -59,17 +66,21 @@ internal object Otp {
|
||||||
numDigits == null -> {
|
numDigits == null -> {
|
||||||
return Err(IllegalArgumentException("Digits specifier has to be either 's' or numeric"))
|
return Err(IllegalArgumentException("Digits specifier has to be either 's' or numeric"))
|
||||||
}
|
}
|
||||||
numDigits < 6 -> {
|
numDigits < MINIMUM_DIGITS -> {
|
||||||
return Err(IllegalArgumentException("TOTP codes have to be at least 6 digits long"))
|
return Err(
|
||||||
|
IllegalArgumentException("TOTP codes have to be at least $MINIMUM_DIGITS digits long")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
numDigits > 10 -> {
|
numDigits > MAXIMUM_DIGITS -> {
|
||||||
return Err(IllegalArgumentException("TOTP codes can be at most 10 digits long"))
|
return Err(
|
||||||
|
IllegalArgumentException("TOTP codes can be at most $MAXIMUM_DIGITS digits long")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// 2^31 = 2_147_483_648, so we can extract at most 10 digits with the first one
|
// 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.
|
// always being 0, 1, or 2. Pad with leading zeroes.
|
||||||
val codeStringBase10 = codeInt.toString(10).padStart(10, '0')
|
val codeStringBase10 = codeInt.toString(MAXIMUM_DIGITS).padStart(MAXIMUM_DIGITS, '0')
|
||||||
check(codeStringBase10.length == 10)
|
check(codeStringBase10.length == MAXIMUM_DIGITS)
|
||||||
codeStringBase10.takeLast(numDigits)
|
codeStringBase10.takeLast(numDigits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,7 @@ class PasswordEntryTest {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
const val TOTP_URI =
|
const val TOTP_URI =
|
||||||
"otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30"
|
"otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue