Beispiel #1
0
        InputDerivedKey32 CreatePasswordDerivedKeyWithBCrypt(IV16 iv, KeyMaterial64 keyMaterial64, RoundsExponent roundsExponent,
                                                             LongRunningOperationContext context)
        {
            var leftSHA512  = new byte[32];
            var rightSHA512 = new byte[32];

            Buffer.BlockCopy(keyMaterial64.GetBytes(), 0, leftSHA512, 0, 32);
            Buffer.BlockCopy(keyMaterial64.GetBytes(), 32, rightSHA512, 0, 32);

            context.EncryptionProgress.Message = LocalizableStrings.MsgProcessingKey;

            // Compute the left side on a ThreadPool thread
            var task = Task.Run(() => BCrypt.CreateHash(iv, leftSHA512, roundsExponent.Value, context));

            // Compute the right side after dispatching the work for the right side
            BCrypt24 rightBCrypt = BCrypt.CreateHash(iv, rightSHA512, roundsExponent.Value, context);

            // Wait for the left side result
            task.Wait(context.CancellationToken);

            // Use the results
            var combinedHashes = ByteArrays.Concatenate(keyMaterial64.GetBytes(), task.Result.GetBytes(), rightBCrypt.GetBytes());

            Debug.Assert(combinedHashes.Length == 64 + 24 + 24);

            var condensedHash = this._platform.ComputeSHA256(combinedHashes);

            return(new InputDerivedKey32(condensedHash));
        }
Beispiel #2
0
        public static void ValidateWitnessCommitment(this SlimBlock slimBlock)
        {
            var   ser   = slimBlock.Serialize();
            Block block = Block.Load(ser, C.Network.Consensus.ConsensusFactory);

            if (!ByteArrays.AreAllBytesEqual(ser, block.ToBytes(C.ProtocolVersion)))
            {
                throw new InvalidOperationException("Serialization issue.");
            }

            // Validation for witness commitments.
            // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the
            //   coinbase (where 0x0000....0000 is used instead).
            // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained).
            // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header).
            // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are
            //   {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are
            //   multiple, the last one is used.
            bool fHaveWitness = false;

            Script commitment = GetWitnessCommitment(block);

            if (commitment != null)
            {
                uint256 hashWitness = BlockWitnessMerkleRoot(block, out bool _);

                // The malleation check is ignored; as the transaction tree itself
                // already does not permit it, it is impossible to trigger in the
                // witness tree.
                WitScript witness = block.Transactions[0].Inputs[0].WitScript;
                if ((witness.PushCount != 1) || (witness.Pushes.First().Length != 32))
                {
                    throw new InvalidOperationException("ConsensusErrors.BadWitnessNonceSize");
                }

                var hashed = new byte[64];
                Buffer.BlockCopy(hashWitness.ToBytes(), 0, hashed, 0, 32);
                Buffer.BlockCopy(witness.Pushes.First(), 0, hashed, 32, 32);
                hashWitness = Hashes.DoubleSHA256(hashed); // todo: not nice code to reuse tha variable


                if (!ByteArrays.AreAllBytesEqual(hashWitness.ToBytes(), commitment.ToBytes(true).Skip(6).ToArray()))
                {
                    throw new InvalidOperationException("ConsensusErrors.BadWitnessMerkleMatch");
                }

                fHaveWitness = true;
            }

            if (!fHaveWitness)
            {
                for (int i = 0; i < block.Transactions.Count; i++)
                {
                    if (block.Transactions[i].HasWitness)
                    {
                        throw new InvalidOperationException("Unexpected witness.");
                    }
                }
            }
        }
Beispiel #3
0
        public static byte[] GetRandom(int length)
        {
            if (length != 32 && length != 64 && length != 20)
            {
                throw new ArgumentException(nameof(length));
            }

            using var sha = SHA512.Create();
            using var rng = new RNGCryptoServiceProvider();

            var rngBytes         = new byte[512];
            var tickBytes        = BitConverter.GetBytes(DateTime.Now.Ticks);
            var startedTickBytes = BitConverter.GetBytes(Started * Started);
            var guidBytes        = Guid.NewGuid().ToByteArray();

            rng.GetBytes(rngBytes);

            var sources = new[] { startedTickBytes, tickBytes, guidBytes, rngBytes };

            byte[] entropy = ByteArrays.Concatenate(rngBytes, startedTickBytes, tickBytes, guidBytes, rngBytes);

            foreach (var src in sources)
            {
                entropy = sha.ComputeHash(ByteArrays.Concatenate(entropy, src));
            }

            var ret = new byte[length];

            Buffer.BlockCopy(entropy, 0, ret, 0, length);
            return(ret);
        }
Beispiel #4
0
        async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetInitialEndToEndEncryptionKey(string recipientId)
        {
            E2EUser user = await GetCheckedUser(recipientId);

            // user.DynamicPrivateDecryptionKeys = new Dictionary<long, byte[]>(); // don't do this. Or only the last receipt of a resent message can be decrypted
            user.LatestDynamicPublicKey   = null;
            user.LatestDynamicPublicKeyId = 0;


            long nextDynamicPublicKeyId = this._ratchetTimer.GetNextTicks(0);
            var  ecdhKeypair            = this._visualCrypt2Service.GenerateECKeyPair().Result;

            byte[] dynamicPublicKey = ecdhKeypair.PublicKey;

            user.DynamicPrivateDecryptionKeys[nextDynamicPublicKeyId] = ecdhKeypair.PrivateKey;
            RemoveExcessKeys(user);

            await this._updateUser(user);

            long privateKeyHint = 0;

            var dynamicSharedSecret          = this._visualCrypt2Service.CalculateAndHashSharedSecret(ecdhKeypair.PrivateKey, user.StaticPublicKey);
            var symmetricKeyMaterial         = ByteArrays.Concatenate(dynamicSharedSecret, user.AuthSecret);
            var symmetricKeyMaterialMetaData = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]);             // note we are not using user.AuthSecret fro the metadata

            return(new Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64>(new KeyMaterial64(symmetricKeyMaterial), dynamicPublicKey, nextDynamicPublicKeyId, privateKeyHint, new KeyMaterial64(symmetricKeyMaterialMetaData)));
        }
