WIP: add Milenage
AUTN is not correct yet
This commit is contained in:
parent
e82826bf5a
commit
b6692a5ac6
7 changed files with 161 additions and 14 deletions
|
@ -133,9 +133,9 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
|||
|
||||
aapsLogger.info(LTag.PUMPCOMM, "Got LTK: ${ltk.ltk.toHex()}")
|
||||
|
||||
emitter.onNext(PodEvent.EstablishingSession)
|
||||
// emitter.onNext(PodEvent.EstablishingSession)
|
||||
|
||||
val EapAkaExchanger = EapAkaExchanger(msgIO, ltk)
|
||||
val EapAkaExchanger = EapAkaExchanger(aapsLogger, msgIO, ltk)
|
||||
val sessionKeys = EapAkaExchanger.negotiateSessionKeys()
|
||||
aapsLogger.info(LTag.PUMPCOMM, "Got session Key: $sessionKeys")
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
|
|||
pdmPublic.copyOfRange(pdmPublic.size - 4, pdmPublic.size) +
|
||||
podNonce.copyOfRange(podNonce.size - 4, podNonce.size) +
|
||||
pdmNonce.copyOfRange(pdmNonce.size - 4, pdmNonce.size)
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "LTK, first key: ${firstKey.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "First key for LTK: ${firstKey.toHex()}")
|
||||
|
||||
val intermediateKey = ByteArray(CMAC_SIZE)
|
||||
aesCmac(firstKey, curveLTK, intermediateKey)
|
||||
|
|
|
@ -42,7 +42,7 @@ class PodScanner(private val logger: AAPSLogger, private val bluetoothAdapter: B
|
|||
companion object {
|
||||
|
||||
const val SCAN_FOR_SERVICE_UUID = "00004024-0000-1000-8000-00805F9B34FB"
|
||||
const val POD_ID_NOT_ACTIVATED = 0xFFFFFFFFL
|
||||
const val POD_ID_NOT_ACTIVATED = 0xFFFFFFFEL
|
||||
private const val SCAN_DURATION_MS = 5000
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,34 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.PairResult
|
||||
import okio.ByteString.Companion.decodeHex
|
||||
import info.nightscout.androidaps.utils.extensions.toHex
|
||||
import org.spongycastle.util.encoders.Hex
|
||||
import java.security.SecureRandom
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class EapAkaExchanger(private val msgIO: MessageIO, private val ltk: PairResult) {
|
||||
class EapAkaExchanger(private val aapsLogger: AAPSLogger, private val msgIO: MessageIO, private val ltk: PairResult) {
|
||||
|
||||
var seq = ltk.seq
|
||||
|
||||
private val controllerIV = ByteArray(IV_SIZE)
|
||||
private var nodeIV = ByteArray(IV_SIZE)
|
||||
|
||||
private val controllerId = Id.fromInt(OmnipodDashBleManagerImpl.CONTROLLER_ID)
|
||||
private val sqn = byteArrayOf(0, 0, 0, 0, 0, 1)
|
||||
private val milenage = Milenage(aapsLogger, ltk.ltk, sqn)
|
||||
|
||||
init {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Starting EAP-AKA")
|
||||
val random = SecureRandom()
|
||||
random.nextBytes(controllerIV)
|
||||
}
|
||||
|
||||
fun negotiateSessionKeys(): SessionKeys {
|
||||
// send EAP-AKA challenge
|
||||
|
@ -33,10 +51,18 @@ class EapAkaExchanger(private val msgIO: MessageIO, private val ltk: PairResult)
|
|||
|
||||
private fun eapAkaChallenge(): EapAkaMessage {
|
||||
val payload = ByteArray(0)
|
||||
val attributes =
|
||||
arrayOf(
|
||||
// TODO: verify the order or attributes
|
||||
EapAkaAttributeRand(milenage.rand),
|
||||
EapAkaAttributeAutn(milenage.autn),
|
||||
EapAkaAttributeCustomIV(controllerIV),
|
||||
)
|
||||
|
||||
return EapAkaMessage(
|
||||
code = EapCode.REQUEST,
|
||||
identifier = 42, // TODO: find what value we need here
|
||||
attributes = null,
|
||||
attributes = attributes,
|
||||
sequenceNumber = seq,
|
||||
source = controllerId,
|
||||
destination = ltk.podId,
|
||||
|
@ -52,7 +78,7 @@ class EapAkaExchanger(private val msgIO: MessageIO, private val ltk: PairResult)
|
|||
val payload = ByteArray(0)
|
||||
return EapAkaMessage(
|
||||
code = EapCode.SUCCESS,
|
||||
attributes = null,
|
||||
attributes = null,
|
||||
identifier = 42, // TODO: find what value we need here
|
||||
sequenceNumber = seq,
|
||||
source = controllerId,
|
||||
|
@ -61,10 +87,13 @@ class EapAkaExchanger(private val msgIO: MessageIO, private val ltk: PairResult)
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private val MILENAGE_OP = "cdc202d5123e20f62b6d676ac72cb318".decodeHex()
|
||||
private val MILENAGE_AMF = "b9b9".decodeHex()
|
||||
|
||||
private val MILENAGE_OP = Hex.decode("cdc202d5123e20f62b6d676ac72cb318")
|
||||
private val MILENAGE_AMF = Hex.decode("b9b9")
|
||||
private const val KEY_SIZE = 16 // 128 bits
|
||||
private const val IV_SIZE = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.utils.extensions.toHex
|
||||
import org.spongycastle.util.encoders.Hex
|
||||
import java.security.SecureRandom
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class Milenage(private val aapsLogger: AAPSLogger, private val k: ByteArray, val sqn: ByteArray, val _rand: ByteArray? = null) {
|
||||
init {
|
||||
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()}" }
|
||||
}
|
||||
|
||||
private val secretKeySpec = SecretKeySpec(k, "AES")
|
||||
private val cipher: Cipher = Cipher.getInstance("AES/ECB/NoPadding")
|
||||
|
||||
init {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec)
|
||||
}
|
||||
|
||||
val rand = _rand ?: ByteArray(KEY_SIZE)
|
||||
|
||||
init {
|
||||
if (_rand == null ){
|
||||
val random = SecureRandom()
|
||||
random.nextBytes(rand)
|
||||
}
|
||||
}
|
||||
|
||||
private val opc = cipher.doFinal(MILENAGE_OP) xor MILENAGE_OP
|
||||
private val rand_opc_encrypted = cipher.doFinal(rand xor opc)
|
||||
private val rand_opc_encrypted_opc = rand_opc_encrypted xor opc
|
||||
|
||||
init {
|
||||
rand_opc_encrypted_opc[15] = (rand_opc_encrypted_opc[15].toInt() xor 1).toByte()
|
||||
}
|
||||
|
||||
private val res_ak = cipher.doFinal(rand_opc_encrypted_opc) xor opc
|
||||
|
||||
val res = res_ak.copyOfRange(8, 16)
|
||||
val ak = res_ak.copyOfRange(0, 6)
|
||||
|
||||
private val ck_input = ByteArray(KEY_SIZE)
|
||||
|
||||
init {
|
||||
for (i in 0..15) {
|
||||
ck_input[(i + 12) % 16] = (rand_opc_encrypted[i].toInt() xor opc[i].toInt()).toByte()
|
||||
}
|
||||
ck_input[15] = (ck_input[15].toInt() xor 2).toByte()
|
||||
}
|
||||
|
||||
val ck = cipher.doFinal(ck_input) xor opc
|
||||
|
||||
private val macAInput = ByteArray(KEY_SIZE)
|
||||
|
||||
init {
|
||||
for (i in 0..15) {
|
||||
macAInput[(i + 8) % 16] = (rand_opc_encrypted[i].toInt() xor opc[i].toInt()).toByte()
|
||||
}
|
||||
}
|
||||
|
||||
private val macAFull = cipher.doFinal(macAInput xor opc)
|
||||
private val macA = macAFull.copyOfRange(0, 8)
|
||||
val autn = (ak xor sqn) + MILENAGE_AMF + macA
|
||||
|
||||
init {
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage K: ${k.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage RAND: ${rand.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage SEQ: ${sqn.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage CK: ${ck.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage AUTN: ${autn.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage RES: ${res.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage AK: ${ak.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage OPC: ${opc.toHex()}")
|
||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Milenage MacA: ${macA.toHex()}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val MILENAGE_OP = Hex.decode("cdc202d5123e20f62b6d676ac72cb318")
|
||||
private val MILENAGE_AMF = Hex.decode("b9b9")
|
||||
private const val KEY_SIZE = 16
|
||||
private const val SEQ_SIZE = 6
|
||||
}
|
||||
}
|
||||
|
||||
private infix fun ByteArray.xor(other: ByteArray): ByteArray {
|
||||
val out = ByteArray(size)
|
||||
for (i in indices) out[i] = (this[i].toInt() xor other[i].toInt()).toByte()
|
||||
return out
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
|
||||
|
||||
class SessionKeys {
|
||||
}
|
||||
class SessionKeys
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
|
||||
|
||||
import info.nightscout.androidaps.logging.AAPSLoggerTest
|
||||
import info.nightscout.androidaps.utils.extensions.toHex
|
||||
import okio.ByteString.Companion.decodeHex
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import org.spongycastle.util.encoders.Hex
|
||||
|
||||
class MilenageTest {
|
||||
|
||||
@Test fun testMilenage() {
|
||||
val aapsLogger = AAPSLoggerTest()
|
||||
val m = Milenage(
|
||||
aapsLogger,
|
||||
Hex.decode("c0772899720972a314f557de66d571dd"),
|
||||
byteArrayOf(0,0,0,0,0,1),
|
||||
Hex.decode("c2cd1248451103bd77a6c7ef88c441ba")
|
||||
)
|
||||
Assert.assertEquals(m.ck.toHex(), "55799fd26664cbf6e476525e2dee52c6")
|
||||
Assert.assertEquals(m.res.toHex(), "a40bc6d13861447e")
|
||||
Assert.assertEquals(m.autn.toHex(), "00c55c78e8d3b9b9e935860a7259f6c0")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue