Skip to content

Commit

Permalink
Merge pull request #19 from 0pen-dash/andrei/encryption
Browse files Browse the repository at this point in the history
andrei/encryption
  • Loading branch information
avereha committed Mar 9, 2021
2 parents bef32e7 + c337b52 commit 034cbef
Show file tree
Hide file tree
Showing 31 changed files with 631 additions and 168 deletions.
Expand Up @@ -49,7 +49,8 @@ class OmnipodDashManagerImpl @Inject constructor(

private val observeConnectToPod: Observable<PodEvent>
get() = Observable.defer {
bleManager.connect().retryWithBackoff(retries = 2, delay = 3, timeUnit = TimeUnit.SECONDS)
bleManager.connect()

} // TODO are these reasonable values?

private fun observeSendProgramBolusCommand(
Expand Down
Expand Up @@ -36,5 +36,8 @@ data class Id(val address: ByteArray) {
fun fromInt(v: Int): Id {
return Id(ByteBuffer.allocate(4).putInt(v).array())
}
fun fromLong(v: Long): Id {
return Id(ByteBuffer.allocate(8).putLong(v).array().copyOfRange(4, 8))
}
}
}
Expand Up @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
Expand All @@ -10,19 +11,22 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommandHello
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CharacteristicType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.LTKExchanger
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.Session
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.SessionEstablisher
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.SessionKeys
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status.ConnectionStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.utils.extensions.toHex
import io.reactivex.Observable
import org.apache.commons.lang3.NotImplementedException
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.TimeoutException
Expand All @@ -32,12 +36,17 @@ import javax.inject.Singleton
@Singleton
class OmnipodDashBleManagerImpl @Inject constructor(
private val context: Context,
private val aapsLogger: AAPSLogger
private val aapsLogger: AAPSLogger,
private val podState: OmnipodDashPodStateManager
) : OmnipodDashBleManager {

private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
private var sessionKeys: SessionKeys? = null
private var msgIO: MessageIO? = null
private var gatt: BluetoothGatt? = null
private var status: ConnectionStatus = ConnectionStatus.IDLE

@Throws(
FailedToConnectException::class,
Expand All @@ -50,44 +59,73 @@ class OmnipodDashBleManagerImpl @Inject constructor(
DescriptorNotFoundException::class,
CouldNotConfirmDescriptorWriteException::class
)
private fun connect(podAddress: String): BleIO {
// TODO: locking?
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
private fun connect(podDevice: BluetoothDevice): BleIO {
val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
mapOf(
CharacteristicType.CMD to LinkedBlockingDeque(),
CharacteristicType.DATA to LinkedBlockingDeque()
)
val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to $podAddress")
var autoConnect = true
if (BuildConfig.DEBUG) {
autoConnect = false
// TODO: remove this in the future
// it's easier to start testing from scratch on each run.
}
val gatt = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}")
val autoConnect = false // TODO: check what to use here

val gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
val connectionState = bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT)
aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: $connectionState")
if (connectionState != BluetoothProfile.STATE_CONNECTED) {
throw FailedToConnectException(podAddress)
throw FailedToConnectException(podDevice.address)
}
val discoverer = ServiceDiscoverer(aapsLogger, gatt, bleCommCallbacks)
val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
val chars = discoverer.discoverServices()
val bleIO = BleIO(aapsLogger, chars, incomingPackets, gatt, bleCommCallbacks)
val bleIO = BleIO(aapsLogger, chars, incomingPackets, gattConnection, bleCommCallbacks)
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandHello(CONTROLLER_ID).data)
bleIO.readyToRead()
gatt = gattConnection
return bleIO
}

override fun sendCommand(cmd: Command): Observable<PodEvent> {
// TODO
return Observable.error(NotImplementedException("sendCommand is not yet implemented"))
override fun sendCommand(cmd: Command): Observable<PodEvent> = Observable.create { emitter ->
try {
val keys = sessionKeys
val mIO = msgIO
if (keys == null || mIO == null) {
// TODO handle reconnects
throw Exception("Not connected")
}
emitter.onNext(PodEvent.CommandSending(cmd))
// TODO switch to RX
emitter.onNext(PodEvent.CommandSent(cmd))

val enDecrypt = EnDecrypt(
aapsLogger,
keys.nonce,
keys.ck
)

val session = Session(
aapsLogger = aapsLogger,
msgIO = mIO,
myId = Id.fromInt(CONTROLLER_ID),
podId = Id.fromInt(CONTROLLER_ID).increment(),
sessionKeys = keys,
enDecrypt = enDecrypt
)
val response = session.sendCommand(cmd)
emitter.onNext(PodEvent.ResponseReceived(response))

emitter.onComplete()
} catch (ex: Exception) {
emitter.tryOnError(ex)
}
}

override fun getStatus(): ConnectionStatus {
TODO("not implemented")
var s: ConnectionStatus
synchronized(status) {
s = status
}
return s
}

@Throws(
Expand All @@ -102,54 +140,91 @@ class OmnipodDashBleManagerImpl @Inject constructor(
DescriptorNotFoundException::class,
CouldNotConfirmDescriptorWriteException::class
)

override fun connect(): Observable<PodEvent> = Observable.create { emitter ->
// TODO: when we are already connected,
// emit PodEvent.AlreadyConnected, complete the observable and return from this method

try {
// TODO: this is wrong and I know it
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")

val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
emitter.onNext(PodEvent.Scanning)

val podAddress = podScanner.scanForPod(
PodScanner.SCAN_FOR_SERVICE_UUID,
PodScanner.POD_ID_NOT_ACTIVATED
).scanResult.device.address
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
if (podState.bluetoothAddress == null) {
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")

val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
emitter.onNext(PodEvent.Scanning)

val podAddress = podScanner.scanForPod(
PodScanner.SCAN_FOR_SERVICE_UUID,
PodScanner.POD_ID_NOT_ACTIVATED
).scanResult.device.address
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
podState.bluetoothAddress = podAddress
}
emitter.onNext(PodEvent.BluetoothConnecting)
val podAddress = podState.bluetoothAddress ?: throw FailedToConnectException("Lost connection")
// check if already connected
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
val connectionState = bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT)
aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: $connectionState")

val bleIO = connect(podAddress)
emitter.onNext(PodEvent.BluetoothConnected(podAddress))

val msgIO = MessageIO(aapsLogger, bleIO)
val ltkExchanger = LTKExchanger(aapsLogger, msgIO)

emitter.onNext(PodEvent.Pairing)

val ltk = ltkExchanger.negotiateLTK()

aapsLogger.info(LTag.PUMPCOMM, "Got LTK: ${ltk.ltk.toHex()}")
if (connectionState == BluetoothProfile.STATE_CONNECTED) {
podState.uniqueId ?: throw FailedToConnectException("Already connection and uniqueId is missing")
emitter.onNext(PodEvent.AlreadyConnected(podAddress, podState.uniqueId ?: 0))
emitter.onComplete()
return@create
}
if (msgIO != null) {
disconnect()
}

val bleIO = connect(podDevice)
val mIO = MessageIO(aapsLogger, bleIO)
val myId = Id.fromInt(CONTROLLER_ID)
val podId = myId.increment()
var msgSeq = 1.toByte()
val ltkExchanger = LTKExchanger(aapsLogger, mIO, myId, podId, Id.fromLong(PodScanner.POD_ID_NOT_ACTIVATED))
if (podState.ltk == null) {
emitter.onNext(PodEvent.Pairing)
val pairResult = ltkExchanger.negotiateLTK()
podState.ltk = pairResult.ltk
podState.uniqueId = podId.toLong()
msgSeq = pairResult.msgSeq
podState.eapAkaSequenceNumber = 1
if (BuildConfig.DEBUG) {
aapsLogger.info(LTag.PUMPCOMM, "Got LTK: ${pairResult.ltk.toHex()}")
}
}

val ltk: ByteArray = podState.ltk!!

emitter.onNext(PodEvent.EstablishingSession)
val eapSqn = podState.increaseEapAkaSequenceNumber()
val eapAkaExchanger = SessionEstablisher(aapsLogger, mIO, ltk, eapSqn, myId, podId, msgSeq)
val keys = eapAkaExchanger.negotiateSessionKeys()
podState.commitEapAkaSequenceNumber()

val eapAkaExchanger = SessionEstablisher(aapsLogger, msgIO, ltk)
val sessionKeys = eapAkaExchanger.negotiateSessionKeys()
aapsLogger.info(LTag.PUMPCOMM, "CK: ${sessionKeys.ck.toHex()}")
aapsLogger.info(LTag.PUMPCOMM, "noncePrefix: ${sessionKeys.noncePrefix.toHex()}")
aapsLogger.info(LTag.PUMPCOMM, "SQN: ${sessionKeys.sqn.toHex()}")
if (BuildConfig.DEBUG) {
aapsLogger.info(LTag.PUMPCOMM, "CK: ${keys.ck.toHex()}")
aapsLogger.info(LTag.PUMPCOMM, "msgSequenceNumber: ${keys.msgSequenceNumber}")
aapsLogger.info(LTag.PUMPCOMM, "Nonce: ${keys.nonce}")
}
sessionKeys = keys
msgIO = mIO

emitter.onNext(PodEvent.Connected(ltk.podId.toLong())) // TODO supply actual pod id
emitter.onNext(PodEvent.Connected(podId.toLong()))

emitter.onComplete()
} catch (ex: Exception) {
disconnect()
emitter.tryOnError(ex)
}
}

override fun disconnect() {
TODO("not implemented")
val localGatt = gatt
localGatt?.close() // TODO: use disconnect?
gatt = null
msgIO = null
sessionKeys = null
}

companion object {
Expand Down
Expand Up @@ -30,7 +30,7 @@ class BleCommCallbacks(

override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
super.onConnectionStateChange(gatt, status, newState)
aapsLogger.debug(LTag.PUMPBTCOMM, "OnConnectionStateChange discovered with status/state$status/$newState")
aapsLogger.debug(LTag.PUMPBTCOMM, "OnConnectionStateChange with status/state: $status/$newState")
if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
connected.countDown()
}
Expand Down
Expand Up @@ -2,6 +2,16 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command

import info.nightscout.androidaps.utils.extensions.toHex

class BleCommandRTS : BleCommand(BleCommandType.RTS)

class BleCommandCTS : BleCommand(BleCommandType.CTS)

class BleCommandAbort : BleCommand(BleCommandType.ABORT)

class BleCommandSuccess : BleCommand(BleCommandType.SUCCESS)

class BleCommandFail : BleCommand(BleCommandType.FAIL)

open class BleCommand(val data: ByteArray) {

constructor(type: BleCommandType) : this(byteArrayOf(type.value))
Expand All @@ -26,14 +36,5 @@ open class BleCommand(val data: ByteArray) {
override fun hashCode(): Int {
return data.contentHashCode()
}
}

class BleCommandRTS : BleCommand(BleCommandType.RTS)

class BleCommandCTS : BleCommand(BleCommandType.CTS)

class BleCommandAbort : BleCommand(BleCommandType.ABORT)

class BleCommandSuccess : BleCommand(BleCommandType.SUCCESS)

class BleCommandFail : BleCommand(BleCommandType.FAIL)
}
Expand Up @@ -7,9 +7,7 @@ enum class BleCommandType(val value: Byte) {
ABORT(0x03.toByte()),
SUCCESS(0x04.toByte()),
FAIL(0x05.toByte()),
HELLO(
0x06.toByte()
);
HELLO(0x06.toByte());

companion object {

Expand Down

0 comments on commit 034cbef

Please sign in to comment.