Beispiel #5
0
        InputDerivedKey32 CreateDerivedKeyWithSHA256(IV16 iv, KeyMaterial64 keyMaterial64)
        {
            var keyMaterial = ByteArrays.Concatenate(iv.GetBytes(), keyMaterial64.GetBytes());
            var derivedKey  = this._platform.ComputeSHA256(keyMaterial);

            return(new InputDerivedKey32(derivedKey));
        }
Beispiel #6
0
        public TLSEnvelope TLSServerEncryptRequest(byte[] clearPacket, string recipientId)
        {
            byte[] authSecret = GetAuthSecret(recipientId);

            DynamicSecret dynamicSecret = GetDynamicSecretForEncryption(recipientId);

            Debug.WriteLine($"{this.ServerId}: TLSEncrypt: DynamicPublicKeyID: {dynamicSecret.DynamicPublicKeyId}, PrivateKeyHint: {dynamicSecret.PrivateKeyHint}.");

            // Concatenate = 'TLSAuthMode.Combined'
            byte[] symmetricKeyMaterial64 = ByteArrays.Concatenate(dynamicSecret.DynamicSharedSecret, authSecret);

            var lro        = new LongRunningOperation(progress => { }, () => { });
            var clearBytes = new Clearbytes(clearPacket);
            var sha512PW64 = new KeyMaterial64(symmetricKeyMaterial64);
            var method     = new RoundsExponent(0xff);

            var encryptResponse = this.ixdsCryptoService.BinaryEncrypt(clearBytes, sha512PW64, method, lro.Context);

            if (!encryptResponse.IsSuccess)
            {
                throw new Exception(encryptResponse.Error);
            }

            var encodeResponse = this.ixdsCryptoService.BinaryEncodeXDSSec(encryptResponse.Result, lro.Context);

            if (!encodeResponse.IsSuccess)
            {
                throw new Exception(encodeResponse.Error);
            }

            var tlsEnvelope = new TLSEnvelope(dynamicSecret.PrivateKeyHint, dynamicSecret.DynamicPublicKeyId, dynamicSecret.DynamicPublicKey, encodeResponse.Result);

            return(tlsEnvelope);
        }
Beispiel #7
0
        public Response <QualifiedRandom> GetRandom(int randomLenght, byte[] seed = null)
        {
            var response = new Response <QualifiedRandom>();

            try
            {
                EnsurePlatform();

                var randomBytes = this._platform.GenerateRandomBytes(randomLenght);
                randomBytes = this._platform.ComputeSHA512(randomBytes);

                if (seed != null)
                {
                    seed = this._platform.ComputeSHA512(seed);
                    var combinedBytes = ByteArrays.Concatenate(randomBytes, seed);
                    randomBytes = this._platform.ComputeSHA512(combinedBytes);
                }
                response.Result = new QualifiedRandom {
                    X = randomBytes.Take(randomLenght).ToArray()
                };
                response.SetSuccess();
            }
            catch (Exception e)
            {
                response.SetError(e);
            }
            return(response);
        }
        static (byte[] seed, string sentence) CreateHdSecrets(byte[] sourceKeyMaterial, int startIndex)
        {
            WordList wordList = WordList.English;

            var usedKeyMaterial = new byte[32];

            Buffer.BlockCopy(sourceKeyMaterial, startIndex, usedKeyMaterial, 0, 32);

            var mnemonic = new Mnemonic(wordList, usedKeyMaterial);

            if (!mnemonic.IsValidChecksum)
            {
                throw new InvalidOperationException("Invalid checksum.");
            }

            var seed = mnemonic.DeriveSeed();

            seed.Check(64);

            var sentence = mnemonic.ToString();

            CheckNumberOfWords(sentence, WordListLength);

            var testRecovery = new Mnemonic(sentence, WordList.English);

            if (testRecovery.ToString() != sentence ||
                !ByteArrays.AreAllBytesEqual(testRecovery.DeriveSeed(), seed))
            {
                throw new InvalidOperationException("This seed cannot be recovered.");
            }

            return(seed, sentence);
        }
Beispiel #9
0
        public KeyMaterial64 GetInitialE2EDecryptionKey(byte[] dynamicPublicKey)
        {
            var dynamicSharedSecret  = this._visualCrypt2Service.CalculateAndHashSharedSecret(this._myStaticPrivateKey, dynamicPublicKey);
            var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]);

            return(new KeyMaterial64(symmetricKeyMaterial));
        }
Beispiel #10
0
        public static byte[] Build(IList <byte[]> merkleLeaves)
        {
            if (merkleLeaves == null || merkleLeaves.Count == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(merkleLeaves));
            }

            while (true)
            {
                if (merkleLeaves.Count == 1)
                {
                    return(merkleLeaves[0]);
                }

                if (merkleLeaves.Count % 2 > 0)
                {
                    merkleLeaves.Add(merkleLeaves[merkleLeaves.Count - 1]); // merkleLeaves[^1] last element
                }

                var merkleBranches = new List <byte[]>();

                for (int i = 0; i < merkleLeaves.Count; i += 2)
                {
                    var leafBytePair    = ByteArrays.Concatenate(merkleLeaves[i], merkleLeaves[i + 1]);
                    var newMerkleBranch = DoubleSha256(leafBytePair);
                    merkleBranches.Add(newMerkleBranch);
                }

                merkleLeaves = merkleBranches;
            }
        }
