public void ReceiveCallback(IAsyncResult result) { try { // Check how much bytes are received and call EndReceive to finalize handshake var received = Tcp.Client.EndReceive(result); // If we have received nothing then leave. if (received <= 0) { return; } // Copy the recieved data into new buffer , to avoid null bytes var recData = new byte[received]; Buffer.BlockCopy(ReceiveBuffer, 0, recData, 0, received); var encryptedString = Encoding.UTF8.GetString(recData); WholeMessage = string.IsNullOrWhiteSpace(WholeMessage) ? encryptedString : WholeMessage + encryptedString; // This is the reply to the start of the interaction. // If we can decrypt here, then we send back the _sym // key we will be using to encrypt our messages. if (WholeMessage.EndsWith("<BEG>")) { WholeMessage = WholeMessage.Replace("<BEG>", ""); var decryptedString = Crypto.DecryptWithAesKeyIV(WholeMessage, Crypto.CreateAesKeyIV(_sharedPasscode)); var publicKey = PublicKey.FromJson(new JSONObject(decryptedString)); // Send a new AES Symmetric key for using! _sharedSymmetricKey = Crypto.CreateAesKeyIV(); var encryptedWithTheirPublicKey = Crypto.EncryptWithPublicKey( publicKey.ToRsaParameters(), System.Text.Encoding.UTF8.GetBytes(_sharedSymmetricKey.ToJson().ToString())); Task.Run(async() => await StartConversation(Convert.ToBase64String(encryptedWithTheirPublicKey) + "<END>")); WholeMessage = null; } else if (WholeMessage.EndsWith("<TAC>")) { // The computer asked for the client list... // we shall send it to them encrypted! var contacts = Contact.GetAllContacts(this); var jsonArray = ContactInfo.ToJsonArray(contacts); var encrypted = Crypto.EncryptWithAesKeyIV(jsonArray.ToString(), _sharedSymmetricKey); var msg = Encoding.UTF8.GetBytes(encrypted + "<TAC>"); Task.Run(async() => await Tcp.Client.SendAsync(new ArraySegment <byte>(msg), SocketFlags.None)); WholeMessage = null; } else if (WholeMessage.EndsWith("<EOF>")) { // This is an encrypted message from the computer. WholeMessage = WholeMessage.Replace("<EOF>", ""); var decryptedString = Crypto.DecryptWithAesKeyIV(WholeMessage, _sharedSymmetricKey); var payload = TcpPayload.FromJson(new JSONObject(decryptedString)); if (!string.IsNullOrWhiteSpace(payload.Contact.PhoneNumber) && !string.IsNullOrWhiteSpace(payload.Message)) { // TODO Possible send delay so we don't overload the sms manager? Sms.SendTo(payload.Contact.PhoneNumber, payload.Message); } else { // Log an issue... } WholeMessage = null; } // Start receiving again Tcp.Client.BeginReceive(ReceiveBuffer, 0, ReceiveBuffer.Length, SocketFlags.None, ReceiveCallback, null); } catch (SocketException ex) { Android.Util.Log.Error("Receive Callback", ex.Message); OnDestroy(); } catch (Exception ex) { Android.Util.Log.Error("Receive Callback", ex.Message); // Clear the whole message WholeMessage = null; // Start receiving again if (Tcp != null && Tcp.Client != null) { Tcp.Client.BeginReceive(ReceiveBuffer, 0, ReceiveBuffer.Length, SocketFlags.None, ReceiveCallback, null); } } }
// TODO investigate and ensure that a single receive will // only ever come from 1 source and not multiple sources like // the pdusObj might suggest. public override void OnReceive(Context context, Intent intent) { // If we don't have a connection we aren't storing messages // to send at a later date, so just jump out. if (!TcpService.HasTcpConnection) { return; } // Retrieves a map of extended data from the intent. var bundle = intent.Extras; try { if (bundle != null) { var pdusObj = (Java.Lang.Object[])bundle.Get("pdus"); var tcpPayload = new TcpPayload { Contact = new ContactInfo { ID = "-1", PhoneNumber = null, Name = null }, Message = "" }; foreach (var t in pdusObj) { var currentMessage = SmsMessage.CreateFromPdu((byte[])t, bundle.GetString("format")); var phoneNumber = currentMessage.DisplayOriginatingAddress; var message = currentMessage.DisplayMessageBody; if (tcpPayload.Contact.PhoneNumber != null) { // We already have the phone number so just append the message. tcpPayload.Message += message; } else { // We possibly could find multiple contacts with the same // phone number. The app does nothing to stop this. var contacts = Contact.FindContact(phoneNumber, context); if (contacts.Count >= 1) { // If we only have 1 contact show that name. tcpPayload.Contact = contacts[0]; } else { // Otherwise we no contact so just put the phone number in. tcpPayload.Contact.PhoneNumber = phoneNumber; } tcpPayload.Message = message; } } if (tcpPayload.Contact.PhoneNumber != null && !string.IsNullOrWhiteSpace(tcpPayload.Message)) { var outgoingIntent = new Intent(context, typeof(TcpService)); outgoingIntent.SetAction(TcpService.ACTION_SEND_MESSAGE); outgoingIntent.PutExtra("payload", tcpPayload.ToJson().ToString()); context.StartService(outgoingIntent); } else { throw new Exception($"We parsed the message, but something when wrong. Phone Number: {tcpPayload.Contact.PhoneNumber}. Message: {tcpPayload.Message}."); } } } catch (Exception ex) { Android.Util.Log.Error("SMS_READ", ex.Message); } }