summaryrefslogtreecommitdiff
path: root/core/src/jvmTest/kotlin/coffee/liz/rookery/bt/GanCryptoTest.kt
blob: a8ad88e000c99d0919286d0cb034dd06c9bc69e2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package coffee.liz.rookery.bt

import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertFailsWith

@OptIn(ExperimentalStdlibApi::class)
class GanCryptoTest {
    // Vectors computed with a Node.js re-implementation of gan-web-bluetooth's GanGen2CubeEncrypter
    // (aes-js CBC swapped for node:crypto aes-128-cbc, no padding). The MAC is made up; it is chosen so
    // that key[5] + salt[5] = 0x100, exercising the firmware's `% 0xFF` (255, not 256) salting.
    private val mac = "6F:F0:12:34:56:78"
    private val enc = GanEncrypter(JvmAes128, GanDefinitions.GAN_KEY, mac)

    @Test fun saltedKeyAndIv() {
        assertContentEquals("7958763a220116072005185442111253".hexToByteArray(), enc.key)
        assertContentEquals("8959663a127076272095781432120243".hexToByteArray(), enc.iv)
    }

    @Test fun encryptMatchesReference() {
        val m20 = "030a11181f262d343b424950575e656c737a8188".hexToByteArray()
        assertContentEquals("f8b5b4a9ac44104f5d5ce7635f869bf95bc95c02".hexToByteArray(), enc.encrypt(m20))
        val m16 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2".hexToByteArray()
        assertContentEquals("c4f8c0629684911cb703cd9a76f44ffb".hexToByteArray(), enc.encrypt(m16))
    }

    @Test fun roundTrips() {
        val moyu = GanEncrypter(JvmAes128, GanDefinitions.MOYU_AI_KEY, mac)
        for (size in listOf(16, 17, 19, 20, 31, 32)) {
            val msg = ByteArray(size) { (it * 37 + size).toByte() }
            assertContentEquals(msg, enc.decrypt(enc.encrypt(msg)))
            assertContentEquals(msg, moyu.decrypt(moyu.encrypt(msg)))
        }
    }

    @Test fun rejectsShortMessages() {
        assertFailsWith<IllegalArgumentException> { enc.encrypt(ByteArray(15)) }
        assertFailsWith<IllegalArgumentException> { enc.decrypt(ByteArray(15)) }
    }
}