Beispiel #11
0
        public static byte[] Serialize(this SlimBlock slimBlock)
        {
            // These buffers will be concatenated to create the full block;
            List <byte[]> parts = new List <byte[]>();

            // Block header
            parts.Add(slimBlock.SlimBlockHeader.Data);

            // Number of tx in the block
            byte[] nTransactions = new byte[1];

            // this must be VarInt, todo: use real varint
            var txCount = slimBlock.IsProofOfStake ? slimBlock.PayloadTransactions.Count + 2 : slimBlock.PayloadTransactions.Count + 1;

            if (txCount > 252)
            {
                throw new InvalidOperationException($"Value of {txCount} needs a real VarInt");
            }
            nTransactions[0] = (byte)txCount;
            parts.Add(nTransactions);

            // Transactions
            var coinbaseTxBytes = slimBlock.CoinbaseTransaction.ToBytes();

            parts.Add(coinbaseTxBytes);

            if (slimBlock.IsProofOfStake)
            {
                Debug.Assert(slimBlock.CoinstakeTransaction.IsCoinstake());
                var coinstakeTxBytes = slimBlock.CoinstakeTransaction.ToBytes();
                parts.Add(coinstakeTxBytes);
            }

            foreach (var payloadTransaction in slimBlock.PayloadTransactions)
            {
                parts.Add(payloadTransaction.ToBytes());
            }

            // Block signature as VarString. First byte of VarString is length as VarInt
            if (slimBlock.IsProofOfStake)
            {
                // 252 (0xfc) is the max for a 1-byte VarInt, and we assume the signature is not longer
                Debug.Assert(slimBlock.SignatureBytes.Length <= 252);
                parts.Add(slimBlock.SignatureBytes); // SignatureBytes is written by the BlockSignature ReadWrite method. This should already be in VarString format.
                //byte[] signature = new byte[1 + slimBlock.SignatureBytes.Length];
                //signature[0] = (byte)slimBlock.SignatureBytes.Length;
                //Buffer.BlockCopy(slimBlock.SignatureBytes, 0, signature, 1, slimBlock.SignatureBytes.Length);
                //parts.Add(signature);
            }
            else
            {
                parts.Add(slimBlock.SignatureBytes);
            }

            var blockBytes = ByteArrays.Concatenate(parts.ToArray());

            return(blockBytes);
        }
Beispiel #12
0
        public KeyMaterial64 GetInitialE2EDecryptionKey(byte[] dynamicPublicKey)
        {
            EnsureInitialized();

            var dynamicSharedSecret  = this.ixdsCryptoService.CalculateAndHashSharedSecret(this.parameters.OwnStaticPrivateKey, dynamicPublicKey);
            var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]);

            return(new KeyMaterial64(symmetricKeyMaterial));
        }
Beispiel #13
0
        public static byte[] SerializeTo80Bytes(this SlimBlockHeader slimBlockHeader)
        {
            var versionBytes = BitConverter.GetBytes(slimBlockHeader.Version);
            var timeBytes    = BitConverter.GetBytes(slimBlockHeader.Timestamp);
            var bitsBytes    = BitConverter.GetBytes(slimBlockHeader.Bits);
            var nonceBytes   = BitConverter.GetBytes(slimBlockHeader.Nonce);

            return(ByteArrays.Concatenate(versionBytes, slimBlockHeader.HashPrevBlock, slimBlockHeader.MerkleRoot, timeBytes, bitsBytes, nonceBytes));
        }
        public async Task <string> AddIdentity(XIdentity identity, Action <string, byte[]> initTlsUser)
        {
            await SemaphoreSlim.WaitAsync();

            try
            {
                if (identity == null)
                {
                    throw new ArgumentNullException(nameof(XIdentity));
                }
                if (identity.Id == null)
                {
                    throw new ArgumentNullException(nameof(XIdentity.Id));
                }
                if (identity.PublicIdentityKey == null)
                {
                    throw new ArgumentNullException(nameof(XIdentity.PublicIdentityKey));
                }
                if (ChatId.GenerateChatId(identity.PublicIdentityKey) != identity.Id)
                {
                    throw new Exception("Id and public key are unrelated.");
                }
                identity.ContactState = ContactState.Valid;

                XIdentity existing = await this.identitiesRepository.Get(identity.Id);

                if (existing == null)
                {
                    await this.identitiesRepository.Add(identity);
                }
                else
                {
                    if (!ByteArrays.AreAllBytesEqual(identity.PublicIdentityKey, existing.PublicIdentityKey))
                    {
                        throw new Exception($"Different new PublicKey for {identity.Id}. Ignoring request!");
                    }
                    existing.LastSeenUTC = DateTime.UtcNow;
                    await this.identitiesRepository.Update(existing);
                }


                if (initTlsUser != null) // TLS
                {
                    initTlsUser(identity.Id, identity.PublicIdentityKey);
                }

                this.logger.LogInformation($"Identity {identity.Id} was published.", nameof(MessageNodeRepository));
                return(identity.Id);
            }
            finally
            {
                SemaphoreSlim.Release();
            }
        }
Beispiel #15
0
        public static byte[] AddCheckSum(byte[] data)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            byte[] checkSum         = GetCheckSum(data);
            byte[] dataWithCheckSum = ByteArrays.Concatenate(data, checkSum);
            Debug.Assert(data.Length + CheckSumSizeInBytes == dataWithCheckSum.Length);
            return(dataWithCheckSum);
        }
        public static VisualCryptText CreateVisualCryptText(CipherV2 cipherV2)
        {
            Guard.NotNull(cipherV2);

            var visualCryptTextV2Bytes = ByteArrays.Concatenate(
                // len			Sum(len)		Start Index
                new[] { CipherV2.Version },                // 1			1				0
                new[] { cipherV2.RoundsExponent.Value },   // 1			2				1
                new[] { cipherV2.PlaintextPadding.Value }, // 1			3				2
                cipherV2.IV16.GetBytes(),                  // 16			19				3
                cipherV2.MACCipher16.GetBytes(),           // 16			35				19
                cipherV2.RandomKeyCipher32.GetBytes(),     // 32			67				35
                cipherV2.MessageCipher.GetBytes()          // len			67 + len		67
                );

            if (visualCryptTextV2Bytes.Length < HeaderLenght)
            {
                throw new Exception("Data cannot be shorter than the required header.");
            }

            var visualCryptTextV2Base64 = Base64Encoder.EncodeDataToBase64CharArray(visualCryptTextV2Bytes);

            var       sb          = new StringBuilder();
            const int breakAfter  = 74;
            var       charsInLine = 0;

            foreach (var c in VisualCryptSlashText)
            {
                sb.Append(c);
                if (++charsInLine != breakAfter)
                {
                    continue;
                }
                sb.Append(new[] { '\r', '\n' });
                charsInLine = 0;
            }

            foreach (var c in visualCryptTextV2Base64)
            {
                sb.Append(c == '/' ? '$' : c);
                if (++charsInLine != breakAfter)
                {
                    continue;
                }
                sb.Append(new[] { '\r', '\n' });
                charsInLine = 0;
            }

            return(new VisualCryptText(sb.ToString()));
        }
        public static bool EqualDeep(this XMessage m1, XMessage m2)
        {
            if (ReferenceEquals(m1, m2))
            {
                return(true);
            }

            if (m1 == null || m2 == null)
            {
                return(false);
            }
            if (m1.Id != m2.Id)
            {
                return(false);
            }

            if (m1.DynamicPublicKeyId != m2.DynamicPublicKeyId)
            {
                return(false);
            }
            if (m1.PrivateKeyHint != m2.PrivateKeyHint)
            {
                return(false);
            }

            if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.DynamicPublicKey, m2.DynamicPublicKey))
            {
                return(false);
            }

            if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.MetaCipher, m2.MetaCipher))
            {
                return(false);
            }

            if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.TextCipher, m2.TextCipher))
            {
                return(false);
            }

            if (!ByteArrays.AreAllBytesEqualOrBothNull(m1.ImageCipher, m2.ImageCipher))
            {
                return(false);
            }

            return(true);
        }
        public static byte[] CreateBinary(CipherV2 cipherV2)
        {
            Guard.NotNull(cipherV2);

            var visualCrypt2Bytes = ByteArrays.Concatenate(
                // len			Sum(len)		Start Index
                new[] { CipherV2.Version },                // 1			1				0
                new[] { cipherV2.RoundsExponent.Value },   // 1			2				1
                new[] { cipherV2.PlaintextPadding.Value }, // 1			3				2
                cipherV2.IV16.GetBytes(),                  // 16			19				3
                cipherV2.MACCipher16.GetBytes(),           // 16			35				19
                cipherV2.RandomKeyCipher32.GetBytes(),     // 32			67				35
                cipherV2.MessageCipher.GetBytes()          // len			67 + len		67
                );

            return(visualCrypt2Bytes);
        }
Beispiel #19
0
        public TLSEnvelope TLSServerEncryptRequestAnonymous(byte[] clearPacket, byte[] dynamicPublicKey, long dynamicPublicKeyID)
        {
            byte[] authSecret = new byte[32]; // we cannot create an authSecret based on the client's public key when we don't know who the client is.

            // we use the dynamicPublicKey and the server private key.
            var dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(this._serverPrivateKey, dynamicPublicKey);

            // the hint to the clients privk for pubkey he sent
            long privateKeyHint = dynamicPublicKeyID;

            // and now we create a dynamic public key, just to fit the protocol, but not intended for use.
            var random          = this.ixdsCryptoService.GetRandom(32).Result.X;
            var throwAwayPubKey = this.ixdsCryptoService.GenerateCurve25519KeyPairExact(random).Result.PublicKey;
            // and a fake id
            long fakeDynamicPublicKeyID = 9999; // use a realistic value, not 9999!

            Debug.WriteLine($"{this.ServerId}: TLSServerEncryptRequestAnonymous: FakeDynamicPublicKeyID: {fakeDynamicPublicKeyID}, PrivateKeyHint: {privateKeyHint}.");

            // Concatenate = 'TLSAuthMode.Dynamic' - THIS is anothe case!
            byte[] symmetricKeyMaterial64 = ByteArrays.Concatenate(dynamicSharedSecret, authSecret);

            // same as normally
            var lro        = new LongRunningOperation(progress => { }, () => { });
            var clearBytes = new Clearbytes(clearPacket);
            var sha512PW64 = new KeyMaterial64(symmetricKeyMaterial64);
            var method     = new RoundsExponent(0xff);

            var encryptResponse = this.ixdsCryptoService.BinaryEncrypt(clearBytes, sha512PW64, method, lro.Context);

            if (!encryptResponse.IsSuccess)
            {
                throw new Exception(encryptResponse.Error);
            }

            var encodeResponse = this.ixdsCryptoService.BinaryEncodeXDSSec(encryptResponse.Result, lro.Context);

            if (!encodeResponse.IsSuccess)
            {
                throw new Exception(encodeResponse.Error);
            }

            var tlsEnvelope = new TLSEnvelope(privateKeyHint, fakeDynamicPublicKeyID, throwAwayPubKey, encodeResponse.Result);

            return(tlsEnvelope);
        }
Beispiel #20
0
        /// <summary>
        /// Helper to append any number of byte arrays
        /// </summary>
        /// <param name="ByteArrays">The byte arrays to append</param>
        /// <returns>A joined byte array</returns>
        public static byte[] JoinByteArrays(params byte[][] ByteArrays)
        {
            // Count the total number of bytes
            int TotalBytes = ByteArrays.Sum(ByteArray => ByteArray.Length);

            // Copy all the bytes into the new array
            byte[] JoinedArray = new byte[TotalBytes];

            int NumBytesCopied = 0;

            foreach (var ByteArray in ByteArrays)
            {
                ByteArray.CopyTo(JoinedArray, NumBytesCopied);
                NumBytesCopied += ByteArray.Length;
            }

            return(JoinedArray);
        }
