private static void HandleNormalTextMessage(MetaMessage metaMessage) { // TODO actually do database stuff string senderUuid = metaMessage.OtherUuid; // TODO make much better! ChatState state = GlobalKeyStore.Instance.ChatStates[senderUuid]; NormalTextMessage message = JsonConvert.DeserializeObject <NormalTextMessage>(metaMessage.Payload); MessageHeader header = new MessageHeader { DhRatchetKey = new Curve25519KeyPair(Convert.FromBase64String(message.DhKeyBase64), false, false), PreviousCount = message.PreviousCount, MessageNumber = message.MessageNumber }; byte[] associatedData = null; if (!(message.AssociatedDataBase64 is null)) { associatedData = Convert.FromBase64String(message.AssociatedDataBase64); } byte[] decryptedData = RatchetDecrypt(state, header, Convert.FromBase64String(message.EncryptedTextBase64), associatedData); string decryptedText = Encoding.UTF8.GetString(decryptedData); Device.BeginInvokeOnMainThread(async() => { await Application.Current.MainPage.DisplayAlert("Message received!", $"From: {senderUuid}\nContent: {decryptedText}", "Ok"); }); }
private static (byte[], MessageHeader) RatchetEncrypt(ChatState state, byte[] plainText, byte[] associatedData = null) { byte[] messageKey, derivedAssociatedData; (state.SendingChainKey, messageKey, derivedAssociatedData) = CryptoUtils.RatchetChainKey(state.SendingChainKey); MessageHeader header = new MessageHeader { PreviousCount = state.PreviousCount, MessageNumber = state.CountSent, DhRatchetKey = state.DhSendingKeyPair }; state.CountSent++; return( CryptoUtils.EncryptWithMessageKey(messageKey, plainText, CombineAdAndHeader(associatedData ?? derivedAssociatedData, header)), header); }
private static byte[] RatchetDecrypt(ChatState state, MessageHeader header, byte[] cipherText, byte[] associatedData = null) { // TODO do ratcheting with copy before it's verified to have worked string missedMessagesKey = Convert.ToBase64String(header.DhRatchetKey.XPublicKey) + header.MessageNumber; if (state.MissedMessages.TryGetValue(missedMessagesKey, out (byte[], byte[])skippedMessageTuple)) { state.MissedMessages.Remove(missedMessagesKey); (byte[] skippedMessageKey, byte[] skippedAssociatedData) = skippedMessageTuple; return(CryptoUtils.DecryptWithMessageKey(skippedMessageKey, cipherText, CombineAdAndHeader(associatedData ?? skippedAssociatedData, header))); } if (state.DhReceivingKey is null || !header.DhRatchetKey.XPublicKey.SequenceEqual(state.DhReceivingKey.XPublicKey)) { SkipMessageKeys(state, header.PreviousCount); // Ratchet the chat state state.PreviousCount = state.CountSent; state.CountSent = 0; state.CountReceived = 0; state.DhReceivingKey = header.DhRatchetKey; (state.RootKey, state.ReceivingChainKey) = CryptoUtils.RatchetRootKey(state.RootKey, state.DhSendingKeyPair.CalculateSharedSecret(state.DhReceivingKey)); state.DhSendingKeyPair = new Curve25519KeyPair(); (state.RootKey, state.SendingChainKey) = CryptoUtils.RatchetRootKey(state.RootKey, state.DhSendingKeyPair.CalculateSharedSecret(state.DhReceivingKey)); } SkipMessageKeys(state, header.MessageNumber); byte[] messageKey, derivedAssociatedData; (state.ReceivingChainKey, messageKey, derivedAssociatedData) = CryptoUtils.RatchetChainKey(state.ReceivingChainKey); state.CountReceived++; return(CryptoUtils.DecryptWithMessageKey(messageKey, cipherText, CombineAdAndHeader(associatedData ?? derivedAssociatedData, header))); }
private static void SkipMessageKeys(ChatState state, uint upTo) { if (state.CountReceived + 50 < upTo) { throw new Exception("MAX_SKIP exceeded"); } if (state.ReceivingChainKey is null) { return; } while (state.CountReceived < upTo) { byte[] messageKey, associatedData; (state.ReceivingChainKey, messageKey, associatedData) = CryptoUtils.RatchetChainKey(state.ReceivingChainKey); string missedMessagesKey = Convert.ToBase64String(state.DhReceivingKey.XPublicKey) + state.CountReceived; state.MissedMessages[missedMessagesKey] = (messageKey, associatedData); state.CountReceived++; } }