WIP: Started to convert flicker code decoder from https://6xq.net/flickercodes/ from JavaScript to Kotlin
This commit is contained in:
parent
b6bcea039d
commit
3c7d897d52
|
@ -0,0 +1,24 @@
|
|||
package net.dankito.fints.tan
|
||||
|
||||
|
||||
enum class Bit(val value: Int) {
|
||||
|
||||
Low(0),
|
||||
|
||||
High(1);
|
||||
|
||||
|
||||
fun invert(): Bit {
|
||||
if (this == High) {
|
||||
return Low
|
||||
}
|
||||
|
||||
return High
|
||||
}
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return "$value"
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package net.dankito.fints.tan
|
||||
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
|
||||
open class FlickerCanvas(var code: String) {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(FlickerCanvas::class.java)
|
||||
}
|
||||
|
||||
|
||||
var halfbyteid = 0
|
||||
var clock = Bit.High
|
||||
var bitarray = mutableListOf<MutableList<Bit>>()
|
||||
|
||||
fun reset() {
|
||||
halfbyteid = 0
|
||||
clock = Bit.High
|
||||
}
|
||||
|
||||
init {
|
||||
val bits = mutableMapOf<Char, List<Bit>>()
|
||||
/* bitfield: clock, bits 2^1, 2^2, 2^3, 2^4 */
|
||||
bits['0'] = listOf(Bit.Low, Bit.Low, Bit.Low, Bit.Low, Bit.Low)
|
||||
bits['1'] = listOf(Bit.Low, Bit.High, Bit.Low, Bit.Low, Bit.Low)
|
||||
bits['2'] = listOf(Bit.Low, Bit.Low, Bit.High, Bit.Low, Bit.Low)
|
||||
bits['3'] = listOf(Bit.Low, Bit.High, Bit.High, Bit.Low, Bit.Low)
|
||||
bits['4'] = listOf(Bit.Low, Bit.Low, Bit.Low, Bit.High, Bit.Low)
|
||||
bits['5'] = listOf(Bit.Low, Bit.High, Bit.Low, Bit.High, Bit.Low)
|
||||
bits['6'] = listOf(Bit.Low, Bit.Low, Bit.High, Bit.High, Bit.Low)
|
||||
bits['7'] = listOf(Bit.Low, Bit.High, Bit.High, Bit.High, Bit.Low)
|
||||
bits['8'] = listOf(Bit.Low, Bit.Low, Bit.Low, Bit.Low, Bit.High)
|
||||
bits['9'] = listOf(Bit.Low, Bit.High, Bit.Low, Bit.Low, Bit.High)
|
||||
bits['A'] = listOf(Bit.Low, Bit.Low, Bit.High, Bit.Low, Bit.High)
|
||||
bits['B'] = listOf(Bit.Low, Bit.High, Bit.High, Bit.Low, Bit.High)
|
||||
bits['C'] = listOf(Bit.Low, Bit.Low, Bit.Low, Bit.High, Bit.High)
|
||||
bits['D'] = listOf(Bit.Low, Bit.High, Bit.Low, Bit.High, Bit.High)
|
||||
bits['E'] = listOf(Bit.Low, Bit.Low, Bit.High, Bit.High, Bit.High)
|
||||
bits['F'] = listOf(Bit.Low, Bit.High, Bit.High, Bit.High, Bit.High)
|
||||
|
||||
/* prepend synchronization identifier */
|
||||
code = "0FFF" + code
|
||||
|
||||
for (i in 0 until code.length step 2) {
|
||||
bits[code[i + 1]]?.let { bitarray.add(mutableListOf(*it.toTypedArray())) }
|
||||
bits[code[i]]?.let { bitarray.add(mutableListOf(*it.toTypedArray())) }
|
||||
}
|
||||
|
||||
val steps = mutableListOf<Array<Bit>>()
|
||||
|
||||
do {
|
||||
steps.add(step())
|
||||
} while (halfbyteid > 0 || clock == Bit.Low)
|
||||
|
||||
log.info("Steps:")
|
||||
steps.forEach { step -> log.info(step.joinToString("")) }
|
||||
}
|
||||
|
||||
fun step(): Array<Bit> {
|
||||
bitarray[halfbyteid][0] = clock
|
||||
|
||||
val stepBits = Array(5) { index -> bitarray[halfbyteid][index] }
|
||||
|
||||
clock = clock.invert()
|
||||
|
||||
if (clock == Bit.High) {
|
||||
halfbyteid++
|
||||
|
||||
if (halfbyteid >= bitarray.size) {
|
||||
halfbyteid = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return stepBits
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package net.dankito.fints.tan
|
||||
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
open class FlickercodeDecoder {
|
||||
|
||||
open fun decodeChallenge(challenge: String): String {
|
||||
var code = challenge.toUpperCase().replace ("[^a-fA-F0-9]", "")
|
||||
|
||||
/* length check: first byte */
|
||||
val len = code.length / 2 - 1
|
||||
code = toHex(len, 2) + code.substring(2)
|
||||
|
||||
/* luhn checksum */
|
||||
val luhndata = getPayload(code)
|
||||
var luhnsum = 0
|
||||
var i = 0
|
||||
|
||||
while (i < luhndata.length) {
|
||||
luhnsum += 1 * parseIntToHex(luhndata[i]) + quersumme(2 * parseIntToHex(luhndata[i + 1]))
|
||||
i += 2
|
||||
}
|
||||
|
||||
luhnsum = (10 - luhnsum % 10) % 10
|
||||
code = code.substring(0, code.length - 2) + toHex(luhnsum, 1) + code.substring(code.length - 1)
|
||||
|
||||
/* xor checksum */
|
||||
var xorsum = 0
|
||||
i = 0
|
||||
while (i < code.length - 2) {
|
||||
xorsum = xorsum xor parseIntToHex(code[i])
|
||||
i++
|
||||
}
|
||||
|
||||
code = code.substring(0, code.length - 1) + toHex(xorsum, 1)
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
open fun toHex(number: Int, minLength: Int): String {
|
||||
var result = number.toString (16).toUpperCase()
|
||||
|
||||
while (result.length < minLength) {
|
||||
result = '0' + result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun quersumme(number: Int): Int {
|
||||
var quersumme = 0
|
||||
var temp = number
|
||||
|
||||
while (temp != 0) {
|
||||
quersumme += temp % 10
|
||||
temp = floor(temp / 10.0).toInt()
|
||||
}
|
||||
|
||||
return quersumme
|
||||
}
|
||||
|
||||
fun getPayload(code: String): String {
|
||||
var i = 0
|
||||
var payload = ""
|
||||
|
||||
var len = parseIntToHex(code.substring(0, 2))
|
||||
i += 2
|
||||
|
||||
while (i < code.length-2) {
|
||||
/* skip bcd identifier */
|
||||
i += 1
|
||||
/* parse length */
|
||||
len = parseIntToHex(code.substring(i, i + 1))
|
||||
i += 1
|
||||
// i = 4
|
||||
var endIndex = i + len * 2
|
||||
if (endIndex > code.length) {
|
||||
endIndex = code.length
|
||||
}
|
||||
payload += code.substring(i, endIndex)
|
||||
i += len * 2
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
protected open fun parseIntToHex(char: Char): Int {
|
||||
return parseIntToHex(char.toString())
|
||||
}
|
||||
|
||||
protected open fun parseIntToHex(string: String): Int {
|
||||
return Integer.parseInt(string, 16)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue