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); }
void SaveFileToDownloads(Message message) { var fileName = message.ThreadText.Split(',').First().Trim(); var appDir = App.ServiceProvider.Get <ICancellation>().DataDirRoot.Parent; var exportDir = Path.Combine(appDir.FullName, "temp"); if (!Directory.Exists(exportDir)) { Directory.CreateDirectory(exportDir); } var xdsSec = App.ServiceProvider.Get <IXDSSecService>(); var decryptionkey = new KeyMaterial64(xdsSec.DefaultDecrypt(message.EncryptedE2EEncryptionKey, xdsSec.SymmetricKeyRepository.GetMasterRandomKey())); var plaintextBytes = xdsSec.DefaultDecrypt(message.ImageCipher, decryptionkey); var pathAndFileName = Path.Combine(exportDir, fileName); File.WriteAllBytes(pathAndFileName, plaintextBytes); MessageBox.Query("File decrypted", $"The file was decrypted and saved as {Path.Combine(exportDir, fileName)}. We'll try to delete it when you quit the app!", Strings.Ok); }
InputDerivedKey32 CreateDerivedKeyWithSHA256(IV16 iv, KeyMaterial64 keyMaterial64) { var keyMaterial = ByteArrays.Concatenate(iv.GetBytes(), keyMaterial64.GetBytes()); var derivedKey = this._platform.ComputeSHA256(keyMaterial); return(new InputDerivedKey32(derivedKey)); }
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); }
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)); }
KeyMaterial64 CreateKeyMaterialFromPassphrase(string passphrase) { NormalizedPassword normalizedPassword = this.xdsCryptoService.NormalizePassword(passphrase).Result; KeyMaterial64 hashedPassword = this.xdsCryptoService.HashPassword(normalizedPassword).Result; return(hashedPassword); }
public void ClearMasterRandomKey() { if (this._masterRandomKey != null) { this._masterRandomKey.GetBytes().FillWithZeros(); } this._masterRandomKey = null; }
public void SetMasterRandomKey(KeyMaterial64 masterRandomKey) { if (masterRandomKey == null) { throw new ArgumentNullException(nameof(masterRandomKey)); } this._masterRandomKey = masterRandomKey; }
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 byte[] DefaultEncrypt(byte[] plaintextBytes, KeyMaterial64 keyMaterial64) { var binaryEncryptResponse = BinaryEncrypt(new Clearbytes(plaintextBytes), keyMaterial64, new RoundsExponent(RoundsExponent.DontMakeRounds), null); if (binaryEncryptResponse.IsSuccess) { return(XDSSecFormatter.CreateBinary(binaryEncryptResponse.Result)); } throw new Exception(binaryEncryptResponse.Error); }
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); }
/// <summary> /// Loads the key file and tries to decrypt the master random key. Only if successful, it also sets the decrypted master random key in SymmetricKeyRepository. /// Then, Response.Success is true. Therefore, this method can also be used to check if the master password is correct. If it is incorrect, the method does nothing and /// Response.Success is false. /// </summary> /// <param name="unprunedUtf16LeMasterPassword">the master password</param> /// <param name="context">Progress object</param> public async Task TryLoadDecryptAndSetMasterRandomKeyAsync(string unprunedUtf16LeMasterPassword, LongRunningOperationContext context) { KeyMaterial64 masterPasswordHash = CreateKeyMaterialFromPassphrase(unprunedUtf16LeMasterPassword); byte[] encryptedRandomMasterKey = await this.repo.LoadSpecialFile(MasterKeyFilename); byte[] decryptedMasterRandomKey = this.xdsCryptoService.DefaultDecrypt(encryptedRandomMasterKey, masterPasswordHash, context); this.xdsCryptoService.SymmetricKeyRepository.SetDeviceVaultRandomKey(new KeyMaterial64(decryptedMasterRandomKey)); }
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()); }
void EncryptWithStrategy(Message message, KeyMaterial64 keyMaterial, RoundsExponent roundsExponent, KeyMaterial64 initialMetadataKeyMaterial) { XMessageMetaData messageMetadata = new XMessageMetaData { MessageType = message.MessageType }; switch (message.MessageType) { case MessageType.Text: message.TextCipher = EncryptTextToBytes(message.ThreadText, keyMaterial, roundsExponent); messageMetadata.SenderLocalMessageId = int.Parse(message.Id); break; case MessageType.Media: message.ImageCipher = this.ixdsCryptoService.DefaultEncrypt(message.ThreadMedia, keyMaterial); messageMetadata.SenderLocalMessageId = int.Parse(message.Id); break; case MessageType.TextAndMedia: case MessageType.File: message.TextCipher = EncryptTextToBytes(message.ThreadText, keyMaterial, roundsExponent); message.ImageCipher = this.ixdsCryptoService.DefaultEncrypt(message.ThreadMedia, keyMaterial); messageMetadata.SenderLocalMessageId = int.Parse(message.Id); break; case MessageType.DeliveryReceipt: case MessageType.ReadReceipt: messageMetadata.SenderLocalMessageId = int.Parse(message.SenderLocalMessageId); break; default: throw new Exception("Invalid MessageType"); } if (initialMetadataKeyMaterial != null) // initialMetadataKeyMaterial is only for resend requests { messageMetadata.SenderPublicKey = this.profileViewModel.PublicKey; message.MetaCipher = this.ixdsCryptoService.DefaultEncrypt(messageMetadata.SerializeCore(), initialMetadataKeyMaterial); } else { messageMetadata.SenderPublicKey = this.ixdsCryptoService.GetRandom(32).Result.X; message.MetaCipher = this.ixdsCryptoService.DefaultEncrypt(messageMetadata.SerializeCore(), keyMaterial); } if (!message.MessageType.IsReceipt()) { message.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(keyMaterial.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey()); } }
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); }
byte[] EncryptTextToBytes(string clearText, KeyMaterial64 keyMaterial64, RoundsExponent roundsExponent) { var encryptResponse = this.ixdsCryptoService.Encrypt(new Cleartext(clearText), keyMaterial64, roundsExponent, null); if (!encryptResponse.IsSuccess) { throw new InvalidOperationException(encryptResponse.Error); } var encodeResponse = this.ixdsCryptoService.BinaryEncodeXDSSec(encryptResponse.Result, null); if (!encodeResponse.IsSuccess) { throw new InvalidOperationException(encodeResponse.Error); } return(encodeResponse.Result); }
/// <summary> /// Creates, encrypts and saves the master random key during the onboarding process. /// </summary> /// <param name="newMasterPassphrase">The newly chosen master passphrase.</param> /// <param name="masterKey">The master random key material, including collected user random.</param> /// <param name="context"></param> /// <returns></returns> public async Task InitDeviceVaultKeyAsync(string newMasterPassphrase, byte[] masterKey, LongRunningOperationContext context) { KeyMaterial64 masterPassphraseKeyMaterial = CreateKeyMaterialFromPassphrase(newMasterPassphrase); byte[] deviceVaultKey = new byte[64]; Buffer.BlockCopy(masterKey, 64, deviceVaultKey, 0, 64); // use the second part, so that we do not use the wallet seed material. deviceVaultKey = this.xdsCryptoService.ComputeSHA512(deviceVaultKey); // hash it, because we have a new specific purpose and do not want key reuse. KeyMaterial64 deviceVaultKeyMaterial = new KeyMaterial64(deviceVaultKey); this.xdsCryptoService.SymmetricKeyRepository.SetDeviceVaultRandomKey(deviceVaultKeyMaterial); await Task.Run(async() => { await EncryptAndSaveDeviceVaultKeyAsync(deviceVaultKeyMaterial, masterPassphraseKeyMaterial, 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 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); }
// same in ChatWorker string DecryptBytesToText(byte[] cipherBytes, KeyMaterial64 keyMaterial64) { var decodeResponse = this.ixdsCryptoService.BinaryDecodeXDSSec(cipherBytes, null); if (!decodeResponse.IsSuccess) { throw new Exception(decodeResponse.Error); } var decrpytResponse = this.ixdsCryptoService.Decrypt(decodeResponse.Result, keyMaterial64, null); if (!decrpytResponse.IsSuccess) { throw new Exception(decrpytResponse.Error); } Cleartext cleartext = decrpytResponse.Result; return(cleartext.Text); }
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); }
public Response <CipherV2> BinaryEncrypt(Clearbytes clearBytes, KeyMaterial64 keyMaterial64, RoundsExponent roundsExponent, LongRunningOperationContext context) { var response = new Response <CipherV2>(); try { Guard.NotNull(new object[] { clearBytes, keyMaterial64, roundsExponent }); EnsurePlatform(); Compressed compressed = this._internal.CompressBytes(clearBytes); var cipherV2 = EncryptCommon(keyMaterial64, roundsExponent, context, compressed); response.Result = cipherV2; response.SetSuccess(); } catch (Exception e) { response.SetError(e); } return(response); }
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(); } }
void DecryptToCache(Message message, KeyMaterial64 keyMaterial64) { switch (message.MessageType) { case MessageType.Text: case MessageType.File: message.ThreadText = DecryptBytesToText(message.TextCipher, keyMaterial64); break; case MessageType.Media: message.ThreadMedia = this.ixdsCryptoService.DefaultDecrypt(message.ImageCipher, keyMaterial64); break; case MessageType.TextAndMedia: message.ThreadText = DecryptBytesToText(message.TextCipher, keyMaterial64); message.ThreadMedia = this.ixdsCryptoService.DefaultDecrypt(message.ImageCipher, keyMaterial64); break; default: throw new Exception("Invalid MessageType."); } }
/// <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); }
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 async Task <Response> DecryptCipherTextInVisibleBubble(Message message) { var response = new Response(); try { if (message.ThreadText != null && message.MessageType == MessageType.Text) // TODO: do this properly ....nothing to do, already decrypted { response.SetSuccess(); return(response); } KeyMaterial64 decryptionkey; if (message.LocalMessageState == LocalMessageState.JustReceived) // this is an incoming message { GetE2EDecryptionKeyResult getE2EDecryptionKeyResult = await this.e2ERatchet.GetEndToEndDecryptionKeyAsync(message.SenderId, message.DynamicPublicKey, message.PrivateKeyHint); if (getE2EDecryptionKeyResult.E2EDecryptionKeyType != E2EDecryptionKeyType.UnavailableDynamicPrivateKey) { decryptionkey = getE2EDecryptionKeyResult.E2EDecryptionKeyMaterial; } else { message.LocalMessageState = LocalMessageState.RatchetMismatchError; // When adding a contact and sending the first message to him, the contact can immediately read the message, // because the message is encrypted with the static public key of that contact. // But when one side deletes the contact, and the contact sends a message again, that's not possible, // because the sender dosn't know he would need to use only the static public key. // Instead, he's also use the last dynamic key, expecting the other side has matching material. // Then, we land here. A recovery would require that we ask the other side to resend the message. // Instead, just a delivery receipt is sent automatically, which repairs the ratchet. // The problem is, the other side does not know message.LocalMessageState = LocalMessageState.RatchetMismatchError; // create anothe enum member for this case? await this.repo.UpdateMessage(message); response.SetError(nameof(LocalMessageState.RatchetMismatchError)); return(response); } await Task.Run(() => DecryptToCache(message, decryptionkey)); await this.e2ERatchet.SaveIncomingDynamicPublicKeyOnSuccessfulDecryptionAsync(message.SenderId, message.DynamicPublicKey, message.DynamicPublicKeyId); message.EncryptedE2EEncryptionKey = this.ixdsCryptoService.DefaultEncrypt(decryptionkey.GetBytes(), this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey()); message.LocalMessageState = LocalMessageState.Integrated; Debug.Assert(message.MessageType != MessageType.ReadReceipt && message.MessageType != MessageType.DeliveryReceipt); await this.repo.UpdateMessage(message); await this.ChatWorker.SendReceipt(message, MessageType.ReadReceipt); } else // this is a message we have stored locally { if (message.EncryptedE2EEncryptionKey == null) { message.LocalMessageState = LocalMessageState.LocalDecryptionError; } else { decryptionkey = new KeyMaterial64(this.ixdsCryptoService.DefaultDecrypt(message.EncryptedE2EEncryptionKey, this.ixdsCryptoService.SymmetricKeyRepository.GetMasterRandomKey())); await Task.Run(() => DecryptToCache(message, decryptionkey)); } // should we check that a read receipt has really been sent and retry till we are sure? } response.SetSuccess(); } catch (Exception e) { response.SetError(e.Message); this.logger.LogError(e.Message); } return(response); }