Beispiel #21
0
        async Task <Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64> > GetEndToEndEncryptionKeyAsync(string recipientId)
        {
            E2EUser user = await GetCheckedUserAsync(recipientId);

            long existingMaxKeyId = 0;

            if (user.DynamicPrivateDecryptionKeys.Keys.Count > 0)             // count might be 0 initially...might be a bug or not
            {
                existingMaxKeyId = user.DynamicPrivateDecryptionKeys.Keys.Max();
            }

            long nextDynamicPublicKeyId = this.ratchetTimer.GetNextTicks(existingMaxKeyId);

            var random      = this.ixdsCryptoService.GetRandom(32).Result.X;
            var ecdhKeypair = this.ixdsCryptoService.GenerateCurve25519KeyPairExact(random).Result;

            byte[] dynamicPublicKey = ecdhKeypair.PublicKey;

            long privateKeyHint;

            user.DynamicPrivateDecryptionKeys[nextDynamicPublicKeyId] = ecdhKeypair.PrivateKey;
            RemoveExcessKeys(user);
            await this.parameters.UpdateUser(user);

            byte[] dynamicOrStaticPublicKey;
            if (user.LatestDynamicPublicKey != null)
            {
                dynamicOrStaticPublicKey = user.LatestDynamicPublicKey;
                privateKeyHint           = user.LatestDynamicPublicKeyId;
            }
            else
            {
                dynamicOrStaticPublicKey = user.StaticPublicKey;
                privateKeyHint           = 0;
            }

            var dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(ecdhKeypair.PrivateKey, dynamicOrStaticPublicKey);

            var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, user.AuthSecret);

            return(new Tuple <KeyMaterial64, byte[], long, long, KeyMaterial64>(new KeyMaterial64(symmetricKeyMaterial), dynamicPublicKey, nextDynamicPublicKeyId, privateKeyHint, null));
        }
        public XDSPubKeyAddress CreateHdAddress(byte[] walletSeed, int accountIndex, int changePath, int addressIndex)
        {
            var keyPath    = new KeyPath($"m/44'/{XDSPrincipal.XDSCoinType}'/{accountIndex}'/{changePath}/{addressIndex}");
            var seedExtKey = new ExtKey(walletSeed);
            var derivedKey = seedExtKey.Derive(keyPath);

            CompressedPubKey compressedPubKey = derivedKey.PrivateKey.CompressedPubKey;
            var hash    = compressedPubKey.GetHash160();
            var bech    = new Bech32Encoder("xds");
            var address = bech.Encode(0, hash);

            return(new XDSPubKeyAddress
            {
                PrivateKey = derivedKey.PrivateKey.RawBytes,
                PublicKey = derivedKey.PrivateKey.CompressedPubKey.ToBytes(),
                Hash = hash,
                KeyPath = keyPath.ToString(),
                Address = address,
                ScriptPubKey = ByteArrays.Concatenate(new byte[1], hash)
            });
        }
Beispiel #23
0
        MAC16 CreateMAC(CipherV2 cipherV2, LongRunningOperationContext context)
        {
            // Create the MAC only for items that, while decrypting, have not been used up to this point but do include the version.
            var securables = ByteArrays.Concatenate(cipherV2.MessageCipher.GetBytes(), new[] { cipherV2.PlaintextPadding.Value },
                                                    new[] { CipherV2.Version });

            // See e.g. http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf Chapter 7 for hash truncation.
            byte[] truncatedHash = new byte[16];
            if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds)
            {
                var sha256 = this._platform.ComputeSHA256(securables);
                Buffer.BlockCopy(sha256, 0, truncatedHash, 0, 16);
            }
            else
            {
                context.EncryptionProgress.Message = LocalizableStrings.MsgCalculatingMAC;
                var bCrypt24 = BCrypt.CreateHash(cipherV2.IV16, securables, cipherV2.RoundsExponent.Value, context);
                Buffer.BlockCopy(bCrypt24.GetBytes(), 0, truncatedHash, 0, 16);
            }
            return(new MAC16(truncatedHash));
        }
        public static byte[] CreateBinaryDH(CipherV2 cipherV2)
        {
            Guard.NotNull(cipherV2);
            var randomKeyCipher32Placeholder = new byte[32];

            randomKeyCipher32Placeholder[0] = 1; // insert a bit so that the SecureBytes validation does not throw.
            cipherV2.RandomKeyCipher32      = new RandomKeyCipher32(randomKeyCipher32Placeholder);

            var visualCrypt2Bytes = ByteArrays.Concatenate(
                // len			Sum(len)		Start Index
                new[] { CipherV2.Version },                // 1			1				0
                new[] { cipherV2.RoundsExponent.Value },   // 1			2				1
                new[] { cipherV2.PlaintextPadding.Value }, // 1			3				2
                cipherV2.IV16.GetBytes(),                  // 16			19				3
                cipherV2.MACCipher16.GetBytes(),           // 16			35				19
                cipherV2.RandomKeyCipher32.GetBytes(),     // 32			67				35
                cipherV2.MessageCipher.GetBytes()          // len			67 + len		67
                );

            return(visualCrypt2Bytes);
        }
        public async Task <TLSEnvelope> EncryptRequest(byte[] clearPacket)
        {
            await this._publicMemberLock.WaitAsync();

            try
            {
                DynamicSecret dynamicSecret = GetDynamicSecretForEncryption();
                Debug.WriteLine(
                    $"{this.MyId}: TLSEncrypt: DynamicPublicKeyID: {dynamicSecret.DynamicPublicKeyId}, PrivateKeyHint: {dynamicSecret.PrivateKeyHint}.");

                byte[] authSecret = this._server.AuthSecret;

                var securable = ByteArrays.Concatenate(authSecret, this._myIdBytes, clearPacket);
                var symmetricKeyMaterial64 = ByteArrays.Concatenate(dynamicSecret.DynamicSharedSecret, new byte[32]);

                var lro           = new LongRunningOperation(progress => { }, () => { });
                var clearBytes    = new Clearbytes(securable);
                var keyMaterial64 = new KeyMaterial64(symmetricKeyMaterial64);
                var method        = new RoundsExponent(0xff);

                var encryptResponse = this._visualCrypt2Service.BinaryEncrypt(clearBytes, keyMaterial64, method, lro.Context);
                if (!encryptResponse.IsSuccess)
                {
                    throw new Exception(encryptResponse.Error);
                }

                var encodeResponse = this._visualCrypt2Service.BinaryEncodeVisualCrypt(encryptResponse.Result, lro.Context);
                if (!encodeResponse.IsSuccess)
                {
                    throw new Exception(encodeResponse.Error);
                }

                return(new TLSEnvelope(dynamicSecret.PrivateKeyHint, dynamicSecret.DynamicPublicKeyId,
                                       dynamicSecret.DynamicPublicKey, encodeResponse.Result));
            }
            finally
            {
                this._publicMemberLock.Release();
            }
        }
