implement SPS1 command

I had to add a new dependency: `tink-android:1.5.0` for X25519
This commit is contained in:
Andrei Vereha 2021-02-26 23:56:39 +01:00
parent 11633995f6
commit 81ad52ebce
9 changed files with 56 additions and 19 deletions

View file

@ -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"

View file

@ -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'
}

View file

@ -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

View file

@ -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()
}

View file

@ -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.

View file

@ -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)

View file

@ -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),
))
}

View file

@ -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
}
}

View file

@ -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)