implement SPS1 command
I had to add a new dependency: `tink-android:1.5.0` for X25519
This commit is contained in:
parent
11633995f6
commit
81ad52ebce
9 changed files with 56 additions and 19 deletions
|
@ -47,6 +47,7 @@ dependencies {
|
||||||
//CryptoUtil
|
//CryptoUtil
|
||||||
api 'com.madgag.spongycastle:core:1.58.0.0'
|
api 'com.madgag.spongycastle:core:1.58.0.0'
|
||||||
|
|
||||||
|
|
||||||
// Graphview cannot be upgraded
|
// Graphview cannot be upgraded
|
||||||
api "com.jjoe64:graphview:4.0.1"
|
api "com.jjoe64:graphview:4.0.1"
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ dependencies {
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
implementation "androidx.room:room-rxjava2:$room_version"
|
implementation "androidx.room:room-rxjava2:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
implementation 'com.github.guepardoapps:kulid:1.1.2.0'
|
implementation 'com.github.guepardoapps:kulid:1.1.2.0'
|
||||||
|
implementation 'com.google.crypto.tink:tink-android:1.5.0'
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.bluetooth.BluetoothDevice
|
||||||
import android.bluetooth.BluetoothManager
|
import android.bluetooth.BluetoothManager
|
||||||
import android.bluetooth.BluetoothProfile
|
import android.bluetooth.BluetoothProfile
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.google.crypto.tink.subtle.X25519
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
|
||||||
|
@ -24,6 +25,7 @@ import org.apache.commons.lang3.NotImplementedException
|
||||||
import java.util.concurrent.BlockingQueue
|
import java.util.concurrent.BlockingQueue
|
||||||
import java.util.concurrent.LinkedBlockingDeque
|
import java.util.concurrent.LinkedBlockingDeque
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
import javax.crypto.KeyAgreement
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.utils.extensions.toHex
|
||||||
|
|
||||||
open class BleCommand(val data: ByteArray) {
|
open class BleCommand(val data: ByteArray) {
|
||||||
|
|
||||||
constructor(type: BleCommandType) : this(byteArrayOf(type.value))
|
constructor(type: BleCommandType) : this(byteArrayOf(type.value))
|
||||||
|
@ -17,6 +19,10 @@ open class BleCommand(val data: ByteArray) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Raw command: [${data.toHex()}]";
|
||||||
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return data.contentHashCode()
|
return data.contentHashCode()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,41 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.ltk
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.ltk
|
||||||
|
|
||||||
|
import com.google.crypto.tink.subtle.X25519
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl
|
||||||
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.MessageIO
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding
|
||||||
import info.nightscout.androidaps.utils.extensions.hexStringToByteArray
|
import info.nightscout.androidaps.utils.extensions.hexStringToByteArray
|
||||||
|
import java.security.SecureRandom
|
||||||
|
|
||||||
internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgIO: MessageIO) {
|
internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgIO: MessageIO) {
|
||||||
|
private val privateKey = X25519.generatePrivateKey()
|
||||||
|
private val nonce = ByteArray(16)
|
||||||
|
private val controllerId = Id.fromInt(OmnipodDashBleManagerImpl.CONTROLLER_ID)
|
||||||
|
val nodeId = controllerId.increment()
|
||||||
|
private var seq: Byte = 1
|
||||||
|
|
||||||
|
init{
|
||||||
|
val random = SecureRandom()
|
||||||
|
random.nextBytes(nonce)
|
||||||
|
}
|
||||||
|
|
||||||
fun negotiateLTKAndNonce(): LTK? {
|
fun negotiateLTKAndNonce(): LTK? {
|
||||||
// send SP1, SP2
|
// send SP1, SP2
|
||||||
// TODO: get this from somewhere(preferences?)
|
var sp1sp2 = sp1sp2(nodeId.address, sp2())
|
||||||
var seq: Byte = 1
|
|
||||||
val controllerId = Id.fromInt(OmnipodDashBleManagerImpl.CONTROLLER_ID)
|
|
||||||
val nodeId = controllerId.increment()
|
|
||||||
|
|
||||||
var sp1sp2 = sp1sp2(nodeId.address, sp2(), seq, controllerId, nodeId)
|
|
||||||
msgIO.sendMesssage(sp1sp2.messagePacket)
|
msgIO.sendMesssage(sp1sp2.messagePacket)
|
||||||
|
|
||||||
/*
|
seq++
|
||||||
var sps1 =
|
var sps1 = sps1()
|
||||||
msgIO.sendMesssage(sps1.messagePacket)
|
msgIO.sendMesssage(sps1.messagePacket)
|
||||||
// send SPS1
|
// send SPS1
|
||||||
|
|
||||||
// read SPS1
|
// read SPS1
|
||||||
val podSps1 = msgIO.receiveMessage()
|
val podSps1 = msgIO.receiveMessage()
|
||||||
|
aapsLogger.info(LTag.PUMPBTCOMM, "Received message: %s", podSps1)
|
||||||
|
/*
|
||||||
// send SPS2
|
// send SPS2
|
||||||
var sps2 = PairMessage()
|
var sps2 = PairMessage()
|
||||||
msgIO.sendMesssage(sps2.messagePacket)
|
msgIO.sendMesssage(sps2.messagePacket)
|
||||||
|
@ -46,7 +56,7 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
|
||||||
return GET_POD_STATUS_HEX_COMMAND.hexStringToByteArray()
|
return GET_POD_STATUS_HEX_COMMAND.hexStringToByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sp1sp2(sp1: ByteArray, sp2: ByteArray, seq: Byte, controllerId: Id, nodeId: Id): PairMessage {
|
fun sp1sp2(sp1: ByteArray, sp2: ByteArray): PairMessage {
|
||||||
val payload = StringLengthPrefixEncoding.formatKeys(
|
val payload = StringLengthPrefixEncoding.formatKeys(
|
||||||
arrayOf("SP1=", ",SP2="),
|
arrayOf("SP1=", ",SP2="),
|
||||||
arrayOf(sp1, sp2),
|
arrayOf(sp1, sp2),
|
||||||
|
@ -59,6 +69,20 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sps1(): PairMessage {
|
||||||
|
val publicKey = X25519.publicFromPrivate(privateKey)
|
||||||
|
val payload = StringLengthPrefixEncoding.formatKeys(
|
||||||
|
arrayOf("SPS1="),
|
||||||
|
arrayOf(publicKey+nonce),
|
||||||
|
)
|
||||||
|
return PairMessage(
|
||||||
|
sequenceNumber = seq,
|
||||||
|
source = controllerId,
|
||||||
|
destination = nodeId,
|
||||||
|
payload = payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val GET_POD_STATUS_HEX_COMMAND = "ffc32dbd08030e0100008a" // TODO for now we are assuming this command is build out of constant parameters, use a proper command builder for that.
|
private val GET_POD_STATUS_HEX_COMMAND = "ffc32dbd08030e0100008a" // TODO for now we are assuming this command is build out of constant parameters, use a proper command builder for that.
|
||||||
|
|
|
@ -38,7 +38,7 @@ class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) {
|
||||||
|
|
||||||
fun receiveMessage(): MessagePacket {
|
fun receiveMessage(): MessagePacket {
|
||||||
val expectRTS = bleIO.receivePacket(CharacteristicType.CMD)
|
val expectRTS = bleIO.receivePacket(CharacteristicType.CMD)
|
||||||
if (BleCommand(expectRTS) != BleCommandCTS()) {
|
if (BleCommand(expectRTS) != BleCommandRTS()) {
|
||||||
throw UnexpectedCommandException(BleCommand(expectRTS))
|
throw UnexpectedCommandException(BleCommand(expectRTS))
|
||||||
}
|
}
|
||||||
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandCTS().data)
|
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandCTS().data)
|
||||||
|
@ -53,7 +53,7 @@ class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (joiner.oneExtra) {
|
if (joiner.oneExtra) {
|
||||||
var data = bleIO.receivePacket(CharacteristicType.DATA)
|
data = bleIO.receivePacket(CharacteristicType.DATA)
|
||||||
val accumulateAction = joiner.accumulate(data)
|
val accumulateAction = joiner.accumulate(data)
|
||||||
if (accumulateAction is PayloadJoinerActionReject) {
|
if (accumulateAction is PayloadJoinerActionReject) {
|
||||||
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandNack(accumulateAction.idx).data)
|
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandNack(accumulateAction.idx).data)
|
||||||
|
|
|
@ -25,6 +25,7 @@ internal class PayloadSplitter(private val payload: ByteArray) {
|
||||||
ret.add(LastOptionalPlusOneBlePacket(
|
ret.add(LastOptionalPlusOneBlePacket(
|
||||||
index = 1,
|
index = 1,
|
||||||
payload = payload.copyOfRange(end, payload.size),
|
payload = payload.copyOfRange(end, payload.size),
|
||||||
|
size = (payload.size-end).toByte(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
@ -53,9 +54,10 @@ internal class PayloadSplitter(private val payload: ByteArray) {
|
||||||
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS, middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + end),
|
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS, middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + end),
|
||||||
crc32 = crc32,
|
crc32 = crc32,
|
||||||
))
|
))
|
||||||
if (rest > 14) {
|
if (rest > LastBlePacket.CAPACITY) {
|
||||||
ret.add(LastOptionalPlusOneBlePacket(
|
ret.add(LastOptionalPlusOneBlePacket(
|
||||||
index = (middleFragments + 2).toByte(),
|
index = (middleFragments + 2).toByte(),
|
||||||
|
size = (rest-LastBlePacket.CAPACITY).toByte(),
|
||||||
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + LastBlePacket.CAPACITY, payload.size),
|
payload = payload.copyOfRange(middleFragments * MiddleBlePacket.CAPACITY + FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + LastBlePacket.CAPACITY, payload.size),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ sealed class BlePacket {
|
||||||
abstract fun asByteArray(): ByteArray
|
abstract fun asByteArray(): ByteArray
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val MAX_BLE_PACKET_LEN = 20
|
const val MAX_BLE_PACKET_LEN = 20
|
||||||
const val MAX_BLE_BUFFER_LEN = MAX_BLE_PACKET_LEN + 1 // we use this as the size allocated for the ByteBuffer
|
const val MAX_BLE_BUFFER_LEN = MAX_BLE_PACKET_LEN + 1 // we use this as the size allocated for the ByteBuffer
|
||||||
}
|
}
|
||||||
|
@ -33,6 +34,7 @@ data class FirstBlePacket(val totalFragments: Byte, val payload: ByteArray, val
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
internal const val CAPACITY_WITHOUT_MIDDLE_PACKETS = 13 // we are using all fields
|
internal const val CAPACITY_WITHOUT_MIDDLE_PACKETS = 13 // we are using all fields
|
||||||
internal const val CAPACITY_WITH_MIDDLE_PACKETS = 18 // we are not using crc32 or size
|
internal const val CAPACITY_WITH_MIDDLE_PACKETS = 18 // we are not using crc32 or size
|
||||||
internal const val CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET = 18
|
internal const val CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET = 18
|
||||||
|
@ -46,6 +48,7 @@ data class MiddleBlePacket(val index: Byte, val payload: ByteArray) : BlePacket(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
internal const val CAPACITY = 19
|
internal const val CAPACITY = 19
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,14 +69,15 @@ data class LastBlePacket(val index: Byte, val size: Byte, val payload: ByteArray
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
internal const val CAPACITY = 14
|
internal const val CAPACITY = 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class LastOptionalPlusOneBlePacket(val index: Byte, val payload: ByteArray) : BlePacket() {
|
data class LastOptionalPlusOneBlePacket(val index: Byte, val payload: ByteArray, val size: Byte) : BlePacket() {
|
||||||
|
|
||||||
override fun asByteArray(): ByteArray {
|
override fun asByteArray(): ByteArray {
|
||||||
return byteArrayOf(index) + payload
|
return byteArrayOf(index, size) + payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ class BleDiscoveredDevice(val scanResult: ScanResult, private val scanRecord: Sc
|
||||||
@Throws(DiscoveredInvalidPodException::class)
|
@Throws(DiscoveredInvalidPodException::class)
|
||||||
|
|
||||||
private fun validatePodId() {
|
private fun validatePodId() {
|
||||||
val scanRecord = scanResult.scanRecord
|
|
||||||
val serviceUUIDs = scanRecord.serviceUuids
|
val serviceUUIDs = scanRecord.serviceUuids
|
||||||
val hexPodId = extractUUID16(serviceUUIDs[3]) + extractUUID16(serviceUUIDs[4])
|
val hexPodId = extractUUID16(serviceUUIDs[3]) + extractUUID16(serviceUUIDs[4])
|
||||||
val podId = hexPodId.toLong(16)
|
val podId = hexPodId.toLong(16)
|
||||||
|
|
Loading…
Reference in a new issue