public const int RESPONSE_CRC      = -0x0002; // UInt16BE

        // returns the dycrypted response payload only
        protected byte[] Decode(MedtronicCnlSession pumpSession, byte[] payload)
        {
            if (payload.Length < MM_PAYLOAD + NGP_PAYLOAD + NGP55_PAYLOAD + RESPONSE_PAYLOAD ||
                payload[MM_COMMAND] == (byte)CommandType.READ_INFO ||
                payload[MM_COMMAND] == (byte)CommandType.REQUEST_LINK_KEY_RESPONSE)
            {
                throw new EncryptionException("Message received for decryption wrong type/size");
            }

            byte encryptedPayloadSize = payload[MM_PAYLOAD + NGP_PAYLOAD + NGP55_ENCRYPTED_SIZE];

            if (encryptedPayloadSize == 0)
            {
                throw new EncryptionException("Could not decrypt Medtronic Message (encryptedPayloadSize == 0)");
            }

            var encryptedPayload = new MedtronicMessageBuffer(encryptedPayloadSize);

            encryptedPayload.Put(payload, MM_PAYLOAD + NGP_PAYLOAD + NGP55_PAYLOAD, encryptedPayloadSize);
            var decryptedPayload = Decrypt(pumpSession.getKey(), pumpSession.getIV(), encryptedPayload);

            if (decryptedPayload == null)
            {
                throw new EncryptionException("Could not decrypt Medtronic Message (decryptedPayload == null)");
            }

#if DEBUG
            string outputstring = HexDump.DumpHexstring(decryptedPayload);
            Log.d(TAG, "DECRYPTED: " + outputstring);
#endif

            return(decryptedPayload);
        }
        protected async Task <byte[]> SendToPump(UsbHidDriver mDevice, MedtronicCnlSession pumpSession, string tag)
        {
            tag = " (" + tag + ")";
            byte[] payload;
            byte   medtronicSequenceNumber = pumpSession.getMedtronicSequenceNumber();

            // extra safety check and delay, CNL is not happy when we miss incoming messages
            // the short delay may also help with state readiness
            ClearMessage(mDevice, PRESEND_CLEAR_TIMEOUT_MS);

            SendMessage(mDevice);

            try
            {
                payload = await ReadMessage_0x81(mDevice);
            }
            catch (TimeoutException e)
            {
                // ugh... there should always be a CNL 0x81 response and if we don't get one it usually ends with a E86 / E81 error on the CNL needing a unplug/plug cycle
                Log.e(TAG, "Timeout waiting for 0x81 response." + tag);
                ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                throw new TimeoutException("Timeout waiting for 0x81 response" + tag);
            }

            Log.d(TAG, "0x81 response: payload.Length=" + payload.Length + (payload.Length >= 0x30 ? " payload[0x21]=" + payload[0x21] + " payload[0x2C]=" + payload[0x2C] + " medtronicSequenceNumber=" + medtronicSequenceNumber + " payload[0x2D]=" + payload[0x2D] : "") + tag);

            // following errors usually have no further response from the CNL but occasionally they do
            // and these need to be read and cleared asap or yep E86 me baby and a unresponsive CNL
            // the extra delay from the clearMessage timeout may be helping here too by holding back any further downstream sends etc - investigate
            if (payload.Length <= 0x21)
            {
                ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                throw new UnexpectedMessageException("0x81 response was empty" + tag);  // *bad* CNL death soon after this, may want to end comms immediately
            }
            else if (payload.Length != 0x30 && payload[0x21] != 0x55)
            {
                ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                throw new UnexpectedMessageException("0x81 response was not a 0x55 message" + tag);
            }
            else if (payload[0x2C] != medtronicSequenceNumber)
            {
                ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                throw new UnexpectedMessageException("0x81 sequence number does not match" + tag);
            }
            else if (payload[0x2D] == 0x04)
            {
                ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                throw new UnexpectedMessageException("0x81 connection busy" + tag);
            }
            else if (payload[0x2D] != 0x02)
            {
                ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                throw new UnexpectedMessageException("0x81 connection lost" + tag);
            }

            return(payload);
        }
 public AckMessage(MedtronicCnlSession pumpSession, object p)
 {
     this.pumpSession = pumpSession;
     this.p           = p;
 }
        protected async Task <byte[]> ReadFromPump(UsbHidDriver mDevice, MedtronicCnlSession pumpSession, string tag)
        {
            tag = " (" + tag + ")";

            MultipacketSession multipacketSession = null;

            byte[] tupple;
            byte[] payload   = null;
            byte[] decrypted = null;

            bool fetchMoreData    = true;
            int  retry            = 0;
            int  expectedSegments = 0;

            int cmd;

            while (fetchMoreData)
            {
                if (multipacketSession != null)
                {
                    do
                    {
                        if (expectedSegments < 1)
                        {
                            tupple = multipacketSession.MissingSegments();
                            new MultipacketResendPacketsMessage(pumpSession, tupple).send(mDevice);
                            expectedSegments = read16BEtoUInt(tupple, 0x02);
                        }
                        try
                        {
                            payload = await ReadMessage(mDevice, MULTIPACKET_TIMEOUT_MS);

                            break;
                        }
                        catch (TimeoutException e)
                        {
                            if (++retry >= SEGMENT_RETRY)
                            {
                                throw new TimeoutException("Timeout waiting for response from pump (multipacket)" + tag);
                            }
                            Log.d(TAG, "*** Multisession timeout, expecting:" + expectedSegments + " retry: " + retry);
                            expectedSegments = 0;
                        }
                    } while (retry > 0);
                }
                else
                {
                    try
                    {
                        payload = await ReadMessage(mDevice, READ_TIMEOUT_MS);
                    }
                    catch (TimeoutException e)
                    {
                        throw new TimeoutException("Timeout waiting for response from pump" + tag);
                    }
                }

                if (payload.Length < 0x0039)
                {
                    Log.d(TAG, "*** bad response" + HexDump.DumpHexstring(payload, 0x12, payload.Length - 0x14));
                    fetchMoreData = true;
                }
                else
                {
                    decrypted = Decode(pumpSession, payload);

                    cmd = read16BEtoUInt(decrypted, RESPONSE_COMMAND);
                    Log.d(TAG, "CMD: " + HexDump.ToHexstring(cmd));

                    if (MedtronicSendMessageRequestMessage.MessageType.EHSM_SESSION.response(cmd))
                    {
                        // EHSM_SESSION(0)
                        Log.d(TAG, "*** EHSM response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = true;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.NAK_COMMAND.response(cmd))
                    {
                        Log.d(TAG, "*** NAK response" + HexDump.DumpHexstring(decrypted));
                        ClearMessage(mDevice, ERROR_CLEAR_TIMEOUT_MS);
                        // if multipacket was in progress we may need to clear 2 EHSM_SESSION(1) messages from pump
                        short nakcmd  = read16BEtoShort(decrypted, 3);
                        byte  nakcode = decrypted[5];
                        throw new UnexpectedMessageException("Pump sent a NAK(" + string.Format("%02X", nakcmd) + ":" + string.Format("%02X", nakcode) + ") response" + tag);
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.INITIATE_MULTIPACKET_TRANSFER.response(cmd))
                    {
                        multipacketSession = new MultipacketSession(decrypted);
                        new AckMessage(pumpSession, MedtronicSendMessageRequestMessage.MessageType.INITIATE_MULTIPACKET_TRANSFER.response()).send(mDevice);
                        expectedSegments = multipacketSession.PacketsToFetch;
                        fetchMoreData    = true;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.MULTIPACKET_SEGMENT_TRANSMISSION.response(cmd))
                    {
                        if (multipacketSession == null)
                        {
                            throw new UnexpectedMessageException("multipacketSession not initiated before segment received" + tag);
                        }
                        multipacketSession.AddSegment(decrypted);
                        expectedSegments--;

                        if (multipacketSession.PayloadComplete)
                        {
                            Log.d(TAG, "*** Multisession Complete");
                            new AckMessage(pumpSession, MedtronicSendMessageRequestMessage.MessageType.MULTIPACKET_SEGMENT_TRANSMISSION.response()).send(mDevice);

                            // read 0412 = EHSM_SESSION(1)
                            payload = await ReadMessage(mDevice, READ_TIMEOUT_MS);

                            decrypted = Decode(pumpSession, payload);
                            Log.d(TAG, "*** response" + HexDump.DumpHexstring(decrypted));

                            return(multipacketSession.Response);
                        }
                        else
                        {
                            fetchMoreData = true;
                        }
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.END_HISTORY_TRANSMISSION.response(cmd))
                    {
                        Log.d(TAG, "*** END_HISTORY_TRANSMISSION response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_PUMP_TIME.response(cmd))
                    {
                        Log.d(TAG, "*** READ_PUMP_TIME response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_PUMP_STATUS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_PUMP_STATUS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_HISTORY_INFO.response(cmd))
                    {
                        Log.d(TAG, "*** READ_HISTORY_INFO response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BASAL_PATTERN.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BASAL_PATTERN response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BOLUS_WIZARD_CARB_RATIOS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BOLUS_WIZARD_CARB_RATIOS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BOLUS_WIZARD_SENSITIVITY_FACTORS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BOLUS_WIZARD_SENSITIVITY_FACTORS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else if (MedtronicSendMessageRequestMessage.MessageType.READ_BOLUS_WIZARD_BG_TARGETS.response(cmd))
                    {
                        Log.d(TAG, "*** READ_BOLUS_WIZARD_BG_TARGETS response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = false;
                    }
                    else
                    {
                        Log.d(TAG, "*** ??? response" + HexDump.DumpHexstring(decrypted));
                        fetchMoreData = true;
                    }
                }
            }

            // when returning non-multipacket decrypted data, we need to trim the 2 byte checksum
            if (decrypted == null)
            {
                return(payload);
            }
            return(decrypted.Partial(0, decrypted.Length - 2));
        }
Example #5
0
 public MultipacketResendPacketsMessage(MedtronicCnlSession pumpSession, byte[] tupple)
 {
     this.pumpSession = pumpSession;
     this.tupple      = tupple;
 }