working mib login and list accounts
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package sh.sar.basedbank.util
|
||||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.experimental.and
|
||||
|
||||
object Totp {
|
||||
|
||||
/**
|
||||
* Generate a 6-digit TOTP code from a Base32-encoded secret (RFC 6238 / RFC 4226).
|
||||
* Uses HmacSHA1, 30-second window, 6 digits — matching standard authenticator apps.
|
||||
*/
|
||||
fun generate(base32Secret: String, digits: Int = 6, periodSeconds: Long = 30): String {
|
||||
val key = base32Decode(base32Secret.uppercase().replace(" ", "").replace("-", ""))
|
||||
val counter = System.currentTimeMillis() / 1000L / periodSeconds
|
||||
val otp = hotp(key, counter, digits)
|
||||
return otp.toString().padStart(digits, '0')
|
||||
}
|
||||
|
||||
private fun hotp(key: ByteArray, counter: Long, digits: Int): Int {
|
||||
val msg = ByteArray(8) { i -> ((counter shr ((7 - i) * 8)) and 0xFF).toByte() }
|
||||
val mac = Mac.getInstance("HmacSHA1")
|
||||
mac.init(SecretKeySpec(key, "HmacSHA1"))
|
||||
val hash = mac.doFinal(msg)
|
||||
|
||||
val offset = (hash[hash.size - 1] and 0x0F).toInt()
|
||||
val code = ((hash[offset].toInt() and 0x7F) shl 24) or
|
||||
((hash[offset + 1].toInt() and 0xFF) shl 16) or
|
||||
((hash[offset + 2].toInt() and 0xFF) shl 8) or
|
||||
(hash[offset + 3].toInt() and 0xFF)
|
||||
|
||||
val modulus = Math.pow(10.0, digits.toDouble()).toInt()
|
||||
return code % modulus
|
||||
}
|
||||
|
||||
private val BASE32_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
||||
|
||||
private fun base32Decode(input: String): ByteArray {
|
||||
val clean = input.trimEnd('=')
|
||||
val output = ByteArray(clean.length * 5 / 8)
|
||||
var buffer = 0
|
||||
var bitsLeft = 0
|
||||
var outIndex = 0
|
||||
for (c in clean) {
|
||||
val v = BASE32_CHARS.indexOf(c)
|
||||
require(v >= 0) { "Invalid Base32 character: $c" }
|
||||
buffer = (buffer shl 5) or v
|
||||
bitsLeft += 5
|
||||
if (bitsLeft >= 8) {
|
||||
bitsLeft -= 8
|
||||
output[outIndex++] = ((buffer shr bitsLeft) and 0xFF).toByte()
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user