private static void DecryptFile(string filePath, int parametersLength, byte[] macBackup, byte[] encryptionKey) { try { string decryptedFilePath = Regex.Replace(filePath, Constants.EncryptedExtension, string.Empty); int headersLength = Constants.SaltLength + parametersLength; using (var plaintext = new FileStream(decryptedFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, Constants.FileBufferSize, FileOptions.SequentialScan)) using (var ciphertext = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, Constants.FileBufferSize, FileOptions.SequentialScan)) { // Skip the header bytes ciphertext.Seek(headersLength, SeekOrigin.Begin); byte[] fileBytes = FileHandling.GetBufferSize(ciphertext.Length); // Generate a counter starting at 0 byte[] counter = Generate.Counter(); int bytesRead; MemoryEncryption.DecryptByteArray(ref encryptionKey); while ((bytesRead = ciphertext.Read(fileBytes, 0, fileBytes.Length)) > 0) { byte[] decryptedBytes = StreamEncryption.DecryptXChaCha20(fileBytes, counter, encryptionKey); plaintext.Write(decryptedBytes, 0, bytesRead); counter = Sodium.Utilities.Increment(counter); } Utilities.ZeroArray(encryptionKey); } CompleteDecryption(filePath, decryptedFilePath); } catch (Exception ex) when(ExceptionFilters.FileEncryptionExceptions(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.High); DisplayMessage.Error(filePath, ex.GetType().Name, "Unable to decrypt the file."); Utilities.ZeroArray(encryptionKey); RestoreMAC(filePath, macBackup); } }
private static void UsingPassword(string inputFilePath, byte[] passwordBytes) { try { bool fileIsDirectory = FileHandling.IsDirectory(inputFilePath); if (fileIsDirectory) { DirectoryDecryption.UsingPassword(inputFilePath, passwordBytes); return; } byte[] salt = FileHeaders.ReadSalt(inputFilePath); byte[] keyEncryptionKey = Argon2.DeriveKey(passwordBytes, salt); string outputFilePath = GetOutputFilePath(inputFilePath); DecryptFile.Initialize(inputFilePath, outputFilePath, keyEncryptionKey); Utilities.ZeroArray(keyEncryptionKey); DecryptionSuccessful(inputFilePath, outputFilePath); } catch (Exception ex) when(ExceptionFilters.FileAccess(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.Error); if (ex is ArgumentException || ex is ArgumentOutOfRangeException) { DisplayMessage.FilePathMessage(inputFilePath, ex.Message); return; } DisplayMessage.FilePathException(inputFilePath, ex.GetType().Name, "Unable to decrypt the file."); } }
private static void EncryptFile(string filePath, string encryptedFilePath, byte[] salt, byte[] encryptionKey, byte[] macKey) { try { using (var ciphertext = new FileStream(encryptedFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, Constants.FileBufferSize, FileOptions.SequentialScan)) using (var plaintext = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, Constants.FileBufferSize, FileOptions.SequentialScan)) { WriteFileHeaders.WriteHeaders(ciphertext, salt); byte[] fileBytes = FileHandling.GetBufferSize(plaintext.Length); // Generate a counter starting at 0 byte[] counter = Generate.Counter(); int bytesRead; MemoryEncryption.DecryptByteArray(ref encryptionKey); while ((bytesRead = plaintext.Read(fileBytes, 0, fileBytes.Length)) > 0) { byte[] encryptedBytes = StreamEncryption.EncryptXChaCha20(fileBytes, counter, encryptionKey); ciphertext.Write(encryptedBytes, 0, bytesRead); counter = Sodium.Utilities.Increment(counter); } } Utilities.ZeroArray(encryptionKey); CompleteEncryption(filePath, encryptedFilePath, macKey); } catch (Exception ex) when(ExceptionFilters.FileEncryptionExceptions(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.High); DisplayMessage.Error(filePath, ex.GetType().Name, "Unable to encrypt the file."); FileHandling.DeleteFile(encryptedFilePath); Utilities.ZeroArray(encryptionKey); Utilities.ZeroArray(macKey); } }
private static void UsingPassword(string inputFilePath, byte[] passwordBytes) { try { bool fileIsDirectory = FileHandling.IsDirectory(inputFilePath); if (fileIsDirectory) { DirectoryEncryption.UsingPassword(inputFilePath, passwordBytes); return; } // Derive a unique KEK per file byte[] salt = Generate.Salt(); byte[] keyEncryptionKey = Argon2.DeriveKey(passwordBytes, salt); // Fill the ephemeral public key header with random key (since not in use) byte[] randomEphemeralPublicKeyHeader = Generate.EphemeralPublicKeyHeader(); string outputFilePath = GetOutputFilePath(inputFilePath); EncryptFile.Initialize(inputFilePath, outputFilePath, randomEphemeralPublicKeyHeader, salt, keyEncryptionKey); Utilities.ZeroArray(keyEncryptionKey); EncryptionSuccessful(inputFilePath, outputFilePath); } catch (Exception ex) when(ExceptionFilters.FileAccess(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.Error); DisplayMessage.FilePathException(inputFilePath, ex.GetType().Name, "Unable to encrypt the file."); } }
public static void Initialize(string inputFilePath, string outputFilePath, byte[] keyEncryptionKey) { byte[] dataEncryptionKey = new byte[Constants.EncryptionKeyLength]; try { byte[] encryptedHeader = FileHeaders.ReadEncryptedHeader(inputFilePath); byte[] nonce = FileHeaders.ReadNonce(inputFilePath); byte[] header = DecryptFileHeader(inputFilePath, encryptedHeader, nonce, keyEncryptionKey); if (header == null) { throw new ArgumentException("Incorrect password/keyfile or this file has been tampered with."); } ChunkHandling.ValidateKeyCommitmentBlock(header); int lastChunkLength = FileHeaders.GetLastChunkLength(header); int fileNameLength = FileHeaders.GetFileNameLength(header); dataEncryptionKey = FileHeaders.GetDataEncryptionKey(header); Utilities.ZeroArray(header); using (var inputFile = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, Constants.FileStreamBufferSize, FileOptions.SequentialScan)) using (var outputFile = new FileStream(outputFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, Constants.FileStreamBufferSize, FileOptions.SequentialScan)) { nonce = Sodium.Utilities.Increment(nonce); byte[] additionalData = ChunkHandling.GetPreviousPoly1305Tag(encryptedHeader); Decrypt(inputFile, outputFile, nonce, dataEncryptionKey, additionalData, lastChunkLength); } Finalize(inputFilePath, outputFilePath, fileNameLength); } catch (Exception ex) when(ExceptionFilters.Cryptography(ex)) { Utilities.ZeroArray(dataEncryptionKey); throw; } }
public static void SignEachFile(byte[] privateKey, string comment, bool preHash, string[] filePaths) { privateKey = PrivateKey.Decrypt(privateKey); if (privateKey == null) { return; } if (string.IsNullOrEmpty(comment)) { comment = _defaultComment; } foreach (string filePath in filePaths) { try { DigitalSignatures.SignFile(filePath, comment, preHash, privateKey); DisplayMessage.FilePathMessage(filePath, "File signed successfully."); } catch (Exception ex) when(ExceptionFilters.Cryptography(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.Error); DisplayMessage.FilePathException(filePath, ex.GetType().Name, "Unable to create signature."); } } Utilities.ZeroArray(privateKey); }
public static char[] ConvertUserInput(bool encryption, char[] base64Key, char[] password) { try { NullChecks.CharArray(base64Key); NullChecks.CharArray(password); char[] message; byte[] key = Convert.FromBase64CharArray(base64Key, 0, base64Key.Length); if (encryption == true) { byte[] plaintext = Encoding.UTF8.GetBytes(password); message = EncryptPassword(plaintext, key); Utilities.ZeroArray(plaintext); } else { byte[] ciphertext = Convert.FromBase64CharArray(password, 0, password.Length); message = DecryptPassword(ciphertext, key); Utilities.ZeroArray(ciphertext); } Utilities.ZeroArray(key); return(message); } catch (Exception ex) when(ex is FormatException || ex is EncoderFallbackException) { DisplayMessage.Error(ex.GetType().Name, "Invalid key or password format."); return(Array.Empty <char>()); } }
private static void UsingPublicKey(string inputFilePath, byte[] sharedSecret, byte[] recipientPublicKey) { try { bool fileIsDirectory = FileHandling.IsDirectory(inputFilePath); if (fileIsDirectory) { DirectoryEncryption.UsingPublicKey(inputFilePath, sharedSecret, recipientPublicKey); return; } // Derive a unique KEK per file (byte[] ephemeralSharedSecret, byte[] ephemeralPublicKey) = KeyExchange.GetEphemeralSharedSecret(recipientPublicKey); byte[] salt = Generate.Salt(); byte[] keyEncryptionKey = Generate.KeyEncryptionKey(sharedSecret, ephemeralSharedSecret, salt); string outputFilePath = GetOutputFilePath(inputFilePath); EncryptFile.Initialize(inputFilePath, outputFilePath, ephemeralPublicKey, salt, keyEncryptionKey); Utilities.ZeroArray(keyEncryptionKey); EncryptionSuccessful(inputFilePath, outputFilePath); } catch (Exception ex) when(ExceptionFilters.FileAccess(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.Error); DisplayMessage.FilePathException(inputFilePath, ex.GetType().Name, "Unable to encrypt the file."); } }
public static void GenerateKeyfile(string keyfilePath) { byte[] keyfileBytes = Generate.Keyfile(); File.WriteAllBytes(keyfilePath, keyfileBytes); File.SetAttributes(keyfilePath, FileAttributes.ReadOnly); Utilities.ZeroArray(keyfileBytes); }
public static void RecoverPublicKey(string privateKeyPath) { bool validUserInput = FilePathValidation.RecoverPublicKey(privateKeyPath); if (!validUserInput) { return; } byte[] privateKey = AsymmetricKeyValidation.GetPrivateKeyFromFile(privateKeyPath); if (privateKey == null) { return; } privateKey = PrivateKey.Decrypt(privateKey); if (privateKey == null) { return; } byte[] publicKey = privateKey.Length switch { Constants.EncryptionKeyLength => AsymmetricKeys.GetCurve25519PublicKey(privateKey), _ => AsymmetricKeys.GetEd25519PublicKey(privateKey), }; Utilities.ZeroArray(privateKey); Console.WriteLine($"Public key: {Convert.ToBase64String(publicKey)}"); }
private static void UsingPrivateKey(string inputFilePath, byte[] sharedSecret, byte[] recipientPrivateKey) { try { bool fileIsDirectory = FileHandling.IsDirectory(inputFilePath); if (fileIsDirectory) { DirectoryDecryption.UsingPrivateKey(inputFilePath, sharedSecret, recipientPrivateKey); return; } byte[] ephemeralPublicKey = FileHeaders.ReadEphemeralPublicKey(inputFilePath); byte[] ephemeralSharedSecret = KeyExchange.GetSharedSecret(recipientPrivateKey, ephemeralPublicKey); byte[] salt = FileHeaders.ReadSalt(inputFilePath); byte[] keyEncryptionKey = Generate.KeyEncryptionKey(sharedSecret, ephemeralSharedSecret, salt); string outputFilePath = GetOutputFilePath(inputFilePath); DecryptFile.Initialize(inputFilePath, outputFilePath, keyEncryptionKey); Utilities.ZeroArray(keyEncryptionKey); DecryptionSuccessful(inputFilePath, outputFilePath); } catch (Exception ex) when(ExceptionFilters.FileAccess(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.Error); if (ex is ArgumentException || ex is ArgumentOutOfRangeException) { DisplayMessage.FilePathMessage(inputFilePath, ex.Message); return; } DisplayMessage.FilePathException(inputFilePath, ex.GetType().Name, "Unable to decrypt the file."); } }
public static void CallEncryption(bool encryption, char[] password, string keyfilePath, string[] filePaths) { byte[] passwordBytes = FileEncryption.GetPasswordBytes(password); Utilities.ZeroArray(password); FileEncryption.StartEncryption(encryption, filePaths, passwordBytes, keyfilePath); EncryptionCompleted(encryption); }
public static byte[] KeyEncryptionKey(byte[] sharedSecret, byte[] ephemeralSharedSecret, byte[] salt) { byte[] inputKeyingMaterial = Utilities.ConcatArrays(sharedSecret, ephemeralSharedSecret); byte[] keyEncryptionKey = Blake2.KeyDerivation(inputKeyingMaterial, salt, Constants.EncryptionKeyLength); Utilities.ZeroArray(ephemeralSharedSecret); Utilities.ZeroArray(inputKeyingMaterial); return(keyEncryptionKey); }
private static void CompleteEncryption(string filePath, string encryptedFilePath, byte[] macKey) { // Calculate and append MAC bool fileSigned = FileAuthentication.SignFile(encryptedFilePath, macKey); Utilities.ZeroArray(macKey); if (fileSigned == true && Globals.OverwriteFiles == true) { FileHandling.OverwriteFile(filePath, encryptedFilePath); } FileHandling.MakeFileReadOnly(encryptedFilePath); GetEncryptionResult(filePath, fileSigned); }
public static void GenerateRandomPassword(int length) { if (length >= 8 && length <= 128) { char[] password = PasswordGenerator.GenerateRandomPassword(length, true, true, true, true); Console.WriteLine(password); Utilities.ZeroArray(password); } else { Console.WriteLine("Error: Invalid length. Randomly generated passwords must be between 8-128 characters."); } }
public static void GenerateRandomPassphrase(int length) { if (length >= 4 && length <= 20) { char[] passphrase = PasswordGenerator.GenerateRandomPassphrase(length, true, true); Console.WriteLine(passphrase); Utilities.ZeroArray(passphrase); } else { Console.WriteLine("Error: Invalid number of words. Randomly generated passphrases must be between 4-20 words."); } }
public static byte[] Encrypt(byte[] passwordBytes, byte[] privateKey, byte[] keyAlgorithm) { byte[] salt = Generate.Salt(); byte[] key = Argon2.DeriveKey(passwordBytes, salt); Utilities.ZeroArray(passwordBytes); byte[] nonce = Generate.Nonce(); byte[] additionalData = Utilities.ConcatArrays(keyAlgorithm, Constants.PrivateKeyVersion); byte[] keyCommitmentBlock = ChunkHandling.GetKeyCommitmentBlock(); privateKey = Utilities.ConcatArrays(keyCommitmentBlock, privateKey); byte[] encryptedPrivateKey = SecretAeadXChaCha20Poly1305.Encrypt(privateKey, nonce, key, additionalData); Utilities.ZeroArray(privateKey); Utilities.ZeroArray(key); return(Utilities.ConcatArrays(additionalData, salt, nonce, encryptedPrivateKey)); }
public static void StartEncryption(bool encryption, string[] filePaths, byte[] passwordBytes, string keyfilePath) { // Don't use keyfile bytes when only a keyfile is selected if (passwordBytes != null) { passwordBytes = GetKeyfileBytes(passwordBytes, keyfilePath); } else { passwordBytes = KeyfileAsPassword(keyfilePath); } GetFilePaths(encryption, filePaths, passwordBytes); Utilities.ZeroArray(passwordBytes); }
private static void DecryptEachFileWithPassword(string[] filePaths, byte[] keyEncryptionKey) { foreach (string inputFilePath in filePaths) { bool validFilePath = FilePathValidation.FileDecryption(inputFilePath); if (!validFilePath) { --Globals.TotalCount; continue; } DecryptInputFile(inputFilePath, keyEncryptionKey); } Utilities.ZeroArray(keyEncryptionKey); }
private static void Encrypt(FileStream inputFile, FileStream outputFile, byte[] nonce, byte[] dataEncryptionKey, byte[] additionalData) { const int offset = 0; byte[] plaintext = new byte[Constants.FileChunkSize]; while (inputFile.Read(plaintext, offset, plaintext.Length) > 0) { byte[] plaintextChunk = ChunkHandling.PrependKeyCommitmentBlock(plaintext); byte[] ciphertextChunk = SecretAeadXChaCha20Poly1305.Encrypt(plaintextChunk, nonce, dataEncryptionKey, additionalData); nonce = Sodium.Utilities.Increment(nonce); additionalData = ChunkHandling.GetPreviousPoly1305Tag(ciphertextChunk); outputFile.Write(ciphertextChunk, offset, ciphertextChunk.Length); } Utilities.ZeroArray(dataEncryptionKey); }
private static void CheckForTampering(string filePath, int parametersLength, byte[] encryptionKey, byte[] macKey) { bool fileTampered = FileAuthentication.AuthenticateFile(filePath, macKey, out byte[] macBackup); Utilities.ZeroArray(macKey); if (fileTampered == false) { DecryptFile(filePath, parametersLength, macBackup, encryptionKey); } else { Console.WriteLine($"{Path.GetFileName(filePath)}: Incorrect password/keyfile, wrong encryption algorithm, or this file has been tampered with."); Utilities.ZeroArray(encryptionKey); } }
private static byte[] Decrypt(byte[] passwordBytes, byte[] privateKey) { byte[] keyAlgorithm = GetKeyAlgorithm(privateKey); byte[] keyVersion = GetKeyVersion(privateKey); byte[] salt = GetSalt(privateKey); byte[] nonce = GetNonce(privateKey); byte[] additionalData = Utilities.ConcatArrays(keyAlgorithm, keyVersion); byte[] encryptedPrivateKey = GetEncryptedPrivateKey(privateKey); byte[] key = Argon2.DeriveKey(passwordBytes, salt); Utilities.ZeroArray(passwordBytes); byte[] decryptedPrivateKey = SecretAeadXChaCha20Poly1305.Decrypt(encryptedPrivateKey, nonce, key, additionalData); Utilities.ZeroArray(key); ChunkHandling.ValidateKeyCommitmentBlock(decryptedPrivateKey); return(ChunkHandling.RemoveKeyCommitmentBlock(decryptedPrivateKey)); }
public static void GenerateKeyfile(string filePath) { try { byte[] keyfileBytes = SodiumCore.GetRandomBytes(Constants.MACKeySize); File.WriteAllBytes(filePath, keyfileBytes); File.SetAttributes(filePath, FileAttributes.ReadOnly); Utilities.ZeroArray(keyfileBytes); Console.WriteLine("Keyfile successfully generated."); } catch (Exception ex) when(ExceptionFilters.FileAccessExceptions(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.Low); DisplayMessage.Error(ex.GetType().Name, "Keyfile generation failed."); } }
public static void EncryptEachFileWithPassword(string[] filePaths, byte[] passwordBytes) { Globals.TotalCount = filePaths.Length; foreach (string inputFilePath in filePaths) { bool validFilePath = FilePathValidation.FileEncryption(inputFilePath); if (!validFilePath) { --Globals.TotalCount; continue; } UsingPassword(inputFilePath, passwordBytes); } Utilities.ZeroArray(passwordBytes); DisplayMessage.SuccessfullyEncrypted(); }
private static byte[] GetKeyfileBytes(byte[] passwordBytes, string keyfilePath) { if (!string.IsNullOrEmpty(keyfilePath)) { byte[] keyfileBytes = Keyfiles.ReadKeyfile(keyfilePath); if (keyfileBytes != null) { MemoryEncryption.DecryptByteArray(ref passwordBytes); // Combine password and keyfile bytes passwordBytes = HashingAlgorithms.Blake2(passwordBytes, keyfileBytes); MemoryEncryption.EncryptByteArray(ref passwordBytes); Utilities.ZeroArray(keyfileBytes); } } return(passwordBytes); }
private static void EncryptEachFileWithPassword(string[] filePaths, byte[] salt, byte[] keyEncryptionKey) { foreach (string inputFilePath in filePaths) { bool validFilePath = FilePathValidation.FileEncryption(inputFilePath); if (!validFilePath) { --Globals.TotalCount; continue; } // Fill the ephemeral public key header with random bytes (since not in use) byte[] randomEphemeralPublicKeyHeader = Generate.EphemeralPublicKeyHeader(); string outputFilePath = FileEncryption.GetOutputFilePath(inputFilePath); EncryptInputFile(inputFilePath, outputFilePath, randomEphemeralPublicKeyHeader, salt, keyEncryptionKey); } Utilities.ZeroArray(keyEncryptionKey); }
private static void DecryptEachFileWithPrivateKey(string[] filePaths, byte[] privateKey) { foreach (string inputFilePath in filePaths) { bool validFilePath = FilePathValidation.FileDecryption(inputFilePath); if (!validFilePath) { --Globals.TotalCount; continue; } byte[] ephemeralPublicKey = FileHeaders.ReadEphemeralPublicKey(inputFilePath); byte[] ephemeralSharedSecret = KeyExchange.GetSharedSecret(privateKey, ephemeralPublicKey); byte[] salt = FileHeaders.ReadSalt(inputFilePath); byte[] keyEncryptionKey = Generate.KeyEncryptionKey(ephemeralSharedSecret, salt); DecryptInputFile(inputFilePath, keyEncryptionKey); Utilities.ZeroArray(keyEncryptionKey); } }
private static void RestoreMAC(string filePath, byte[] macBackup) { bool restored = FileAuthentication.AppendHash(filePath, macBackup); if (restored == false) { try { File.WriteAllBytes($"{filePath}.backup", macBackup); } catch (Exception ex) when(ExceptionFilters.FileAccessExceptions(ex)) { Logging.LogException(ex.ToString(), Logging.Severity.High); DisplayMessage.Error(filePath, ex.GetType().Name, "Failed to backup the MAC. This data is required for decryption."); } } Utilities.ZeroArray(macBackup); }
private static void EncryptEachFileWithPrivateKey(string[] filePaths, byte[] privateKey) { foreach (string inputFilePath in filePaths) { bool validFilePath = FilePathValidation.FileEncryption(inputFilePath); if (!validFilePath) { --Globals.TotalCount; continue; } // Derive a unique KEK per file (byte[] ephemeralSharedSecret, byte[] ephemeralPublicKey) = KeyExchange.GetPrivateKeySharedSecret(privateKey); byte[] salt = Generate.Salt(); byte[] keyEncryptionKey = Generate.KeyEncryptionKey(ephemeralSharedSecret, salt); string outputFilePath = FileEncryption.GetOutputFilePath(inputFilePath); EncryptInputFile(inputFilePath, outputFilePath, ephemeralPublicKey, salt, keyEncryptionKey); Utilities.ZeroArray(keyEncryptionKey); } }
public static (byte[] encryptionKey, byte[] macKey) DeriveKeys(byte[] passwordBytes, byte[] salt, int iterations, int memorySize) { var argon2id = PasswordHash.ArgonAlgorithm.Argon_2ID13; MemoryEncryption.DecryptByteArray(ref passwordBytes); // Derive a 96 byte key byte[] derivedKey = PasswordHash.ArgonHashBinary(passwordBytes, salt, iterations, memorySize, Constants.Argon2KeySize, argon2id); // 256-bit encryption key byte[] encryptionKey = new byte[Constants.EncryptionKeySize]; Array.Copy(derivedKey, encryptionKey, encryptionKey.Length); // 512-bit MAC key byte[] macKey = new byte[Constants.MACKeySize]; Array.Copy(derivedKey, encryptionKey.Length, macKey, 0, macKey.Length); Utilities.ZeroArray(derivedKey); MemoryEncryption.EncryptByteArray(ref passwordBytes); MemoryEncryption.EncryptByteArray(ref encryptionKey); MemoryEncryption.EncryptByteArray(ref macKey); return(encryptionKey, macKey); }