dash ble: detekt
This commit is contained in:
parent
210a446123
commit
64fbea6afe
7 changed files with 78 additions and 61 deletions
|
@ -46,8 +46,7 @@ data class Id(val address: ByteArray) {
|
|||
|
||||
companion object {
|
||||
|
||||
private const val PERIPHERAL_NODE_INDEX =
|
||||
1 // TODO: understand the meaning of this value. It comes from preferences
|
||||
private const val PERIPHERAL_NODE_INDEX = 1
|
||||
|
||||
fun fromInt(v: Int): Id {
|
||||
return Id(ByteBuffer.allocate(4).putInt(v).array())
|
||||
|
|
|
@ -2,4 +2,5 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
|
|||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.NakResponse
|
||||
|
||||
class NakResponseException(val response: NakResponse) : Exception("Received NAK response: ${response.nakErrorType.value} ${response.nakErrorType.name}")
|
||||
class NakResponseException(val response: NakResponse) :
|
||||
Exception("Received NAK response: ${response.nakErrorType.value} ${response.nakErrorType.name}")
|
||||
|
|
|
@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
|
|||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseMessageException
|
||||
import retrofit2.http.HEAD
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
/***
|
||||
|
@ -75,9 +76,8 @@ data class MessagePacket(
|
|||
private const val HEADER_SIZE = 16
|
||||
|
||||
fun parse(payload: ByteArray): MessagePacket {
|
||||
if (payload.size < HEADER_SIZE) {
|
||||
throw CouldNotParseMessageException(payload)
|
||||
}
|
||||
payload.assertSizeAtLeast(HEADER_SIZE)
|
||||
|
||||
if (payload.copyOfRange(0, 2).decodeToString() != MAGIC_PATTERN) {
|
||||
throw CouldNotParseMessageException(payload)
|
||||
}
|
||||
|
@ -100,9 +100,8 @@ data class MessagePacket(
|
|||
val sequenceNumber = payload[4]
|
||||
val ackNumber = payload[5]
|
||||
val size = (payload[6].toInt() shl 3) or (payload[7].toUnsignedInt() ushr 5)
|
||||
if (size + HEADER_SIZE > payload.size) {
|
||||
throw CouldNotParseMessageException(payload)
|
||||
}
|
||||
payload.assertSizeAtLeast(size + HEADER_SIZE)
|
||||
|
||||
val payloadEnd = 16 + size +
|
||||
if (type == MessageType.ENCRYPTED) 8 // TAG
|
||||
else 0
|
||||
|
@ -146,3 +145,9 @@ private class Flag(var value: Int = 0) {
|
|||
}
|
||||
|
||||
internal fun Byte.toUnsignedInt() = this.toInt() and 0xff
|
||||
|
||||
private fun ByteArray.assertSizeAtLeast(size: Int) {
|
||||
if (this.size < size) {
|
||||
throw CouldNotParseMessageException(this)
|
||||
}
|
||||
}
|
|
@ -17,23 +17,19 @@ class StringLengthPrefixEncoding private constructor() {
|
|||
val ret = Array(keys.size) { ByteArray(0) }
|
||||
var remaining = payload
|
||||
for ((index, key) in keys.withIndex()) {
|
||||
remaining.assertSizeAtLeast(key.length)
|
||||
when {
|
||||
remaining.size < key.length ->
|
||||
throw MessageIOException("Payload too short: ${payload.toHex()} for key: $key")
|
||||
!(remaining.copyOfRange(0, key.length).decodeToString() == key) ->
|
||||
remaining.copyOfRange(0, key.length).decodeToString() != key ->
|
||||
throw MessageIOException("Key not found: $key in ${payload.toHex()}")
|
||||
// last key can be empty, no length
|
||||
index == keys.size - 1 && remaining.size == key.length ->
|
||||
return ret
|
||||
|
||||
remaining.size < key.length + LENGTH_BYTES ->
|
||||
throw MessageIOException("Length not found: for $key in ${payload.toHex()}")
|
||||
}
|
||||
remaining.assertSizeAtLeast(key.length + LENGTH_BYTES)
|
||||
|
||||
remaining = remaining.copyOfRange(key.length, remaining.size)
|
||||
val length = (remaining[0].toUnsignedInt() shl 1) or remaining[1].toUnsignedInt()
|
||||
if (length > remaining.size) {
|
||||
throw MessageIOException("Payload too short, looking for length $length for $key in ${payload.toHex()}")
|
||||
}
|
||||
remaining.assertSizeAtLeast(length)
|
||||
ret[index] = remaining.copyOfRange(LENGTH_BYTES, LENGTH_BYTES + length)
|
||||
remaining = remaining.copyOfRange(LENGTH_BYTES + length, remaining.size)
|
||||
}
|
||||
|
@ -64,3 +60,9 @@ class StringLengthPrefixEncoding private constructor() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ByteArray.assertSizeAtLeast(size: Int) {
|
||||
if (this.size < size) {
|
||||
throw MessageIOException("Payload too short: ${this.toHex()}")
|
||||
}
|
||||
}
|
|
@ -46,27 +46,24 @@ data class FirstBlePacket(
|
|||
companion object {
|
||||
|
||||
fun parse(payload: ByteArray): FirstBlePacket {
|
||||
if (payload.size < FirstBlePacket.HEADER_SIZE_WITH_MIDDLE_PACKETS) {
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
}
|
||||
payload.assertSizeAtLeast(HEADER_SIZE_WITH_MIDDLE_PACKETS, 0)
|
||||
|
||||
if (payload[0].toInt() != 0) {
|
||||
// most likely we lost the first packet.
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
}
|
||||
val fullFragments = payload[1].toInt()
|
||||
require(fullFragments < MAX_FRAGMENTS) { "Received more than $MAX_FRAGMENTS fragments" }
|
||||
when {
|
||||
// Without middle packets
|
||||
payload.size < HEADER_SIZE_WITHOUT_MIDDLE_PACKETS ->
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
|
||||
payload.assertSizeAtLeast(HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, 0)
|
||||
|
||||
return when {
|
||||
|
||||
fullFragments == 0 -> {
|
||||
val rest = payload[6]
|
||||
val end = Integer.min(rest + HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, payload.size)
|
||||
if (end > payload.size) {
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
}
|
||||
return FirstBlePacket(
|
||||
payload.assertSizeAtLeast(end, 0)
|
||||
FirstBlePacket(
|
||||
fullFragments = fullFragments,
|
||||
payload = payload.copyOfRange(HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, end),
|
||||
crc32 = ByteBuffer.wrap(payload.copyOfRange(2, 6)).int.toUnsignedLong(),
|
||||
|
@ -76,11 +73,11 @@ data class FirstBlePacket(
|
|||
}
|
||||
|
||||
// With middle packets
|
||||
payload.size < BlePacket.MAX_SIZE ->
|
||||
payload.size < MAX_SIZE ->
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
|
||||
else -> {
|
||||
return FirstBlePacket(
|
||||
FirstBlePacket(
|
||||
fullFragments = fullFragments,
|
||||
payload = payload.copyOfRange(HEADER_SIZE_WITH_MIDDLE_PACKETS, MAX_SIZE)
|
||||
)
|
||||
|
@ -110,9 +107,7 @@ data class MiddleBlePacket(val index: Byte, override val payload: ByteArray) : B
|
|||
companion object {
|
||||
|
||||
fun parse(payload: ByteArray): MiddleBlePacket {
|
||||
if (payload.size < MAX_SIZE) {
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
}
|
||||
payload.assertSizeAtLeast(MAX_SIZE)
|
||||
return MiddleBlePacket(
|
||||
index = payload[0],
|
||||
payload.copyOfRange(1, MAX_SIZE)
|
||||
|
@ -148,14 +143,13 @@ data class LastBlePacket(
|
|||
companion object {
|
||||
|
||||
fun parse(payload: ByteArray): LastBlePacket {
|
||||
if (payload.size < HEADER_SIZE) {
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
}
|
||||
payload.assertSizeAtLeast(HEADER_SIZE)
|
||||
|
||||
val rest = payload[1]
|
||||
val end = Integer.min(rest + HEADER_SIZE, payload.size)
|
||||
if (payload.size < end) {
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
}
|
||||
|
||||
payload.assertSizeAtLeast(end)
|
||||
|
||||
return LastBlePacket(
|
||||
index = payload[0],
|
||||
crc32 = ByteBuffer.wrap(payload.copyOfRange(2, 6)).int.toUnsignedLong(),
|
||||
|
@ -183,10 +177,10 @@ data class LastOptionalPlusOneBlePacket(
|
|||
companion object {
|
||||
|
||||
fun parse(payload: ByteArray): LastOptionalPlusOneBlePacket {
|
||||
payload.assertSizeAtLeast(2)
|
||||
val size = payload[1].toInt()
|
||||
if (payload.size < HEADER_SIZE + size) {
|
||||
throw IncorrectPacketException(payload, 0)
|
||||
}
|
||||
payload.assertSizeAtLeast(HEADER_SIZE + size)
|
||||
|
||||
return LastOptionalPlusOneBlePacket(
|
||||
index = payload[0],
|
||||
payload = payload.copyOfRange(
|
||||
|
@ -200,3 +194,10 @@ data class LastOptionalPlusOneBlePacket(
|
|||
private const val HEADER_SIZE = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun ByteArray.assertSizeAtLeast(size: Int, index: Byte?=null) {
|
||||
if (this.size < size) {
|
||||
throw IncorrectPacketException(this, index)
|
||||
}
|
||||
}
|
|
@ -27,19 +27,15 @@ internal class LTKExchanger(
|
|||
private val keyExchange = KeyExchange(aapsLogger, X25519KeyGenerator(), RandomByteGenerator())
|
||||
private var seq: Byte = 1
|
||||
|
||||
@Throws(PairingException::class)
|
||||
fun negotiateLTK(): PairResult {
|
||||
val sp1sp2 = sp1sp2(podId.address, sp2())
|
||||
val sendSp1Sp2Result = msgIO.sendMessage(sp1sp2.messagePacket)
|
||||
if (sendSp1Sp2Result !is MessageSendSuccess) {
|
||||
throw PairingException("Could not send SP1SP2: $sendSp1Sp2Result")
|
||||
}
|
||||
throwOnSendError(sp1sp2.messagePacket, "SP1SP2")
|
||||
|
||||
seq++
|
||||
val sps1 = sps1()
|
||||
val sp1Result = msgIO.sendMessage(sps1.messagePacket)
|
||||
if (sp1Result !is MessageSendSuccess) {
|
||||
throw PairingException("Could not send SP1: $sp1Result")
|
||||
}
|
||||
throwOnSendError(sps1.messagePacket, "SP1")
|
||||
|
||||
|
||||
val podSps1 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS1")
|
||||
processSps1FromPod(podSps1)
|
||||
|
@ -47,20 +43,16 @@ internal class LTKExchanger(
|
|||
|
||||
seq++
|
||||
val sps2 = sps2()
|
||||
val sp2Result = msgIO.sendMessage(sps2.messagePacket)
|
||||
if (sp2Result !is MessageSendSuccess) {
|
||||
throw PairingException("Could not send sps2: $sp2Result")
|
||||
}
|
||||
throwOnSendError(sps2.messagePacket, "SPS2")
|
||||
|
||||
|
||||
val podSps2 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS2")
|
||||
validatePodSps2(podSps2)
|
||||
|
||||
seq++
|
||||
// send SP0GP0
|
||||
val sp0gp0Result = msgIO.sendMessage(sp0gp0().messagePacket)
|
||||
if (sp0gp0Result is MessageSendErrorSending) {
|
||||
throw PairingException("Could not send SP0GP0: $sp0gp0Result")
|
||||
}
|
||||
throwOnSendErrorSending(sp0gp0().messagePacket, "SP0GP0")
|
||||
|
||||
|
||||
// No exception throwing after this point. It is possible that the pod saved the LTK
|
||||
msgIO.receiveMessage()
|
||||
|
@ -73,6 +65,22 @@ internal class LTKExchanger(
|
|||
)
|
||||
}
|
||||
|
||||
@Throws(PairingException::class)
|
||||
private fun throwOnSendError(msg: MessagePacket, msgType: String) {
|
||||
val result = msgIO.sendMessage(msg)
|
||||
if (result !is MessageSendSuccess) {
|
||||
throw PairingException("Could not send or confirm $msgType: $result")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(PairingException::class)
|
||||
private fun throwOnSendErrorSending(msg: MessagePacket, msgType: String) {
|
||||
val result = msgIO.sendMessage(msg)
|
||||
if (result is MessageSendErrorSending) {
|
||||
throw PairingException("Could not send $msgType: $result")
|
||||
}
|
||||
}
|
||||
|
||||
private fun sp1sp2(sp1: ByteArray, sp2: ByteArray): PairMessage {
|
||||
val payload = StringLengthPrefixEncoding.formatKeys(
|
||||
arrayOf(SP1, SP2),
|
||||
|
@ -160,7 +168,8 @@ internal class LTKExchanger(
|
|||
companion object {
|
||||
|
||||
private const 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.
|
||||
"ffc32dbd08030e0100008a"
|
||||
// This is the binary representation of "GetPodStatus command"
|
||||
|
||||
private const val SP1 = "SP1="
|
||||
private const val SP2 = ",SP2="
|
||||
|
|
Loading…
Reference in a new issue