Exemple #1
0
        /// <summary>
        /// Decrypts the key and HMAC concatenation (<see cref="OmemoMessage.cipherText"/>) with the given <paramref name="session"/>.
        /// </summary>
        /// <param name="authMsg">The <see cref="OmemoAuthenticatedMessage"/> containing the key and HMAC.</param>
        /// <param name="session">The <see cref="OmemoSessionModel"/> that should be used for decryption.</param>
        /// <returns>key || HMAC</returns>
        private byte[] DecryptKeyHmacForDevice(OmemoAuthenticatedMessage authMsg, OmemoSessionModel session)
        {
            OmemoMessage msg = new OmemoMessage(authMsg.OMEMO_MESSAGE);

            byte[] plainText = TrySkippedMessageKeys(msg, authMsg.HMAC, session);
            if (!(plainText is null))
            {
                return(plainText);
            }

            if (session.dhR is null || !msg.DH.Equals(session.dhR.pubKey))
            {
                SkipMessageKeys(session, msg.PN);
                session.InitDhRatchet(msg);
            }
            SkipMessageKeys(session, msg.N);

            // If no receive chain has been initialized yet, initialize it now.
            // This happens in case we received a key exchange message with a new session and now would like to send our first message.
            if (session.ckR is null)
            {
                session.InitReceiverKeyChain();
            }

            byte[] mk = LibSignalUtils.KDF_CK(session.ckR, 0x01);
            session.ckR = LibSignalUtils.KDF_CK(session.ckR, 0x02);
            ++session.nR;
            return(DecryptKeyHmacForDevice(mk, msg, authMsg.HMAC, session.assData));
        }
Exemple #2
0
        public void Test_OmemoKeyExchangeMessage()
        {
            Random rand = new Random();

            byte[] hmac = new byte[16];
            rand.NextBytes(hmac);
            byte[] sessionKey = new byte[32];
            rand.NextBytes(sessionKey);

            IdentityKeyPairModel identityKey     = KeyHelper.GenerateIdentityKeyPair();
            SignedPreKeyModel    signedPreKey    = KeyHelper.GenerateSignedPreKey(0, identityKey.privKey);
            OmemoSessionModel    refOmemoSession = new OmemoSessionModel(new Bundle()
            {
                identityKey     = identityKey.pubKey,
                preKeys         = KeyHelper.GeneratePreKeys(0, 10),
                preKeySignature = signedPreKey.signature,
                signedPreKey    = signedPreKey.preKey.pubKey,
                signedPreKeyId  = signedPreKey.preKey.keyId
            }, 0, KeyHelper.GenerateIdentityKeyPair())
            {
                ek = KeyHelper.GenerateKeyPair().pubKey
            };
            OmemoMessage refOmemoMessage = new OmemoMessage(refOmemoSession);
            OmemoAuthenticatedMessage refOmemoAuthenticatedMessage = new OmemoAuthenticatedMessage(new byte[16], refOmemoMessage.ToByteArray());
            OmemoKeyExchangeMessage   refOmemoKeyExchangeMessage   = new OmemoKeyExchangeMessage((uint)rand.Next(), (uint)rand.Next(), KeyHelper.GenerateKeyPair().pubKey, KeyHelper.GenerateKeyPair().pubKey, refOmemoAuthenticatedMessage);

            byte[] data = refOmemoKeyExchangeMessage.ToByteArray();
            Assert.IsTrue(data.Length > 0);

            OmemoKeyExchangeMessage omemoKeyExchangeMessage = new OmemoKeyExchangeMessage(data);

            Assert.IsTrue(omemoKeyExchangeMessage.Equals(refOmemoKeyExchangeMessage));
        }
