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
|
||||
api 'com.madgag.spongycastle:core:1.58.0.0'
|
||||
|
||||
|
||||
// Graphview cannot be upgraded
|
||||
api "com.jjoe64:graphview:4.0.1"
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ dependencies {
|
|||
implementation "androidx.room:room-runtime:$room_version"
|
||||
implementation "androidx.room:room-rxjava2:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
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.BluetoothProfile
|
||||
import android.content.Context
|
||||
import com.google.crypto.tink.subtle.X25519
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
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.LinkedBlockingDeque
|
||||
import java.util.concurrent.TimeoutException
|
||||
import javax.crypto.KeyAgreement
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command
|
||||
|
||||
import info.nightscout.androidaps.utils.extensions.toHex
|
||||
|
||||
open class BleCommand(val data: ByteArray) {
|
||||
|
||||
constructor(type: BleCommandType) : this(byteArrayOf(type.value))
|
||||
|
@ -17,6 +19,10 @@ open class BleCommand(val data: ByteArray) {
|
|||
return true
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Raw command: [${data.toHex()}]";
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return data.contentHashCode()
|
||||
}
|
||||
|
|
|
@ -1,31 +1,41 @@
|
|||
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.LTag
|
||||
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.message.MessageIO
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding
|
||||
import info.nightscout.androidaps.utils.extensions.hexStringToByteArray
|
||||
import java.security.SecureRandom
|
||||
|
||||
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? {
|
||||
// send SP1, SP2
|
||||
// TODO: get this from somewhere(preferences?)
|
||||
var seq: Byte = 1
|
||||
val controllerId = Id.fromInt(OmnipodDashBleManagerImpl.CONTROLLER_ID)
|
||||
val nodeId = controllerId.increment()
|
||||
|
||||
var sp1sp2 = sp1sp2(nodeId.address, sp2(), seq, controllerId, nodeId)
|
||||
var sp1sp2 = sp1sp2(nodeId.address, sp2())
|
||||
msgIO.sendMesssage(sp1sp2.messagePacket)
|
||||
|
||||
/*
|
||||
var sps1 =
|
||||
seq++
|
||||
var sps1 = sps1()
|
||||
msgIO.sendMesssage(sps1.messagePacket)
|
||||
// send SPS1
|
||||
|
||||
// read SPS1
|
||||
val podSps1 = msgIO.receiveMessage()
|
||||
|
||||
aapsLogger.info(LTag.PUMPBTCOMM, "Received message: %s", podSps1)
|
||||
/*
|
||||
// send SPS2
|
||||
var sps2 = PairMessage()
|
||||
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()
|
||||
}
|
||||
|
||||
fun sp1sp2(sp1: ByteArray, sp2: ByteArray, seq: Byte, controllerId: Id, nodeId: Id): PairMessage {
|
||||
fun sp1sp2(sp1: ByteArray, sp2: ByteArray): PairMessage {
|
||||
val payload = StringLengthPrefixEncoding.formatKeys(
|
||||
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 {
|
||||
|
||||
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 {
|
||||
val expectRTS = bleIO.receivePacket(CharacteristicType.CMD)
|
||||
if (BleCommand(expectRTS) != BleCommandCTS()) {
|
||||
if (BleCommand(expectRTS) != BleCommandRTS()) {
|
||||
throw UnexpectedCommandException(BleCommand(expectRTS))
|
||||
}
|
||||
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandCTS().data)
|
||||
|
@ -53,7 +53,7 @@ class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) {
|
|||
}
|
||||
}
|
||||
if (joiner.oneExtra) {
|
||||
var data = bleIO.receivePacket(CharacteristicType.DATA)
|
||||
data = bleIO.receivePacket(CharacteristicType.DATA)
|
||||
val accumulateAction = joiner.accumulate(data)
|
||||
if (accumulateAction is PayloadJoinerActionReject) {
|
||||
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandNack(accumulateAction.idx).data)
|
||||
|
|
|
@ -25,6 +25,7 @@ internal class PayloadSplitter(private val payload: ByteArray) {
|
|||
ret.add(LastOptionalPlusOneBlePacket(
|
||||
index = 1,
|
||||
payload = payload.copyOfRange(end, payload.size),
|
||||
size = (payload.size-end).toByte(),
|
||||
))
|
||||
}
|
||||
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),
|
||||
crc32 = crc32,
|
||||
))
|
||||
if (rest > 14) {
|
||||
if (rest > LastBlePacket.CAPACITY) {
|
||||
ret.add(LastOptionalPlusOneBlePacket(
|
||||
index = (middleFragments + 2).toByte(),
|
||||
size = (rest-LastBlePacket.CAPACITY).toByte(),
|
||||
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
|
||||
|
||||
companion object {
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -33,6 +34,7 @@ data class FirstBlePacket(val totalFragments: Byte, val payload: ByteArray, val
|
|||
}
|
||||
|
||||
companion object {
|
||||
|
||||
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_THE_OPTIONAL_PLUS_ONE_PACKET = 18
|
||||
|
@ -46,6 +48,7 @@ data class MiddleBlePacket(val index: Byte, val payload: ByteArray) : BlePacket(
|
|||
}
|
||||
|
||||
companion object {
|
||||
|
||||
internal const val CAPACITY = 19
|
||||
}
|
||||
}
|
||||
|
@ -66,14 +69,15 @@ data class LastBlePacket(val index: Byte, val size: Byte, val payload: ByteArray
|
|||
}
|
||||
|
||||
companion object {
|
||||
|
||||
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 {
|
||||
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)
|
||||
|
||||
private fun validatePodId() {
|
||||
val scanRecord = scanResult.scanRecord
|
||||
val serviceUUIDs = scanRecord.serviceUuids
|
||||
val hexPodId = extractUUID16(serviceUUIDs[3]) + extractUUID16(serviceUUIDs[4])
|
||||
val podId = hexPodId.toLong(16)
|
||||
|
|
Loading…
Reference in a new issue