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)) }
}
}
|