Browse Source

Add base32 decoding function

master
Chris Smith 5 years ago
parent
commit
956fc9240e

+ 27
- 0
app/src/main/java/com/chameth/yaotp/util/Base32.kt View File

@@ -0,0 +1,27 @@
1
+package com.chameth.yaotp.util
2
+
3
+private val alphabet = ('A'..'Z') + ('2'..'7') + '='
4
+
5
+fun base32Decode(input: String): ByteArray {
6
+    val values = padToEights(input).toUpperCase().map(alphabet::indexOf)
7
+    val bytes = ArrayList<Byte>()
8
+
9
+    values.chunked(8).forEach { chunk ->
10
+        // Each chunk of 8 values is mapped on (up to) 5 return bytes:
11
+        //      [A1   A2   A3   A4   A5] [B1   B2   B3
12
+        //       B4   B5] [C1   C2   C3   C4   C5] [D1   -- only if C is present
13
+        //       D2   D3   D4   D5] [E1   E2   E3   E4   -- only if E is present
14
+        //       E5] [F1   F2   F3   F4   F5] [G1   G2   -- only if F is present
15
+        //       G3   G4   G5] [H1   H2   H3   H4   H5]  -- only if H is present
16
+
17
+        if (chunk[0] != 32) bytes.add(((chunk[0] shl 3) or (chunk[1] ushr 2)).toByte())
18
+        if (chunk[2] != 32) bytes.add((((chunk[1] and 3) shl 6) or (chunk[2] shl 1) or (chunk[3] ushr 4)).toByte())
19
+        if (chunk[4] != 32) bytes.add((((chunk[3] and 15) shl 4) or (chunk[4] ushr 1)).toByte())
20
+        if (chunk[5] != 32) bytes.add((((chunk[4] and 1) shl 7) or (chunk[5] shl 2) or (chunk[6] ushr 3)).toByte())
21
+        if (chunk[7] != 32) bytes.add((((chunk[6] and 7) shl 5) or chunk[7]).toByte())
22
+    }
23
+
24
+    return bytes.toByteArray()
25
+}
26
+
27
+internal fun padToEights(input: String) = input + "=".repeat((8 - input.length % 8) % 8)

app/src/test/java/com/chameth/yaotp/algos/TestUtils.kt → app/src/test/java/com/chameth/yaotp/TestUtils.kt View File

@@ -1,3 +1,3 @@
1
-package com.chameth.yaotp.algos
1
+package com.chameth.yaotp
2 2
 
3 3
 fun ByteArray.toHexString() = map { String.format("%02x", it) }.reduce(String::plus)

+ 1
- 0
app/src/test/java/com/chameth/yaotp/algos/HmacTest.kt View File

@@ -1,5 +1,6 @@
1 1
 package com.chameth.yaotp.algos
2 2
 
3
+import com.chameth.yaotp.toHexString
3 4
 import com.natpryce.hamkrest.assertion.assert
4 5
 import com.natpryce.hamkrest.equalTo
5 6
 import org.junit.Test

+ 1
- 0
app/src/test/java/com/chameth/yaotp/algos/HotpTest.kt View File

@@ -1,5 +1,6 @@
1 1
 package com.chameth.yaotp.algos
2 2
 
3
+import com.chameth.yaotp.toHexString
3 4
 import com.natpryce.hamkrest.assertion.assert
4 5
 import com.natpryce.hamkrest.equalTo
5 6
 import org.junit.Test

+ 1
- 0
app/src/test/java/com/chameth/yaotp/algos/TotpTest.kt View File

@@ -1,5 +1,6 @@
1 1
 package com.chameth.yaotp.algos
2 2
 
3
+import com.chameth.yaotp.toHexString
3 4
 import com.natpryce.hamkrest.assertion.assert
4 5
 import com.natpryce.hamkrest.equalTo
5 6
 import org.junit.Test

+ 61
- 0
app/src/test/java/com/chameth/yaotp/util/Base32Test.kt View File

@@ -0,0 +1,61 @@
1
+package com.chameth.yaotp.util
2
+
3
+import com.chameth.yaotp.toHexString
4
+import org.junit.Assert.*
5
+import org.junit.Test
6
+
7
+class Base32Test {
8
+
9
+    @Test
10
+    fun testPadToEights_withEmptyString_returnsEmptyString() {
11
+        assertEquals("", padToEights(""))
12
+    }
13
+
14
+    @Test
15
+    fun testPadToEights_withMultipleOfEight_doesNotAddPadding() {
16
+        assertEquals("0123456701234567", padToEights("0123456701234567"))
17
+    }
18
+
19
+    @Test
20
+    fun testPadToEights_withOddAmounts_padsWithEquals() {
21
+        assertEquals("012345==", padToEights("012345"))
22
+        assertEquals("0123====", padToEights("0123"))
23
+        assertEquals("0=======", padToEights("0"))
24
+    }
25
+
26
+    @Test
27
+    fun testBase32_withEmptyString() {
28
+        assertEquals(0, base32Decode("").size)
29
+        assertEquals(0, base32Decode("====").size)
30
+        assertEquals(0, base32Decode("========").size)
31
+    }
32
+
33
+    @Test
34
+    fun testBase32_withAlignedStrings() {
35
+        assertEquals("00443214c74254b635cf84653a56d9c675be77df", base32Decode("abcdefghijklmnopqrstuvwzyz234567").toHexString())
36
+        assertEquals("ffffffffff", base32Decode("77777777").toHexString())
37
+        assertEquals("0000000000", base32Decode("AAAAAAAA").toHexString())
38
+    }
39
+
40
+    @Test
41
+    fun testBase32_withUnalignedStings() {
42
+        assertEquals("08", base32Decode("A").toHexString())
43
+        assertEquals("00", base32Decode("AA").toHexString())
44
+        assertEquals("0002", base32Decode("AAA").toHexString())
45
+        assertEquals("fffffffc", base32Decode("777777").toHexString())
46
+    }
47
+
48
+    @Test
49
+    fun testBase32_withSampleInputs() {
50
+        assertEquals("28", base32Decode("FA======").toHexString())
51
+        assertEquals("d6", base32Decode("2Y======").toHexString())
52
+        assertEquals("d6f8", base32Decode("234A====").toHexString())
53
+        assertEquals("d6f800", base32Decode("234AA===").toHexString())
54
+        assertEquals("d6f810", base32Decode("234BA===").toHexString())
55
+        assertEquals("d6f8110c", base32Decode("234BCDA=").toHexString())
56
+        assertEquals("d6f8110c80", base32Decode("234BCDEA").toHexString())
57
+        assertEquals("d6f8110c8530", base32Decode("234BCDEFGA======").toHexString())
58
+        assertEquals("d6f8110c8536b7c0886429", base32Decode("234BCDEFG234BCDEFE======").toHexString())
59
+    }
60
+
61
+}

Loading…
Cancel
Save