Exemple #3
0
        /// <summary>
        /// Decrypts the key and HMAC concatenation (<see cref="OmemoMessage.cipherText"/>) with the given <paramref name="mk"/> and returns the result.
        /// </summary>
        /// <param name="mk">The message key that should be used for decryption.</param>
        /// <param name="msg">The <see cref="OmemoMessage"/> containing the key and HMAC concatenation (<see cref="OmemoMessage.cipherText"/>).</param>
        /// <param name="msgHmac">The HMAC of the <paramref name="msg"/>.</param>
        /// <param name="assData">Encode(IK_A) || Encode(IK_B) => Concatenation of Alices and Bobs public part of their identity key.</param>
        /// <returns>key || HMAC</returns>
        private byte[] DecryptKeyHmacForDevice(byte[] mk, OmemoMessage msg, byte[] msgHmac, byte[] assData)
        {
            // 32 byte (256 bit) of salt. Initialized with 0s.
            byte[] hkdfOutput = CryptoUtils.HkdfSha256(mk, new byte[32], "OMEMO Message Key Material", 80);
            CryptoUtils.SplitKey(hkdfOutput, out byte[] encKey, out byte[] authKey, out byte[] iv);
            byte[] hmacInput     = CryptoUtils.Concat(assData, msg.ToByteArray());
            byte[] hmacResult    = CryptoUtils.HmacSha256(authKey, hmacInput);
            byte[] hmacTruncated = CryptoUtils.Truncate(hmacResult, 16);

            // Debug trace output:
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(mk) + ": " + CryptoUtils.ToHexString(mk));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(msgHmac) + ": " + CryptoUtils.ToHexString(msgHmac));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(assData) + ": " + CryptoUtils.ToHexString(assData));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(hkdfOutput) + ": " + CryptoUtils.ToHexString(hkdfOutput));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(encKey) + ": " + CryptoUtils.ToHexString(encKey));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(authKey) + ": " + CryptoUtils.ToHexString(authKey));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(iv) + ": " + CryptoUtils.ToHexString(iv));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(hmacInput) + ": " + CryptoUtils.ToHexString(hmacInput));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(hmacResult) + ": " + CryptoUtils.ToHexString(hmacResult));
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(hmacTruncated) + ": " + CryptoUtils.ToHexString(hmacTruncated));

            if (!hmacTruncated.SequenceEqual(msgHmac))
            {
                throw new OmemoException("Failed to decrypt. HMAC of OmemoMessage does not match.");
            }
            return(CryptoUtils.Aes256CbcDecrypt(encKey, iv, msg.cipherText));
        }
Exemple #4
0
        /// <summary>
        /// Decrypts the key and HMAC concatenation (<see cref="OmemoMessage.cipherText"/>) with the given <paramref name="session"/>.
        /// </summary>
        /// <param name="authMsg">The <see cref="OmemoAuthenticatedMessage"/> containing the key and HMAC.</param>
        /// <param name="session">The <see cref="OmemoSessionModel"/> that should be used for decryption.</param>
        /// <returns>key || HMAC</returns>
        public byte[] DecryptKeyHmacForDevice(OmemoAuthenticatedMessage authMsg, OmemoSessionModel session)
        {
            OmemoMessage msg = new OmemoMessage(authMsg.OMEMO_MESSAGE);

            byte[] plainText = TrySkippedMessageKeys(msg, authMsg.HMAC, session);
            if (!(plainText is null))
            {
                return(plainText);
            }

            if (session.dhR is null || !msg.DH.Equals(session.dhR))
            {
                SkipMessageKeys(session, msg.PN);
                session.InitDhRatchet(msg);
            }
            SkipMessageKeys(session, msg.N);

            byte[] mk = LibSignalUtils.KDF_CK(session.ckR, 0x01);
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(session.ckR) + ": " + CryptoUtils.ToHexString(session.ckR));
            session.ckR = LibSignalUtils.KDF_CK(session.ckR, 0x02);
            ++session.nR;
            byte[] result = DecryptKeyHmacForDevice(mk, msg, authMsg.HMAC, session.assData);

            // Update the session state:
            if (session.state == SessionState.SEND)
            {
                session.state = SessionState.READY;
            }
            Logger.Trace("[" + nameof(DecryptKeyHmacForDevice) + "] " + nameof(session.state) + ": " + session.state);

            return(result);
        }
Exemple #5
0
        /// <summary>
        /// Encrypts the given key and HMAC concatenation and returns the result.
        /// </summary>
        /// <param name="keyHmac">The key, HMAC concatenation result.</param>
        /// <param name="session">The <see cref="OmemoSessionModel"/> between the sender and receiver.</param>
        /// <param name="assData">Encode(IK_A) || Encode(IK_B) => Concatenation of Alices and Bobs public part of their identity key.</param>
        private OmemoAuthenticatedMessage EncryptKeyHmacForDevices(byte[] keyHmac, OmemoSessionModel session, byte[] assData)
        {
            byte[] mk = LibSignalUtils.KDF_CK(session.ckS, 0x01);
            session.ckS = LibSignalUtils.KDF_CK(session.ckS, 0x02);
            OmemoMessage omemoMessage = new OmemoMessage(session);

            ++session.nS;
            // 32 byte (256 bit) of salt. Initialized with 0s.
            byte[] hkdfOutput = CryptoUtils.HkdfSha256(mk, new byte[32], "OMEMO Message Key Material", 80);
            CryptoUtils.SplitKey(hkdfOutput, out byte[] encKey, out byte[] authKey, out byte[] iv);
            omemoMessage.cipherText = CryptoUtils.Aes256CbcEncrypt(encKey, iv, keyHmac);
            byte[] omemoMessageBytes = omemoMessage.ToByteArray();
            byte[] hmacInput         = CryptoUtils.Concat(assData, omemoMessageBytes);
            byte[] hmacResult        = CryptoUtils.HmacSha256(authKey, hmacInput);
            byte[] hmacTruncated     = CryptoUtils.Truncate(hmacResult, 16);
            return(new OmemoAuthenticatedMessage(hmacTruncated, omemoMessageBytes));
        }