Beispiel #26
0
        public async Task <GetE2EDecryptionKeyResult> GetEndToEndDecryptionKeyAsync(string senderId, byte[] dynamicPublicKey, long privateKeyHint)
        {
            EnsureInitialized();

            var user = await GetCheckedUserAsync(senderId);

            var result = new GetE2EDecryptionKeyResult();


            byte[] dynamicPrivateKeyOrStaticPrivateKey;

            if (privateKeyHint == 0)
            {
                dynamicPrivateKeyOrStaticPrivateKey = this.parameters.OwnStaticPrivateKey;
                result.E2EDecryptionKeyType         = E2EDecryptionKeyType.MyStaticPrivateKey;
            }
            else
            {
                if (user.DynamicPrivateDecryptionKeys.TryGetValue(privateKeyHint, out dynamicPrivateKeyOrStaticPrivateKey))
                {
                    result.E2EDecryptionKeyType = E2EDecryptionKeyType.DynamicPrivateKey;
                }
                else
                {
                    // Possible reasons:
                    // - we did not look into the right user's ratchet while trying to determine the sender
                    // - The ratchets are not in sync, resend/new dynamic key exchange required
                    result.E2EDecryptionKeyType = E2EDecryptionKeyType.UnavailableDynamicPrivateKey;
                    return(result);
                }
            }

            var dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(dynamicPrivateKeyOrStaticPrivateKey, dynamicPublicKey);

            var symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, user.AuthSecret);

            result.E2EDecryptionKeyMaterial = new KeyMaterial64(symmetricKeyMaterial);
            return(result);
        }
Beispiel #27
0
        public static bool EqualDeep(this XIdentity id1, XIdentity id2)
        {
            if (ReferenceEquals(id1, id2))
            {
                return(true);
            }

            if (id1 == null || id2 == null)
            {
                return(false);
            }

            if (id1.Id != id2.Id)
            {
                return(false);
            }

            if (!ByteArrays.AreAllBytesEqualOrBothNull(id1.PublicIdentityKey, id2.PublicIdentityKey))
            {
                return(false);
            }

            return(true);
        }
        public async Task <TLSRequest> DecryptRequest(TLSEnvelope tlsEnvelope)
        {
            Guard.NotNull(tlsEnvelope);
            await this._publicMemberLock.WaitAsync();

            try
            {
                var ar = new TLSRequest();

                byte[] clientDynamicPrivateKey;
                this._server.DynamicPrivateDecryptionKeys.TryGetValue(tlsEnvelope.PrivateKeyHint, out clientDynamicPrivateKey);
                if (clientDynamicPrivateKey == null)
                {
                    throw new Exception(
                              "This should rarely happen. It means, the server has hinted me to private Key I no longer have.");
                }

                RemovePreviousKeys(tlsEnvelope.PrivateKeyHint);

                var dynamicSharedSecret = this._visualCrypt2Service.CalculateAndHashSharedSecret(clientDynamicPrivateKey,
                                                                                                 tlsEnvelope.DynamicPublicKey);
                Debug.WriteLine($"{this.MyId}: TLSDecrypt:  PrivateKeyHint: {tlsEnvelope.PrivateKeyHint}");

                // TLSAuthMode Combined
                byte[] authSecretBytes      = this._server.AuthSecret;
                var    symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, authSecretBytes);
                // End TLSAuthMode Combined

                // TODO: make LRO optional!
                var lro             = new LongRunningOperation(progress => { }, () => { });
                var cipherV2        = VisualCrypt2Formatter.DissectVisualCryptBytes(tlsEnvelope.EncipheredPayload, lro.Context);
                var decryptResponse = this._visualCrypt2Service.BinaryDecrypt(cipherV2,
                                                                              new KeyMaterial64(symmetricKeyMaterial), lro.Context);
                var isIncomingKeyUnusable = false;
                if (!decryptResponse.IsSuccess)
                {
                    // has the server just lost the dynamic keys?
                    this._server.DynamicPrivateDecryptionKeys.TryGetValue(tlsEnvelope.PrivateKeyHint,
                                                                          out clientDynamicPrivateKey);
                    if (clientDynamicPrivateKey == null)
                    {
                        throw new Exception(
                                  "This should rarely happen. It means, the server has hinted me to private Key I no longer have.");
                    }
                    dynamicSharedSecret = this._visualCrypt2Service.CalculateAndHashSharedSecret(clientDynamicPrivateKey,
                                                                                                 this._server.StaticPublicKey);
                    authSecretBytes      = new byte[32];
                    symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, authSecretBytes);
                    var decryptResponse2 = this._visualCrypt2Service.BinaryDecrypt(cipherV2,
                                                                                   new KeyMaterial64(symmetricKeyMaterial), lro.Context);
                    if (!decryptResponse2.IsSuccess)
                    {
                        throw new Exception("Decryption failed in all ways!");
                    }
                    decryptResponse = decryptResponse2;
                    Debug.WriteLine("Decryption succeded in Anonymous mode.");
                    DoReset();
                    isIncomingKeyUnusable = true;
                }

                byte[] tlsDecryptedRequest = decryptResponse.Result.GetBytes();

                ar.IsAuthenticated = true;
                ar.UserId          = this._server.UserId;
                ar.CommandData     = tlsDecryptedRequest;

                if (!isIncomingKeyUnusable)
                {
                    Guard.NotNull(tlsEnvelope.DynamicPublicKey);
                    if (tlsEnvelope.DynamicPublicKeyId == 0)
                    {
                        throw new ArgumentException("A dynamic public key must never have an ID of 0.");
                    }
                    this._server.LatestDynamicPublicKey   = tlsEnvelope.DynamicPublicKey;
                    this._server.LatestDynamicPublicKeyId = tlsEnvelope.DynamicPublicKeyId;
                }
                return(ar);
            }
            finally
            {
                this._publicMemberLock.Release();
            }
        }
Beispiel #29
0
        public TLSRequest TLSServerDecryptRequest(TLSEnvelope tlsEnvelope)
        {
            Guard.NotNull(tlsEnvelope);
            var ar = new TLSRequest();

            byte[] serverPrivateKey;             // static or dynamic
            if (tlsEnvelope.PrivateKeyHint == 0) // The client has used server's static public key to encrypt...
            {
                serverPrivateKey = this._serverPrivateKey;
            }
            // ... so we simple use the servers static private key
            else // tlsEnvelope.PrivateKeyHint is the number the server generated earlier
            {
                serverPrivateKey = GetServerDynamicPrivateKeyForUserByPrivateKeyHint(tlsEnvelope.PrivateKeyHint);
            }
            if (serverPrivateKey == null)
            {
                // we lost our dynamic private key b/c of a disk failure and we don't know who sent us what :-(
                return(null);
            }

            byte[] dynamicSharedSecret = this.ixdsCryptoService.CalculateAndHashSharedSecret(serverPrivateKey, tlsEnvelope.DynamicPublicKey);

            Debug.WriteLine($"{this.ServerId}: TLSDecrypt:  PrivateKeyHint: {tlsEnvelope.PrivateKeyHint}");

            // 'TLSAuthMode.Separate'
            byte[] symmetricKeyMaterial = ByteArrays.Concatenate(dynamicSharedSecret, new byte[32]); // no auth, we dont know who the user is
            // End 'TLSAuthMode.Separate'

            var lro             = new LongRunningOperation(progress => { }, () => { });
            var cipherV2        = XDSSecFormatter.DissectXDSSecBytes(tlsEnvelope.EncipheredPayload, lro.Context);
            var decryptResponse = this.ixdsCryptoService.BinaryDecrypt(cipherV2, new KeyMaterial64(symmetricKeyMaterial), lro.Context);

            if (!decryptResponse.IsSuccess)
            {
                throw new Exception(decryptResponse.Error);
            }

            byte[] tlsDecryptedRequest = decryptResponse.Result.GetBytes();

            // 'TLSAuthMode.Separate'
            var authSecretFromMessage = new byte[32];

            Buffer.BlockCopy(tlsDecryptedRequest, 0, authSecretFromMessage, 0, authSecretFromMessage.Length);

            // must be set even if the user is not authenticated for the case where a new identity is published right now.
            ar.UserId = Encoding.UTF8.GetString(tlsDecryptedRequest, 32, 10);
            // try authenticate
            var authSecret = GetAuthSecret(ar.UserId);

            if (authSecret != null)
            {
                if (ByteArrays.AreAllBytesEqual(authSecretFromMessage, authSecret))
                {
                    ar.IsAuthenticated = true;
                }
            }
            else
            {
                ar.IsAuthenticated = false;
            }

            var requestData = new byte[tlsDecryptedRequest.Length - 32 - 10];

            Buffer.BlockCopy(tlsDecryptedRequest, 32 + 10, requestData, 0, requestData.Length);
            ar.CommandData = requestData;
            // End 'TLSAuthMode.Separate'

            if (ar.IsAuthenticated)
            {
                SaveIncomingDynamicPublicKey(ar.UserId, tlsEnvelope.DynamicPublicKey, tlsEnvelope.DynamicPublicKeyId);
            }

            return(ar);
        }
