This commit is contained in:
Andrei Vereha 2021-04-04 13:32:09 +02:00
parent 64fbea6afe
commit ff6a67cc97
4 changed files with 91 additions and 63 deletions

View file

@ -137,6 +137,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
private fun establishSession(msgSeq: Byte) { private fun establishSession(msgSeq: Byte) {
val conn = connection ?: throw FailedToConnectException("connection lost") val conn = connection ?: throw FailedToConnectException("connection lost")
val ltk: ByteArray = podState.ltk ?: throw FailedToConnectException("Missing LTK, activate the pod first") val ltk: ByteArray = podState.ltk ?: throw FailedToConnectException("Missing LTK, activate the pod first")
val uniqueId = podState.uniqueId val uniqueId = podState.uniqueId
val podId = uniqueId?.let { Id.fromLong(uniqueId) } val podId = uniqueId?.let { Id.fromLong(uniqueId) }
?: myId.increment() // pod not activated ?: myId.increment() // pod not activated

View file

@ -97,20 +97,18 @@ open class BleIO(
* @return * @return
*/ */
fun readyToRead(): BleSendResult { fun readyToRead(): BleSendResult {
val notificationSet = gatt.setCharacteristicNotification(characteristic, true) gatt.setCharacteristicNotification(characteristic, true)
if (!notificationSet) { .assertTrue("enable notifications")
throw ConnectException("Could not enable notifications")
}
val descriptors = characteristic.descriptors val descriptors = characteristic.descriptors
if (descriptors.size != 1) { if (descriptors.size != 1) {
throw ConnectException("Expecting one descriptor, found: ${descriptors.size}") throw ConnectException("Expecting one descriptor, found: ${descriptors.size}")
} }
val descriptor = descriptors[0] val descriptor = descriptors[0]
descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
val wrote = gatt.writeDescriptor(descriptor) gatt.writeDescriptor(descriptor)
if (!wrote) { .assertTrue("enable indications on descriptor")
throw ConnectException("Could not enable indications on descriptor")
}
aapsLogger.debug(LTag.PUMPBTCOMM, "Enabling indications for $type") aapsLogger.debug(LTag.PUMPBTCOMM, "Enabling indications for $type")
val confirmation = bleCommCallbacks.confirmWrite( val confirmation = bleCommCallbacks.confirmWrite(
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE,
@ -130,3 +128,9 @@ open class BleIO(
const val DEFAULT_IO_TIMEOUT_MS = 1000.toLong() const val DEFAULT_IO_TIMEOUT_MS = 1000.toLong()
} }
} }
private fun Boolean.assertTrue(operation: String) {
if (!this) {
throw ConnectException("Could not $operation")
}
}

View file

@ -62,13 +62,11 @@ data class EapMessage(
private const val AKA_PACKET_TYPE = 0x17.toByte() private const val AKA_PACKET_TYPE = 0x17.toByte()
fun parse(aapsLogger: AAPSLogger, payload: ByteArray): EapMessage { fun parse(aapsLogger: AAPSLogger, payload: ByteArray): EapMessage {
if (payload.size < 4) { payload.assertSizeAtLeast(4)
throw MessageIOException("Invalid eap payload: ${payload.toHex()}")
}
val totalSize = (payload[2].toInt() shl 8) or payload[3].toInt() val totalSize = (payload[2].toInt() shl 8) or payload[3].toInt()
if (totalSize > payload.size) { payload.assertSizeAtLeast(totalSize)
throw MessageIOException("Invalid eap payload. Too short: ${payload.toHex()}")
}
if (payload.size == 4) { // SUCCESS/FAILURE if (payload.size == 4) { // SUCCESS/FAILURE
return EapMessage( return EapMessage(
code = EapCode.byValue(payload[0]), code = EapCode.byValue(payload[0]),
@ -90,3 +88,9 @@ data class EapMessage(
} }
} }
} }
private fun ByteArray.assertSizeAtLeast(size: Int) {
if (this.size < size) {
throw MessageIOException("Payload too short: ${this.toHex()}")
}
}

View file

@ -90,65 +90,35 @@ class SessionEstablisher(
) )
} }
private fun processChallengeResponse(challengeResponse: MessagePacket): EapSqn? { private fun assertIdentifier(msg: EapMessage) {
val eapMsg = EapMessage.parse(aapsLogger, challengeResponse.payload) if (msg.identifier != identifier) {
if (eapMsg.identifier != identifier) {
aapsLogger.debug( aapsLogger.debug(
LTag.PUMPBTCOMM, LTag.PUMPBTCOMM,
"EAP-AKA: got incorrect identifier ${eapMsg.identifier} expected: $identifier" "EAP-AKA: got incorrect identifier ${msg.identifier} expected: $identifier"
) )
throw SessionEstablishmentException("Received incorrect EAP identifier: ${eapMsg.identifier}") throw SessionEstablishmentException("Received incorrect EAP identifier: ${msg.identifier}")
}
}
private fun processChallengeResponse(challengeResponse: MessagePacket): EapSqn? {
val eapMsg = EapMessage.parse(aapsLogger, challengeResponse.payload)
assertIdentifier(eapMsg)
val eapSqn = isResynchronization(eapMsg)
if (eapSqn != null) {
return eapSqn
} }
if (eapMsg.subType == EapMessage.SUBTYPE_SYNCRONIZATION_FAILURE && assertValidAkaMessage(eapMsg)
eapMsg.attributes.size == 1 &&
eapMsg.attributes[0] is EapAkaAttributeAuts
) {
val auts = eapMsg.attributes[0] as EapAkaAttributeAuts
val autsMilenage = Milenage(
aapsLogger = aapsLogger,
k = ltk,
sqn = eapSqn,
randParam = milenage.rand,
auts = auts.payload
)
val newSqnMilenage = Milenage(
aapsLogger = aapsLogger,
k = ltk,
sqn = autsMilenage.synchronizationSqn,
randParam = milenage.rand,
auts = auts.payload,
amf = Milenage.RESYNC_AMF,
)
if (!newSqnMilenage.macS.contentEquals(newSqnMilenage.receivedMacS)) {
throw SessionEstablishmentException(
"MacS mismatch. " +
"Expected: ${newSqnMilenage.macS.toHex()}. " +
"Received: ${newSqnMilenage.receivedMacS.toHex()}"
)
}
return EapSqn(autsMilenage.synchronizationSqn)
}
if (eapMsg.attributes.size != 2) {
aapsLogger.debug(LTag.PUMPBTCOMM, "EAP-AKA: got message: $eapMsg")
if (eapMsg.attributes.size == 1 && eapMsg.attributes[0] is EapAkaAttributeClientErrorCode) {
throw SessionEstablishmentException(
"Received CLIENT_ERROR_CODE for EAP-AKA challenge: ${
eapMsg.attributes[0].toByteArray().toHex()
}"
)
}
throw SessionEstablishmentException("Expecting two attributes, got: ${eapMsg.attributes.size}")
}
for (attr in eapMsg.attributes) { for (attr in eapMsg.attributes) {
when (attr) { when (attr) {
is EapAkaAttributeRes -> is EapAkaAttributeRes ->
if (!milenage.res.contentEquals(attr.payload)) { if (!milenage.res.contentEquals(attr.payload)) {
throw SessionEstablishmentException("RES mismatch. Expected: ${milenage.res.toHex()} Actual: ${attr.payload.toHex()} ") throw SessionEstablishmentException("RES mismatch." +
"Expected: ${milenage.res.toHex()}." +
"Actual: ${attr.payload.toHex()}.")
} }
is EapAkaAttributeCustomIV -> is EapAkaAttributeCustomIV ->
nodeIV = attr.payload.copyOfRange(0, IV_SIZE) nodeIV = attr.payload.copyOfRange(0, IV_SIZE)
@ -159,6 +129,55 @@ class SessionEstablisher(
return null return null
} }
private fun assertValidAkaMessage(eapMsg: EapMessage) {
if (eapMsg.attributes.size != 2) {
aapsLogger.debug(LTag.PUMPBTCOMM, "EAP-AKA: got incorrect: $eapMsg")
if (eapMsg.attributes.size == 1 && eapMsg.attributes[0] is EapAkaAttributeClientErrorCode) {
throw SessionEstablishmentException(
"Received CLIENT_ERROR_CODE for EAP-AKA challenge: ${
eapMsg.attributes[0].toByteArray().toHex()
}"
)
}
throw SessionEstablishmentException("Expecting two attributes, got: ${eapMsg.attributes.size}")
}
}
private fun isResynchronization(eapMsg: EapMessage): EapSqn? {
if (eapMsg.subType != EapMessage.SUBTYPE_SYNCRONIZATION_FAILURE ||
eapMsg.attributes.size != 1 ||
eapMsg.attributes[0] !is EapAkaAttributeAuts)
return null
val auts = eapMsg.attributes[0] as EapAkaAttributeAuts
val autsMilenage = Milenage(
aapsLogger = aapsLogger,
k = ltk,
sqn = eapSqn,
randParam = milenage.rand,
auts = auts.payload
)
val newSqnMilenage = Milenage(
aapsLogger = aapsLogger,
k = ltk,
sqn = autsMilenage.synchronizationSqn,
randParam = milenage.rand,
auts = auts.payload,
amf = Milenage.RESYNC_AMF,
)
if (!newSqnMilenage.macS.contentEquals(newSqnMilenage.receivedMacS)) {
throw SessionEstablishmentException(
"MacS mismatch. " +
"Expected: ${newSqnMilenage.macS.toHex()}. " +
"Received: ${newSqnMilenage.receivedMacS.toHex()}"
)
}
return EapSqn(autsMilenage.synchronizationSqn)
}
private fun eapSuccess(): MessagePacket { private fun eapSuccess(): MessagePacket {
val eapMsg = EapMessage( val eapMsg = EapMessage(
code = EapCode.SUCCESS, code = EapCode.SUCCESS,