Exemple #6
0
        /// <summary>
        /// Encrypts the given key and HMAC concatenation and returns the result.
        /// </summary>
        /// <param name="keyHmac">The key, HMAC concatenation result.</param>
        /// <param name="session">The <see cref="OmemoSessionModel"/> between the sender and receiver.</param>
        /// <param name="assData">Encode(IK_A) || Encode(IK_B) => Concatenation of Alices and Bobs public part of their identity key.</param>
        private OmemoAuthenticatedMessage EncryptKeyHmacForDevices(byte[] keyHmac, OmemoSessionModel session, byte[] assData)
        {
            byte[] mk = LibSignalUtils.KDF_CK(session.ckS, 0x01);
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(session.ckS) + ": " + CryptoUtils.ToHexString(session.ckS));
            session.ckS = LibSignalUtils.KDF_CK(session.ckS, 0x02);
            OmemoMessage omemoMessage = new OmemoMessage(session);

            ++session.nS;

            // 32 byte (256 bit) of salt. Initialized with 0s.
            byte[] hkdfOutput = CryptoUtils.HkdfSha256(mk, new byte[32], "OMEMO Message Key Material", 80);
            CryptoUtils.SplitKey(hkdfOutput, out byte[] encKey, out byte[] authKey, out byte[] iv);
            omemoMessage.cipherText = CryptoUtils.Aes256CbcEncrypt(encKey, iv, keyHmac);
            byte[] omemoMessageBytes = omemoMessage.ToByteArray();
            byte[] hmacInput         = CryptoUtils.Concat(assData, omemoMessageBytes);
            byte[] hmacResult        = CryptoUtils.HmacSha256(authKey, hmacInput);
            byte[] hmacTruncated     = CryptoUtils.Truncate(hmacResult, 16);

            // Update the session state:
            if (session.state == SessionState.RECEIVED)
            {
                session.state = SessionState.READY;
            }

            // Debug trace output:
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(mk) + ": " + CryptoUtils.ToHexString(mk));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(assData) + ": " + CryptoUtils.ToHexString(assData));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(hkdfOutput) + ": " + CryptoUtils.ToHexString(hkdfOutput));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(encKey) + ": " + CryptoUtils.ToHexString(encKey));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(authKey) + ": " + CryptoUtils.ToHexString(authKey));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(iv) + ": " + CryptoUtils.ToHexString(iv));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(hmacInput) + ": " + CryptoUtils.ToHexString(hmacInput));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(hmacResult) + ": " + CryptoUtils.ToHexString(hmacResult));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(hmacTruncated) + ": " + CryptoUtils.ToHexString(hmacTruncated));
            Logger.Trace("[" + nameof(EncryptKeyHmacForDevices) + "] " + nameof(session.state) + ": " + session.state);

            return(new OmemoAuthenticatedMessage(hmacTruncated, omemoMessageBytes));
        }
Exemple #7
0
 /// <summary>
 /// Tries a skipped message key for decrypting the key and HMAC concatenation (<see cref="OmemoMessage.cipherText"/>).
 /// </summary>
 /// <param name="msg">The <see cref="OmemoMessage"/> containing the key and HMAC concatenation (<see cref="OmemoMessage.cipherText"/>).</param>
 /// <param name="msgHmac">The HMAC of the <paramref name="msg"/>.</param>
 /// <param name="session">The <see cref="OmemoSessionModel"/> that should be used for decryption.</param>
 /// <returns>key || HMAC on success, else null.</returns>
 private byte[] TrySkippedMessageKeys(OmemoMessage msg, byte[] msgHmac, OmemoSessionModel session)
 {
     byte[] mk = session.MK_SKIPPED.GetMessagekey(msg.DH, msg.N);
     return(!(mk is null) ? DecryptKeyHmacForDevice(mk, msg, msgHmac, session.assData) : null);
 }