Compressed DecryptCommon(CipherV2 cipherV2, KeyMaterial64 keyMaterial64, LongRunningOperationContext context) { if (context == null) { context = new LongRunningOperation(progress => { }, () => { }).Context; } Guard.NotNull(new object[] { cipherV2, keyMaterial64 }); EnsurePlatform(); InputDerivedKey32 inputDerivedKey = cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds ? CreateDerivedKeyWithSHA256(cipherV2.IV16, keyMaterial64) : CreatePasswordDerivedKeyWithBCrypt(cipherV2.IV16, keyMaterial64, cipherV2.RoundsExponent, context); XDSSecAPIInternal.IVCache ivCache = cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds ? null : this._internal.CreateIVTable(cipherV2.IV16, cipherV2.RoundsExponent.Value); RandomKey32 randomKey = this._internal.AESDecryptRandomKeyWithPasswordDerivedKey(cipherV2, inputDerivedKey, ivCache, context); MAC16 decryptedMAC = this._internal.AESDecryptMAC(cipherV2, randomKey, ivCache, context); MAC16 actualMAC = CreateMAC(cipherV2, context); if (!actualMAC.GetBytes().SequenceEqual(decryptedMAC.GetBytes())) { throw new Exception(LocalizableStrings.MsgPasswordError); } PaddedData paddedData = this._internal.AESDecryptMessage(cipherV2, cipherV2.IV16, randomKey, ivCache, context); Compressed compressed = this._internal.RemovePadding(paddedData); return(compressed); }
public static byte[] EncryptClient(byte[] plaintextBytes, byte[] serverAuthPubKey, byte[] serverSessionPublicKey, byte[] singleClientPrivateKey) { if (plaintextBytes == null) { throw new ArgumentNullException(nameof(plaintextBytes)); } if (serverAuthPubKey == null) { throw new ArgumentNullException(nameof(serverAuthPubKey)); } if (serverSessionPublicKey == null) { throw new ArgumentNullException(nameof(serverSessionPublicKey)); } if (singleClientPrivateKey == null) { throw new ArgumentNullException(nameof(singleClientPrivateKey)); } var hashedSharedSecretBytes = VCL.Instance().CalculateAndHashSharedSecret(singleClientPrivateKey, serverSessionPublicKey); var authSecretBytes = VCL.Instance().CalculateAndHashSharedSecret(singleClientPrivateKey, serverAuthPubKey); var keyMaterial64 = ToKeyMaterial64(hashedSharedSecretBytes, authSecretBytes); CipherV2 cipher = VCL.Instance().BinaryEncrypt(new Clearbytes(plaintextBytes), keyMaterial64, new RoundsExponent(RoundsExponent.DontMakeRounds), VCL.GetContext()).Result; return(VCL.Instance().BinaryEncodeXDSSec(cipher, VCL.GetContext()).Result); }
async Task EncryptAndSaveDeviceVaultKeyAsync(KeyMaterial64 masterRandomKey, KeyMaterial64 masterPassphraseKeyMaterial, LongRunningOperationContext context) { context.EncryptionProgress.Message = "Encrypting master random key"; context.EncryptionProgress.Report(context.EncryptionProgress); CipherV2 encryptedMasterRandomKey = this.xdsCryptoService.BinaryEncrypt(new Clearbytes(masterRandomKey.GetBytes()), masterPassphraseKeyMaterial, new RoundsExponent(10), context).Result; byte[] encryptedMasterRandomKeyBytes = this.xdsCryptoService.BinaryEncodeXDSSec(encryptedMasterRandomKey, context).Result; await this.repo.WriteSpecialFile(MasterKeyFilename, encryptedMasterRandomKeyBytes); }
public static byte[] EncryptWithPassphrase(string passphrase, byte[] bytesToEncryt) { var context = GetContext(); NormalizedPassword normalizedPassword = Instance().NormalizePassword(passphrase).Result; KeyMaterial64 passwordDerivedkeyMaterial64 = Instance().HashPassword(normalizedPassword).Result; CipherV2 cipherV2 = Instance().BinaryEncrypt(new Clearbytes(bytesToEncryt), passwordDerivedkeyMaterial64, new RoundsExponent(RoundsExponent.DontMakeRounds), context).Result; var cipherV2Bytes = Instance().BinaryEncodeXDSSec(cipherV2, context).Result; return(cipherV2Bytes); }
public byte[] DefaultDecrypt(byte[] cipherTextBytes, KeyMaterial64 keyMaterial64, LongRunningOperationContext context = null) { CipherV2 cipherV2 = XDSSecFormatter.DissectXDSSecBytes(cipherTextBytes, context); var binaryDecryptResponse = BinaryDecrypt(cipherV2, keyMaterial64, context); if (!binaryDecryptResponse.IsSuccess) { throw new Exception(binaryDecryptResponse.Error); } return(binaryDecryptResponse.Result.GetBytes()); }
static CipherV2 DissectBytesToCipherV2(byte[] visualCryptBytes) { var version = visualCryptBytes[0]; var exponent = visualCryptBytes[1]; var padding = visualCryptBytes[2]; if (version != CipherV2.Version) { throw CommonFormatException("Expected a version byte at index 0 of value '2'."); } if ((exponent > 31 || exponent < 4) && exponent != 0xff) { throw CommonFormatException("The value for the rounds exponent at index 1 is invalid."); } if (padding > 15) { throw CommonFormatException("The value at the padding byte at index 1 is invalid."); } var cipher = new CipherV2 { PlaintextPadding = new PlaintextPadding(padding), RoundsExponent = new RoundsExponent(exponent) }; var iv16 = new byte[16]; Buffer.BlockCopy(visualCryptBytes, 3, iv16, 0, 16); cipher.IV16 = new IV16(iv16); var macCipher = new byte[16]; Buffer.BlockCopy(visualCryptBytes, 19, macCipher, 0, 16); cipher.MACCipher16 = new MACCipher16(macCipher); var randomKeyCipher = new byte[32]; Buffer.BlockCopy(visualCryptBytes, 35, randomKeyCipher, 0, 32); cipher.RandomKeyCipher32 = new RandomKeyCipher32(randomKeyCipher); var cipherBytes = new byte[visualCryptBytes.Length - 67]; Buffer.BlockCopy(visualCryptBytes, 67, cipherBytes, 0, cipherBytes.Length); cipher.MessageCipher = new MessageCipher(cipherBytes); return(cipher); }
public static byte[] DecryptWithPassphrase(string passphrase, byte[] bytesToDecrypt) { var context = GetContext(); NormalizedPassword normalizedPassword = Instance().NormalizePassword(passphrase).Result; KeyMaterial64 passwordDerivedkeyMaterial64 = Instance().HashPassword(normalizedPassword).Result; CipherV2 cipherV2 = Instance().BinaryDecodeXDSSec(bytesToDecrypt, context).Result; var response = Instance().BinaryDecrypt(cipherV2, passwordDerivedkeyMaterial64, context); if (response.IsSuccess) { return(response.Result.GetBytes()); } return(null); }
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 MAC16 AESDecryptMAC(CipherV2 cipherV2, RandomKey32 randomKey, IVCache ivCache, LongRunningOperationContext context) { Guard.NotNull(new object[] { cipherV2, randomKey, context }); byte[] macBytes; if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds) { macBytes = this._platform.ComputeAESRound(AESDir.Decrpyt, cipherV2.IV16.GetBytes(), cipherV2.MACCipher16.GetBytes(), randomKey.GetBytes()); } else { context.EncryptionProgress.Message = LocalizableStrings.MsgDecryptingMAC; macBytes = ComputeAESWithRounds(AESDir.Decrpyt, cipherV2.IV16, cipherV2.MACCipher16.GetBytes(), randomKey.GetBytes(), cipherV2.RoundsExponent.Value, ivCache, context); } return(new MAC16(macBytes)); }
public static byte[] Decrypt(byte[] cipherV2Bytes, byte[] publicKey, byte[] privateKey, byte[] privateAuthKey) { var hashedSharedSecretBytes = VCL.Instance().CalculateAndHashSharedSecret(privateKey, publicKey); var authSecretBytes = VCL.Instance().CalculateAndHashSharedSecret(privateAuthKey, publicKey); var keyMaterial64 = ToKeyMaterial64(hashedSharedSecretBytes, authSecretBytes); CipherV2 cipherV2FromClient = VCL.Instance().BinaryDecodeVisualCrypt(cipherV2Bytes, VCL.GetContext()).Result; var binaryDecryptResponse = VCL.Instance().BinaryDecrypt(cipherV2FromClient, keyMaterial64, VCL.GetContext()); if (!binaryDecryptResponse.IsSuccess && binaryDecryptResponse.Error == LocalizableStrings.MsgPasswordError) { return(null); } return(binaryDecryptResponse.Result.GetBytes()); }
public void AESEncryptMACWithRandomKey(CipherV2 cipherV2, MAC16 mac, RandomKey32 randomKey, IVCache ivCache, LongRunningOperationContext context) { Guard.NotNull(new object[] { cipherV2, mac, randomKey, context }); if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds) { Debug.Assert(ivCache == null); cipherV2.MACCipher16 = new MACCipher16(this._platform.ComputeAESRound(AESDir.Encrypt, cipherV2.IV16.GetBytes(), mac.GetBytes(), randomKey.GetBytes())); } else { context.EncryptionProgress.Message = LocalizableStrings.MsgEncryptingMAC; cipherV2.MACCipher16 = new MACCipher16(ComputeAESWithRounds(AESDir.Encrypt, cipherV2.IV16, mac.GetBytes(), randomKey.GetBytes(), cipherV2.RoundsExponent.Value, ivCache, context)); } }
/// <summary> /// Changes the master password by encrypting the master random key with a new password and updating the key file. /// </summary> /// <param name="currentMasterPassword">The current master password.</param> /// <param name="futureMasterPassword">The future master password.</param> /// <returns></returns> public async Task ChangeMasterPasswordAsync(string currentMasterPassword, string futureMasterPassword) { // Verify the user knows the current master password await TryLoadDecryptAndSetMasterRandomKeyAsync(currentMasterPassword, null); KeyMaterial64 futureMasterPasswordHash = CreateKeyMaterialFromPassphrase(futureMasterPassword); // get the current master random key in plaintext KeyMaterial64 currentPlaintextMasterRandomKey = this.xdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey(); // encrypt the current master random key for saving in the key file and write the file. CipherV2 encryptedMasterRandomKey = this.xdsCryptoService.BinaryEncrypt(new Clearbytes(currentPlaintextMasterRandomKey.GetBytes()), futureMasterPasswordHash, new RoundsExponent(10), null).Result; byte[] encryptedMasterRandomKeyBytes = this.xdsCryptoService.BinaryEncodeXDSSec(encryptedMasterRandomKey, null).Result; await this.repo.WriteSpecialFile(MasterKeyFilename, encryptedMasterRandomKeyBytes); }
public RandomKey32 AESDecryptRandomKeyWithPasswordDerivedKey(CipherV2 cipherV2, InputDerivedKey32 inputDerivedKey, IVCache ivCache, LongRunningOperationContext context) { Guard.NotNull(new object[] { cipherV2, inputDerivedKey, context }); byte[] randomKeyBytes; if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds) { randomKeyBytes = this._platform.ComputeAESRound(AESDir.Decrpyt, cipherV2.IV16.GetBytes(), cipherV2.RandomKeyCipher32.GetBytes(), inputDerivedKey.GetBytes()); } else { context.EncryptionProgress.Message = LocalizableStrings.MsgDecryptingRandomKey; randomKeyBytes = ComputeAESWithRounds(AESDir.Decrpyt, cipherV2.IV16, cipherV2.RandomKeyCipher32.GetBytes(), inputDerivedKey.GetBytes(), cipherV2.RoundsExponent.Value, ivCache, context); } return(new RandomKey32(randomKeyBytes)); }
public PaddedData AESDecryptMessage(CipherV2 cipherV2, IV16 iv16, RandomKey32 randomKey, IVCache ivCache, LongRunningOperationContext context) { Guard.NotNull(new object[] { cipherV2, iv16, randomKey, context }); byte[] paddedDataBytes; if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds) { paddedDataBytes = this._platform.ComputeAESRound(AESDir.Decrpyt, cipherV2.IV16.GetBytes(), cipherV2.MessageCipher.GetBytes(), randomKey.GetBytes()); } else { context.EncryptionProgress.Message = LocalizableStrings.MsgDecryptingMessage; paddedDataBytes = ComputeAESWithRounds(AESDir.Decrpyt, cipherV2.IV16, cipherV2.MessageCipher.GetBytes(), randomKey.GetBytes(), cipherV2.RoundsExponent.Value, ivCache, context); } return(new PaddedData(paddedDataBytes, cipherV2.PlaintextPadding)); }
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); }
public Response <byte[]> BinaryEncodeXDSSec(CipherV2 cipherV2, LongRunningOperationContext context) { var response = new Response <byte[]>(); try { Guard.NotNull(cipherV2); EnsurePlatform(); context?.CancellationToken.ThrowIfCancellationRequested(); response.Result = XDSSecFormatter.CreateBinary(cipherV2); response.SetSuccess(); } catch (Exception e) { response.SetError(e); } return(response); }
public Response <XDSSecText> EncodeXDSSec(CipherV2 cipherV2) { var response = new Response <XDSSecText>(); try { Guard.NotNull(cipherV2); EnsurePlatform(); response.Result = XDSSecFormatter.CreateXDSSecText(cipherV2); response.SetSuccess(); } catch (Exception e) { response.SetError(e); } return(response); }
public Response <Clearbytes> BinaryDecrypt(CipherV2 cipherV2, KeyMaterial64 keyMaterial64, LongRunningOperationContext context) { var response = new Response <Clearbytes>(); try { Compressed compressed = DecryptCommon(cipherV2, keyMaterial64, context); Clearbytes cleartext = this._internal.DecompressBytes(compressed); response.Result = cleartext; response.SetSuccess(); } catch (Exception e) { response.SetError(e); } return(response); }
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); }
CipherV2 EncryptCommon(KeyMaterial64 keyMaterial64, RoundsExponent roundsExponent, LongRunningOperationContext context, Compressed compressed) { if (context == null) { context = new LongRunningOperation(progress => { }, () => { }).Context; } if (this._log) { Debug.WriteLine("KeyMaterial64:"); Debug.WriteLine(keyMaterial64.GetBytes().ToHexView(false)); } if (this._log) { Debug.WriteLine("Compressed:"); Debug.WriteLine(compressed.GetBytes().ToHexView(false)); } PaddedData paddedData = this._internal.ApplyRandomPadding(compressed); if (this._log) { Debug.WriteLine("PaddedData:"); Debug.WriteLine(paddedData.GetBytes().ToHexView(false)); Debug.WriteLine("PlainTextPadding:"); Debug.WriteLine(paddedData.PlaintextPadding); } IV16 iv = new IV16(this._platform.GenerateRandomBytes(16)); if (this._log) { Debug.WriteLine("IV16:"); Debug.WriteLine(iv.GetBytes().ToHexView(false)); } InputDerivedKey32 inputDerivedKey = roundsExponent.Value == RoundsExponent.DontMakeRounds ? CreateDerivedKeyWithSHA256(iv, keyMaterial64) : CreatePasswordDerivedKeyWithBCrypt(iv, keyMaterial64, roundsExponent, context); if (this._log) { Debug.WriteLine("InputDerivedKey32:"); Debug.WriteLine(inputDerivedKey.GetBytes().ToHexView(false)); } RandomKey32 randomKey = new RandomKey32(this._platform.GenerateRandomBytes(32)); if (this._log) { Debug.WriteLine("RandomKey32:"); Debug.WriteLine(randomKey.GetBytes().ToHexView(false)); } XDSSecAPIInternal.IVCache ivCache = roundsExponent.Value == RoundsExponent.DontMakeRounds ? null : this._internal.CreateIVTable(iv, roundsExponent.Value); var cipherV2 = new CipherV2 { RoundsExponent = roundsExponent, IV16 = iv }; this._internal.AESEncryptRandomKeyWithInputDerivedKey(inputDerivedKey, randomKey, cipherV2, ivCache, context); if (this._log) { Debug.WriteLine("RandomKeyCipher32:"); Debug.WriteLine(cipherV2.RandomKeyCipher32.GetBytes().ToHexView(false)); } this._internal.AESEncryptMessageWithRandomKey(paddedData, randomKey, cipherV2, ivCache, context); if (this._log) { Debug.WriteLine("MessageCipher:"); Debug.WriteLine(cipherV2.MessageCipher.GetBytes().ToHexView(false)); } MAC16 mac = CreateMAC(cipherV2, context); if (this._log) { Debug.WriteLine("MAC16:"); Debug.WriteLine(mac.GetBytes().ToHexView(false)); } this._internal.AESEncryptMACWithRandomKey(cipherV2, mac, randomKey, ivCache, context); if (this._log) { Debug.WriteLine("MACCipher16:"); Debug.WriteLine(cipherV2.MACCipher16.GetBytes().ToHexView(false)); } return(cipherV2); }
public void AESEncryptMessageWithRandomKey(PaddedData paddedData, RandomKey32 randomKey, CipherV2 cipherV2, IVCache ivCache, LongRunningOperationContext context) { Guard.NotNull(new object[] { paddedData, randomKey, cipherV2, context }); cipherV2.PlaintextPadding = paddedData.PlaintextPadding; if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds) { Debug.Assert(ivCache == null); cipherV2.MessageCipher = new MessageCipher(this._platform.ComputeAESRound(AESDir.Encrypt, cipherV2.IV16.GetBytes(), paddedData.GetBytes(), randomKey.GetBytes())); } else { context.EncryptionProgress.Message = LocalizableStrings.MsgEncryptingMessage; cipherV2.MessageCipher = new MessageCipher(ComputeAESWithRounds(AESDir.Encrypt, cipherV2.IV16, paddedData.GetBytes(), randomKey.GetBytes(), cipherV2.RoundsExponent.Value, ivCache, context)); } }
public void AESEncryptRandomKeyWithInputDerivedKey(InputDerivedKey32 inputDerivedKey, RandomKey32 randomKey, CipherV2 cipherV2, IVCache ivCache, LongRunningOperationContext context) { Guard.NotNull(new object[] { inputDerivedKey, randomKey, cipherV2, context }); if (cipherV2.RoundsExponent.Value == RoundsExponent.DontMakeRounds) { Debug.Assert(ivCache == null); cipherV2.RandomKeyCipher32 = new RandomKeyCipher32(this._platform.ComputeAESRound(AESDir.Encrypt, cipherV2.IV16.GetBytes(), randomKey.GetBytes(), inputDerivedKey.GetBytes())); } else { context.EncryptionProgress.Message = LocalizableStrings.MsgEncryptingRandomKey; cipherV2.RandomKeyCipher32 = new RandomKeyCipher32(ComputeAESWithRounds(AESDir.Encrypt, cipherV2.IV16, randomKey.GetBytes(), inputDerivedKey.GetBytes(), cipherV2.RoundsExponent.Value, ivCache, context)); } }
/// <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); }