ble/milenage: fix and add tests
This commit is contained in:
parent
099ec0a328
commit
9c42e5749f
4 changed files with 56 additions and 26 deletions
|
@ -86,7 +86,7 @@ class EapAkaAttributeCustomIV(val payload: ByteArray) : EapAkaAttribute(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(payload.size == 4) { "AT_RES payload size has to be 8 bytes. Payload: ${payload.toHex()}" }
|
require(payload.size == 4) { "CUSTOM_IV payload size has to be 4 bytes. Payload: ${payload.toHex()}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toByteArray(): ByteArray {
|
override fun toByteArray(): ByteArray {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType
|
||||||
import retrofit2.http.HEAD
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
enum class EapCode(val code: Byte) {
|
enum class EapCode(val code: Byte) {
|
||||||
|
@ -35,11 +34,12 @@ class EapAkaMessage(
|
||||||
payload = payload,
|
payload = payload,
|
||||||
sequenceNumber = sequenceNumber,
|
sequenceNumber = sequenceNumber,
|
||||||
sas = true // TODO: understand why this is true for PairMessages
|
sas = true // TODO: understand why this is true for PairMessages
|
||||||
)) {
|
)
|
||||||
|
) {
|
||||||
|
|
||||||
fun toByteArray(): ByteArray {
|
fun toByteArray(): ByteArray {
|
||||||
|
|
||||||
val serializedAttributes = attributes?.flatMap{ it.toByteArray().asIterable() }
|
val serializedAttributes = attributes?.flatMap { it.toByteArray().asIterable() }
|
||||||
val joinedAttributes = serializedAttributes?.toTypedArray()?.toByteArray()
|
val joinedAttributes = serializedAttributes?.toTypedArray()?.toByteArray()
|
||||||
|
|
||||||
val attrSize = joinedAttributes?.size ?: 0
|
val attrSize = joinedAttributes?.size ?: 0
|
||||||
|
@ -53,7 +53,7 @@ class EapAkaMessage(
|
||||||
.put((totalSize and 0XFF).toByte())
|
.put((totalSize and 0XFF).toByte())
|
||||||
.put(AKA_PACKET_TYPE)
|
.put(AKA_PACKET_TYPE)
|
||||||
.put(SUBTYPE_AKA_CHALLENGE)
|
.put(SUBTYPE_AKA_CHALLENGE)
|
||||||
.put(byteArrayOf(0,0))
|
.put(byteArrayOf(0, 0))
|
||||||
.put(joinedAttributes ?: ByteArray(0))
|
.put(joinedAttributes ?: ByteArray(0))
|
||||||
|
|
||||||
val ret = bb.array()
|
val ret = bb.array()
|
||||||
|
|
|
@ -11,7 +11,7 @@ import javax.crypto.spec.SecretKeySpec
|
||||||
class Milenage(private val aapsLogger: AAPSLogger, private val k: ByteArray, val sqn: ByteArray, val _rand: ByteArray? = null) {
|
class Milenage(private val aapsLogger: AAPSLogger, private val k: ByteArray, val sqn: ByteArray, val _rand: ByteArray? = null) {
|
||||||
init {
|
init {
|
||||||
require(k.size == KEY_SIZE) { "Milenage key has to be $KEY_SIZE bytes long. Received: ${k.toHex()}" }
|
require(k.size == KEY_SIZE) { "Milenage key has to be $KEY_SIZE bytes long. Received: ${k.toHex()}" }
|
||||||
require(sqn.size == SEQ_SIZE) { "Milenage SEQ has to be $SEQ_SIZE long. Received: ${sqn.toHex()}" }
|
require(sqn.size == SQN) { "Milenage SQN has to be $SQN long. Received: ${sqn.toHex()}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val secretKeySpec = SecretKeySpec(k, "AES")
|
private val secretKeySpec = SecretKeySpec(k, "AES")
|
||||||
|
@ -31,45 +31,47 @@ class Milenage(private val aapsLogger: AAPSLogger, private val k: ByteArray, val
|
||||||
}
|
}
|
||||||
|
|
||||||
private val opc = cipher.doFinal(MILENAGE_OP) xor MILENAGE_OP
|
private val opc = cipher.doFinal(MILENAGE_OP) xor MILENAGE_OP
|
||||||
private val rand_opc_encrypted = cipher.doFinal(rand xor opc)
|
private val randOpcEncrypted = cipher.doFinal(rand xor opc)
|
||||||
private val rand_opc_encrypted_opc = rand_opc_encrypted xor opc
|
private val randOpcEncryptedXorOpc = randOpcEncrypted xor opc
|
||||||
|
private val resAkInput = randOpcEncryptedXorOpc.copyOfRange(0, KEY_SIZE)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
rand_opc_encrypted_opc[15] = (rand_opc_encrypted_opc[15].toInt() xor 1).toByte()
|
resAkInput[15] = (resAkInput[15].toInt() xor 1).toByte()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val res_ak = cipher.doFinal(rand_opc_encrypted_opc) xor opc
|
private val resAk = cipher.doFinal(resAkInput) xor opc
|
||||||
|
|
||||||
val res = res_ak.copyOfRange(8, 16)
|
val res = resAk.copyOfRange(8, 16)
|
||||||
val ak = res_ak.copyOfRange(0, 6)
|
private val ak = resAk.copyOfRange(0, 6)
|
||||||
|
|
||||||
private val ck_input = ByteArray(KEY_SIZE)
|
private val ckInput = ByteArray(KEY_SIZE)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for (i in 0..15) {
|
for (i in 0..15) {
|
||||||
ck_input[(i + 12) % 16] = (rand_opc_encrypted[i].toInt() xor opc[i].toInt()).toByte()
|
ckInput[(i + 12) % 16] = randOpcEncryptedXorOpc[i]
|
||||||
}
|
}
|
||||||
ck_input[15] = (ck_input[15].toInt() xor 2).toByte()
|
ckInput[15] = (ckInput[15].toInt() xor 2).toByte()
|
||||||
}
|
}
|
||||||
|
|
||||||
val ck = cipher.doFinal(ck_input) xor opc
|
val ck = cipher.doFinal(ckInput) xor opc
|
||||||
|
|
||||||
|
private val sqnAmf = sqn + MILENAGE_AMF + sqn + MILENAGE_AMF
|
||||||
|
private val sqnAmfXorOpc = sqnAmf xor opc
|
||||||
private val macAInput = ByteArray(KEY_SIZE)
|
private val macAInput = ByteArray(KEY_SIZE)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for (i in 0..15) {
|
for (i in 0..15) {
|
||||||
macAInput[(i + 8) % 16] = (rand_opc_encrypted[i].toInt() xor opc[i].toInt()).toByte()
|
macAInput[(i + 8) % 16] = sqnAmfXorOpc[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val macAFull = cipher.doFinal(macAInput xor opc)
|
private val macAFull = cipher.doFinal(macAInput xor randOpcEncrypted) xor opc
|
||||||
private val macA = macAFull.copyOfRange(0, 8)
|
private val macA = macAFull.copyOfRange(0, 8)
|
||||||
val autn = (ak xor sqn) + MILENAGE_AMF + macA
|
val autn = (ak xor sqn) + MILENAGE_AMF + macA
|
||||||
|
|
||||||
init {
|
init {
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage K: ${k.toHex()}")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage K: ${k.toHex()}")
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage RAND: ${rand.toHex()}")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage RAND: ${rand.toHex()}")
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage SEQ: ${sqn.toHex()}")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage SQN: ${sqn.toHex()}")
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage CK: ${ck.toHex()}")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage CK: ${ck.toHex()}")
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage AUTN: ${autn.toHex()}")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage AUTN: ${autn.toHex()}")
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage RES: ${res.toHex()}")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage RES: ${res.toHex()}")
|
||||||
|
@ -83,7 +85,7 @@ class Milenage(private val aapsLogger: AAPSLogger, private val k: ByteArray, val
|
||||||
private val MILENAGE_OP = Hex.decode("cdc202d5123e20f62b6d676ac72cb318")
|
private val MILENAGE_OP = Hex.decode("cdc202d5123e20f62b6d676ac72cb318")
|
||||||
private val MILENAGE_AMF = Hex.decode("b9b9")
|
private val MILENAGE_AMF = Hex.decode("b9b9")
|
||||||
private const val KEY_SIZE = 16
|
private const val KEY_SIZE = 16
|
||||||
private const val SEQ_SIZE = 6
|
private const val SQN = 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,39 @@ class MilenageTest {
|
||||||
val m = Milenage(
|
val m = Milenage(
|
||||||
aapsLogger,
|
aapsLogger,
|
||||||
Hex.decode("c0772899720972a314f557de66d571dd"),
|
Hex.decode("c0772899720972a314f557de66d571dd"),
|
||||||
byteArrayOf(0,0,0,0,0,1),
|
byteArrayOf(0,0,0,0,0,2),
|
||||||
Hex.decode("c2cd1248451103bd77a6c7ef88c441ba")
|
Hex.decode("c2cd1248451103bd77a6c7ef88c441ba")
|
||||||
)
|
)
|
||||||
Assert.assertEquals(m.ck.toHex(), "55799fd26664cbf6e476525e2dee52c6")
|
|
||||||
Assert.assertEquals(m.res.toHex(), "a40bc6d13861447e")
|
Assert.assertEquals(m.res.toHex(), "a40bc6d13861447e")
|
||||||
|
Assert.assertEquals(m.ck.toHex(), "55799fd26664cbf6e476525e2dee52c6")
|
||||||
Assert.assertEquals(m.autn.toHex(), "00c55c78e8d3b9b9e935860a7259f6c0")
|
Assert.assertEquals(m.autn.toHex(), "00c55c78e8d3b9b9e935860a7259f6c0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun testMilenage2() {
|
||||||
|
val aapsLogger = AAPSLoggerTest()
|
||||||
|
val m = Milenage(
|
||||||
|
aapsLogger,
|
||||||
|
Hex.decode("78411ccad0fd0fb6f381a47fb3335ecb"),
|
||||||
|
byteArrayOf(0,0,0,0,0,2), // 1 + 1
|
||||||
|
Hex.decode("4fc01ac1a94376ae3e052339c07d9e1f")
|
||||||
|
)
|
||||||
|
Assert.assertEquals(m.res.toHex(), "ec549e00fa668a19")
|
||||||
|
Assert.assertEquals(m.ck.toHex(), "ee3dac761fe358a9f476cc5ee81aa3e9")
|
||||||
|
Assert.assertEquals(m.autn.toHex(), "a3e7a71430c8b9b95245b33b3bd679c4")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun testMilenageIncrementedSQN() {
|
||||||
|
val aapsLogger = AAPSLoggerTest()
|
||||||
|
val m = Milenage(
|
||||||
|
aapsLogger,
|
||||||
|
Hex.decode("c0772899720972a314f557de66d571dd"),
|
||||||
|
// byteArrayOf(0,0,0,0,0x01,0x5d), this is in logs. SQN has to be incremented.
|
||||||
|
byteArrayOf(0,0,0,0,0x01,0x5e),
|
||||||
|
Hex.decode("d71cc44820e5419f42c62ae97c035988")
|
||||||
|
)
|
||||||
|
Assert.assertEquals(m.res.toHex(), "5f807a379a5c5d30")
|
||||||
|
Assert.assertEquals(m.ck.toHex(), "8dd4b3ceb849a01766e37f9d86045c39")
|
||||||
|
Assert.assertEquals(m.autn.toHex(), "0e0264d056fcb9b9752227365a090955")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue