Ejemplo n.º 1
0
        public static MetaMessage ComposeNormalTextMessage(string recipientUuid, string text)
        {
            if (!GlobalKeyStore.Instance.ChatStates.TryGetValue(recipientUuid, out ChatState state))
            {
                throw new Exception("You need to send or receive an initial message before sending a normal message!");
            }

            (byte[] encryptedText, MessageHeader header) = RatchetEncrypt(state, Encoding.UTF8.GetBytes(text));

            NormalTextMessage payload = new NormalTextMessage
            {
                AssociatedDataBase64 = null,
                DhKeyBase64          = Convert.ToBase64String(header.DhRatchetKey.XPublicKey),
                EncryptedTextBase64  = Convert.ToBase64String(encryptedText),
                MessageNumber        = header.MessageNumber,
                PreviousCount        = header.PreviousCount
            };

            string payloadJson = JsonConvert.SerializeObject(payload);

            return(new MetaMessage
            {
                OtherUuid = recipientUuid,
                Type = MessageType.NormalTextMessage,
                Payload = payloadJson
            });
        }
Ejemplo n.º 2
0
        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");
            });
        }
Ejemplo n.º 3
0
        private static void HandleInitialMessage(MetaMessage metaMessage)
        {
            // TODO actually do database stuff

            string senderUuid = metaMessage.OtherUuid;

            InitialMessage message = JsonConvert.DeserializeObject <InitialMessage>(metaMessage.Payload);

            Curve25519KeyPair otherIdentityKey = new Curve25519KeyPair(Convert.FromBase64String(message.SenderIdentityKeyBase64), false, true);
            Curve25519KeyPair ephemeralKey     = new Curve25519KeyPair(Convert.FromBase64String(message.EphemeralKeyBase64), false, false);

            Curve25519KeyPair receivedSignedPreKey = new Curve25519KeyPair(Convert.FromBase64String(message.RecipientSignedPreKeyBase64), false, false);
            Curve25519KeyPair signedPreKeyPair     = GlobalKeyStore.Instance.SignedPreKeyPairs.Find(pair => pair.XPublicKey.SequenceEqual(receivedSignedPreKey.XPublicKey));

            if (signedPreKeyPair is null)
            {
                throw new Exception("Matching signed prekey pair not found");
            }

            Curve25519KeyPair oneTimePreKeyPair;

            if (message.RecipientOneTimePreKeyBase64 is null)
            {
                oneTimePreKeyPair = null;
            }
            else
            {
                Curve25519KeyPair receivedOneTimePreKey = new Curve25519KeyPair(Convert.FromBase64String(message.RecipientOneTimePreKeyBase64), false, false);
                oneTimePreKeyPair = GlobalKeyStore.Instance.OneTimePreKeyPairs.Find(pair => pair.XPublicKey.SequenceEqual(receivedOneTimePreKey.XPublicKey));

                if (oneTimePreKeyPair is null)
                {
                    throw new Exception("Matching one-time prekey pair not found");
                }

                // Remove the one-time key, never to be used again
                GlobalKeyStore.Instance.SignedPreKeyPairs.Remove(oneTimePreKeyPair);
            }

            byte[] secretKey = CryptoUtils.DeriveX3DhSecretReceiver(GlobalKeyStore.Instance.IdentityKeyPair,
                                                                    signedPreKeyPair, otherIdentityKey, ephemeralKey, oneTimePreKeyPair);

            GlobalKeyStore.Instance.ChatStates[senderUuid] = new ChatState
            {
                DhSendingKeyPair  = signedPreKeyPair,
                DhReceivingKey    = null,
                RootKey           = secretKey,
                SendingChainKey   = null,
                ReceivingChainKey = null,
                CountSent         = 0,
                CountReceived     = 0,
                PreviousCount     = 0,
                MissedMessages    = new Dictionary <string, (byte[], byte[])>()
            };

            MetaMessage innerMetaMessage = new MetaMessage
            {
                OtherUuid = metaMessage.OtherUuid,
                Type      = MessageType.NormalTextMessage,
                Payload   = message.InnerMessagePayload,
                Timestamp = metaMessage.Timestamp
            };

            NormalTextMessage innerMessage = JsonConvert.DeserializeObject <NormalTextMessage>(innerMetaMessage.Payload);
            string            requiredAssociatedDataBase64 = Convert.ToBase64String(
                CryptoUtils.CalculateInitialAssociatedData(otherIdentityKey, GlobalKeyStore.Instance.IdentityKeyPair));

            if (innerMessage.AssociatedDataBase64 != requiredAssociatedDataBase64)
            {
                throw new Exception($"Associated data for the initial message isn't what it should be: {innerMessage.AssociatedDataBase64} != {requiredAssociatedDataBase64}");
            }

            HandleNormalTextMessage(innerMetaMessage);
        }
