ble/milenage: fix and add tests

This commit is contained in:
Andrei Vereha 2021-03-02 18:39:03 +01:00
parent 099ec0a328
commit 9c42e5749f
4 changed files with 56 additions and 26 deletions

View file

@ -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 {
@ -97,4 +97,4 @@ class EapAkaAttributeCustomIV(val payload: ByteArray) : EapAkaAttribute(
private const val SIZE = (8 / SIZE_MULTIPLIER).toByte() // type, size, 2 reserved bytes, payload=4 private const val SIZE = (8 / SIZE_MULTIPLIER).toByte() // type, size, 2 reserved bytes, payload=4
} }
} }

View file

@ -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,16 +34,17 @@ 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
val totalSize = HEADER_SIZE + attrSize val totalSize = HEADER_SIZE + attrSize
var bb = ByteBuffer var bb = ByteBuffer
.allocate(totalSize) .allocate(totalSize)
.put(code.code) .put(code.code)
@ -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()
@ -65,4 +65,4 @@ class EapAkaMessage(
private const val SUBTYPE_AKA_CHALLENGE = 1.toByte() private const val SUBTYPE_AKA_CHALLENGE = 1.toByte()
private const val AKA_PACKET_TYPE = 0x17.toByte() private const val AKA_PACKET_TYPE = 0x17.toByte()
} }
} }

View file

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

View file

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