Beispiel #30
0
        /// <summary>
        /// Fully decrypts a message of all types, both control and content messages (including images), if an end-to-en-encryption key can be determined.
        /// </summary>
        async Task <Message> TryDecryptMessageToFindSenderEnryptDecryptionKeyAndSaveIt(XMessage xmessage)
        {
            try
            {
                IReadOnlyList <Identity> contacts = await this.repo.GetAllContacts();

                var decryptedMessage = new Message
                {
                    // XMessage fields
                    RecipientId = "1", // drop my Id

                    TextCipher  = xmessage.TextCipher,
                    ImageCipher = xmessage.ImageCipher,

                    DynamicPublicKey   = xmessage.DynamicPublicKey,
                    DynamicPublicKeyId = xmessage.DynamicPublicKeyId,
                    PrivateKeyHint     = xmessage.PrivateKeyHint,

                    LocalMessageState = LocalMessageState.JustReceived,
                    SendMessageState  = SendMessageState.None,
                    Side = MessageSide.You,

                    // This is what we try to decrypt here
                    MessageType = MessageType.None,
                    SenderId    = null,
                };


                Response <CipherV2> decodeMetaResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.MetaCipher, null);
                if (!decodeMetaResponse.IsSuccess)
                {
                    return(null); // garbled, no chance, ignore!
                }
                foreach (Identity identity in contacts)
                {
                    GetE2EDecryptionKeyResult getE2EDecryptionKeyResult = await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(identity.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint);

                    if (getE2EDecryptionKeyResult.E2EDecryptionKeyType == E2EDecryptionKeyType.UnavailableDynamicPrivateKey)
                    {
                        continue; // there was a privateKeyHint != 0 in the message, but the dynamic private key was not in the ratchet for user in the loop
                    }
                    KeyMaterial64 keyMaterial64 = getE2EDecryptionKeyResult.E2EDecryptionKeyMaterial;

                    var decryptMetaResponse = this.ixdsCryptoService.BinaryDecrypt(decodeMetaResponse.Result, keyMaterial64, null);
                    if (!decryptMetaResponse.IsSuccess)
                    {
                        continue;
                    }

                    await this.e2eRatchet.SaveIncomingDynamicPublicKeyOnSuccessfulDecryptionAsync(identity.Id, xmessage.DynamicPublicKey,
                                                                                                  xmessage.DynamicPublicKeyId);

                    XMessageMetaData metadata = decryptMetaResponse.Result.GetBytes().DeserializeMessageMetadata();

                    decryptedMessage.SenderId             = identity.Id;
                    decryptedMessage.MessageType          = metadata.MessageType;
                    decryptedMessage.SenderLocalMessageId = metadata.SenderLocalMessageId.ToString();


                    if (decryptedMessage.MessageType.IsReceipt())
                    {
                        return(decryptedMessage);
                    }

                    if (decryptedMessage.MessageType.IsContent())
                    {
                        if (decryptedMessage.MessageType == MessageType.Text || decryptedMessage.MessageType == MessageType.TextAndMedia || decryptedMessage.MessageType == MessageType.File)
                        {
                            // if the message has text, decrypt all text
                            var decodeTextResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.TextCipher, null);
                            if (!decodeTextResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            var decrpytTextResponse = this.ixdsCryptoService.Decrypt(decodeTextResponse.Result, keyMaterial64, null);
                            if (!decrpytTextResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            decryptedMessage.ThreadText = decrpytTextResponse.Result.Text;
                        }

                        if (decryptedMessage.MessageType == MessageType.Media || decryptedMessage.MessageType == MessageType.TextAndMedia || decryptedMessage.MessageType == MessageType.File)
                        {
                            // if the message has image content, decrypt the image content
                            var decodeImageResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.ImageCipher, null);
                            if (!decodeImageResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            var decrpytImageResponse = this.ixdsCryptoService.BinaryDecrypt(decodeImageResponse.Result, keyMaterial64, null);
                            if (!decrpytImageResponse.IsSuccess)
                            {
                                return(null); // something is wrong, should have worked
                            }
                            decryptedMessage.ThreadMedia = decrpytImageResponse.Result.GetBytes();
                        }

                        decryptedMessage.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial64.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey());
                        decryptedMessage.LocalMessageState         = LocalMessageState.Integrated;
                        await this.repo.AddMessage(decryptedMessage);
                    }
                    else
                    {
                        throw new Exception($"Invalid MessageType {decryptedMessage.MessageType}");
                    }

                    return(decryptedMessage);
                } // foreach



                // If we are here, assume it's a new contact's message or RESENT message:
                CipherV2      encryptedMetaData          = decodeMetaResponse.Result;
                KeyMaterial64 initialKey                 = this.e2eRatchet.GetInitialE2EDecryptionKey(xmessage.DynamicPublicKey);
                var           decryptInitialMetaResponse = this.ixdsCryptoService.BinaryDecrypt(encryptedMetaData, initialKey, null);
                if (decryptInitialMetaResponse.IsSuccess)
                {
                    XMessageMetaData initialMessageMetadata =
                        decryptInitialMetaResponse.Result.GetBytes().DeserializeMessageMetadata();
                    var incomingPublicKey = initialMessageMetadata.SenderPublicKey;

                    // If we received several resent messages, the first from that incoming contact has already produced that contact:
                    var  contact           = contacts.SingleOrDefault(c => ByteArrays.AreAllBytesEqual(c.StaticPublicKey, incomingPublicKey));
                    bool isIncomingContact = false;

                    if (contact == null)
                    {
                        // Create new contact and save it to make the ratchet work normally
                        isIncomingContact = true;
                        var date            = DateTime.UtcNow;
                        var incomingContact = new Identity
                        {
                            Id = Guid.NewGuid().ToString(),
                            StaticPublicKey = incomingPublicKey,
                            ContactState    = ContactState.Valid,
                            Name            = "Anonymous",
                            FirstSeenUtc    = date,
                            LastSeenUtc     = date
                        };

                        await this.repo.AddContact(incomingContact);

                        contact = incomingContact;
                    }



                    await this.e2eRatchet.SaveIncomingDynamicPublicKeyOnSuccessfulDecryptionAsync(contact.Id, xmessage.DynamicPublicKey,
                                                                                                  xmessage.DynamicPublicKeyId);

                    // add metadata to message
                    decryptedMessage.SenderId             = contact.Id;
                    decryptedMessage.MessageType          = initialMessageMetadata.MessageType;
                    decryptedMessage.SenderLocalMessageId = initialMessageMetadata.SenderLocalMessageId.ToString();

                    if (decryptedMessage.MessageType.IsContent())
                    {
                        var success = true;

                        GetE2EDecryptionKeyResult getE2EDecryptionKeyResult = await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(contact.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint);

                        KeyMaterial64 keyMaterial64 = getE2EDecryptionKeyResult.E2EDecryptionKeyMaterial;
                        await this.e2eRatchet.GetEndToEndDecryptionKeyAsync(contact.Id, xmessage.DynamicPublicKey, xmessage.PrivateKeyHint);

                        if (decryptedMessage.MessageType == MessageType.Text || decryptedMessage.MessageType == MessageType.TextAndMedia)
                        {
                            // if the message has text, decrypt all text
                            var decodeTextResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.TextCipher, null);
                            if (!decodeTextResponse.IsSuccess)
                            {
                                success = false; // something is wrong, should have worked
                            }
                            else
                            {
                                var decrpytTextResponse = this.ixdsCryptoService.Decrypt(decodeTextResponse.Result, keyMaterial64, null);
                                if (!decrpytTextResponse.IsSuccess)
                                {
                                    success = false; // something is wrong, should have worked
                                }
                                else
                                {
                                    decryptedMessage.ThreadText = decrpytTextResponse.Result.Text;
                                }
                            }
                        }

                        if (decryptedMessage.MessageType == MessageType.Media || decryptedMessage.MessageType == MessageType.TextAndMedia)
                        {
                            // if the message has image content, decrypt the image content
                            var decodeImageResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(xmessage.ImageCipher, null);
                            if (!decodeImageResponse.IsSuccess)
                            {
                                success = false; // something is wrong, should have worked
                            }
                            else
                            {
                                var decrpytImageResponse = this.ixdsCryptoService.BinaryDecrypt(decodeImageResponse.Result, keyMaterial64, null);
                                if (!decrpytImageResponse.IsSuccess)
                                {
                                    success = false; // something is wrong, should have worked
                                }
                                else
                                {
                                    decryptedMessage.ThreadMedia = decrpytImageResponse.Result.GetBytes();
                                }
                            }
                        }

                        if (success)
                        {
                            decryptedMessage.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial64.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey());
                            decryptedMessage.LocalMessageState         = LocalMessageState.Integrated;
                            await this.contactListManager.ChatWorker_ContactUpdateReceived(null, contact.Id);

                            await this.repo.AddMessage(decryptedMessage);

                            return(decryptedMessage);
                        }
                        else
                        {
                            if (isIncomingContact)
                            {
                                await this.repo.DeleteContacts(new[] { contact.Id }); // delete the just incoming contact if the was an error anyway.
                            }

                            return(null);
                        }
                    }
                    else
                    {
                        return(null);
                    }
                }

                // nope, resend request (I hope we don't loop here!)
                string networkPayloadHash = NetworkPayloadHash.ComputeAsGuidString(xmessage.SerializedPayload);
                if (!this._resendsRequested.Contains(networkPayloadHash))
                {
                    var response = await this.chatClient.UploadResendRequest(new XResendRequest { Id = networkPayloadHash, RecipientId = null });

                    if (response.IsSuccess)
                    {
                        this._resendsRequested.Add(networkPayloadHash);
                    }
                }
            }
            catch (Exception e)
            {
                this.logger.LogError(e.Message);
            }

            return(null);
        }