Ejemplo n.º 4
0
        public static MetaMessage ComposeInitialMessage(string recipientUuid, ChatPackage chatPackage, string text)
        {
            Curve25519KeyPair ephemeralKeyPair = new Curve25519KeyPair();

            byte[] secretKey = CryptoUtils.DeriveX3DhSecretSender(GlobalKeyStore.Instance.IdentityKeyPair,
                                                                  ephemeralKeyPair, chatPackage.OtherIdentityKey, chatPackage.OtherSignedPreKey,
                                                                  chatPackage.OtherOneTimePreKey);


            Curve25519KeyPair dhSendingKeyPair = new Curve25519KeyPair();

            (byte[] rootKey, byte[] sendingChainKey) = CryptoUtils.RatchetRootKey(secretKey,
                                                                                  dhSendingKeyPair.CalculateSharedSecret(chatPackage.OtherSignedPreKey));

            ChatState state = new ChatState
            {
                DhSendingKeyPair  = dhSendingKeyPair,
                DhReceivingKey    = chatPackage.OtherSignedPreKey,
                RootKey           = rootKey,
                SendingChainKey   = sendingChainKey,
                ReceivingChainKey = null,
                CountSent         = 0,
                CountReceived     = 0,
                PreviousCount     = 0,
                MissedMessages    = new Dictionary <string, (byte[], byte[])>()
            };

            GlobalKeyStore.Instance.ChatStates[recipientUuid] = state;

            byte[] initialAssociatedData = CryptoUtils.CalculateInitialAssociatedData(
                GlobalKeyStore.Instance.IdentityKeyPair,
                chatPackage.OtherIdentityKey);
            (byte[] encryptedText, MessageHeader header) = RatchetEncrypt(state, Encoding.UTF8.GetBytes(text), initialAssociatedData);

            NormalTextMessage innerPayload = new NormalTextMessage
            {
                AssociatedDataBase64 = Convert.ToBase64String(initialAssociatedData),
                DhKeyBase64          = Convert.ToBase64String(dhSendingKeyPair.XPublicKey),
                EncryptedTextBase64  = Convert.ToBase64String(encryptedText),
                MessageNumber        = header.MessageNumber,
                PreviousCount        = header.PreviousCount
            };

            string innerPayloadJson = JsonConvert.SerializeObject(innerPayload);

            InitialMessage payload = new InitialMessage
            {
                EphemeralKeyBase64           = Convert.ToBase64String(ephemeralKeyPair.XPublicKey),
                InnerMessagePayload          = innerPayloadJson,
                SenderIdentityKeyBase64      = Convert.ToBase64String(GlobalKeyStore.Instance.IdentityKeyPair.EdPublicKey),
                RecipientSignedPreKeyBase64  = Convert.ToBase64String(chatPackage.OtherSignedPreKey.XPublicKey),
                RecipientOneTimePreKeyBase64 = Convert.ToBase64String(chatPackage.OtherOneTimePreKey.XPublicKey)
            };

            string payloadJson = JsonConvert.SerializeObject(payload);

            return(new MetaMessage
            {
                OtherUuid = recipientUuid,
                Type = MessageType.InitialMessage,
                Payload = payloadJson
            });
        }