public void Initialize(DerivedBytesProvider derivedBytesProvider, Action <Dictionary <string, byte[]> > afterHeaderRead = null) { if (m_IsDisposed) { throw new ObjectDisposedException(nameof(HelixFileDecryptor)); } if (m_Initialized) { throw new InvalidOperationException("Decryptor has already been initialized"); } if (derivedBytesProvider == null) { throw new ArgumentNullException(nameof(derivedBytesProvider)); } try { decryptor = new MultiBlockDecryptor(streamIn); decryptor.Initialize(derivedBytesProvider, afterHeaderRead); m_Initialized = true; } catch (FileDesignatorException ex) { throw new UnsupportedFileTypeException("Unsupported file type", ex); } catch (HMACAuthenticationException ex) { throw new InvalidPasswordException("Invalid password", ex); } }
/// <summary> /// Returns the header for an encrypted file /// </summary> public static FileEntry DecryptHeader(string encrFile, DerivedBytesProvider derivedBytesProvider) { using var inputStream = File.OpenRead(encrFile); using var decryptor = new HelixFileDecryptor(inputStream); try { decryptor.Initialize(derivedBytesProvider); FileEntry header = decryptor.ReadHeader(); if (!header.IsValid(out HeaderCorruptionException ex)) { throw ex; } return(header); } catch (FileCorruptionException ex) { throw new FileCorruptionException($"Failed to decrypt {encrFile}, {ex.Message}", ex); } catch (AuthenticatedEncryptionException ex) { throw new FileCorruptionException($"Failed to decrypt {encrFile}, {ex.Message}", ex); } }
public DirectoryPair(string decrDirectory, string encrDirectory, DerivedBytesProvider derivedBytesProvider, bool whatIf) { this.DecrDirectory = new FSDirectory(new DirectoryInfo(decrDirectory).FullName, whatIf); this.EncrDirectory = new FSDirectory(new DirectoryInfo(encrDirectory).FullName, whatIf); this.DerivedBytesProvider = derivedBytesProvider; this.WhatIf = whatIf; }
public static DirectoryHeader Load(string filePath, DerivedBytesProvider derivedBytesProvider) { DirectoryHeader directoryHeader = new DirectoryHeader(); using Stream streamIn = File.OpenRead(filePath); using HelixFileDecryptor decryptor = new HelixFileDecryptor(streamIn); decryptor.Initialize(derivedBytesProvider); directoryHeader.FileVersion = decryptor.FileVersion; directoryHeader.Header = decryptor.ReadHeader(); directoryHeader.FileVersion = decryptor.FileVersion; var contentSerialized = decryptor.GetContentString(); //var contentDeserialized = JsonConvert.DeserializeAnonymousType(contentSerialized, new { EncryptedFileNameSalt = new byte[] { } }); JsonConvert.PopulateObject(contentSerialized, directoryHeader); if (!Regex.IsMatch(directoryHeader.DirectoryId, "^[0-9A-F]{32}$")) { throw new HelixException("Data curruption directory header, DirectoryId malformed"); } if (directoryHeader.FileNameKey == null || directoryHeader.FileNameKey.Length != 32) { throw new HelixException("Data curruption directory header, FileNameKey missing or insufficient length"); } return(directoryHeader); }
public void Initialize(DerivedBytesProvider derivedBytesProvider, Action <Dictionary <string, byte[]> > afterHeaderRead = null) { if (derivedBytesProvider == null) { throw new ArgumentNullException(nameof(derivedBytesProvider)); } //header //FileDesignator (8 bytes) + passwordSalt (32 bytes) + hmac salt (32 bytes) + iv (32 bytes) FileHeader header = new FileHeader(); StreamRead(streamIn, header.fileDesignator); StreamRead(streamIn, header.passwordSalt); StreamRead(streamIn, header.hmacSalt); StreamRead(streamIn, header.iv); StreamRead(streamIn, header.headerAuthnDisk); afterHeaderRead?.Invoke(header.ToDictionary()); FileVersion = HelixFileVersion.GetVersion(header.fileDesignator); if (FileVersion == null) { throw new FileDesignatorException("Invalid file format, file designator not correct"); } DerivedBytes = derivedBytesProvider.GetDerivedBytes(header.passwordSalt, FileVersion.DerivedBytesIterations); byte[] headerBytes = header.GetBytesToHash(); byte[] hmacFullKey = ByteBlock.ConcatenateBytes(header.hmacSalt, DerivedBytes.Key); hmacHash = new HMACSHA256(hmacFullKey); //Validate Header HMAC byte[] headerAuthnComputed = hmacHash.ComputeHash(headerBytes); if (!ByteBlock.Equals(header.headerAuthnDisk, headerAuthnComputed)) { throw new HMACAuthenticationException("Header HMAC Authentication Failed"); } hmacTransform = new HMACDecrypt(headerAuthnComputed, hmacHash); hmacStream = new CryptoStream2(streamIn, hmacTransform, CryptoStreamMode.Read); aesTransform = Aes.Create(); aesTransform.KeySize = 256; aesTransform.BlockSize = 128; aesTransform.Mode = CipherMode.CBC; aesTransform.Padding = PaddingMode.PKCS7; aesTransform.IV = header.iv; aesTransform.Key = DerivedBytes.Key; aesStream = new CryptoStream2(hmacStream, aesTransform.CreateDecryptor(), CryptoStreamMode.Read); gzipStream = new GZipStream(aesStream, CompressionMode.Decompress, true); initialized = true; }
public static int Inspect(InspectOptions options, ConsoleEx consoleEx = null) { if (options == null) { throw new ArgumentNullException(nameof(options)); } consoleEx ??= new ConsoleEx(); using (Stream fileIn = File.OpenRead(options.File)) using (HelixFileDecryptor decryptor = new HelixFileDecryptor(fileIn)) { consoleEx.WriteLine("== Outer Header =="); Dictionary <string, byte[]> header = null; decryptor.Initialize(DerivedBytesProvider.FromPassword(options.Password, options.KeyFile), (h) => header = h); consoleEx.WriteLine("Designator: " + BitConverter.ToString(header[nameof(FileHeader.fileDesignator)]).Replace("-", "") + " (" + new string(Encoding.ASCII.GetString(header[nameof(FileHeader.fileDesignator)]) .Select(c => Char.IsControl(c) ? '?' : c) .ToArray()) + ")"); consoleEx.WriteLine("Password Salt: " + BitConverter.ToString(header[nameof(FileHeader.passwordSalt)]).Replace("-", "")); consoleEx.WriteLine("HMAC Salt: " + BitConverter.ToString(header[nameof(FileHeader.hmacSalt)]).Replace("-", "")); consoleEx.WriteLine("IV: " + BitConverter.ToString(header[nameof(FileHeader.iv)]).Replace("-", "")); consoleEx.WriteLine("Authn (HMAC): " + BitConverter.ToString(header[nameof(FileHeader.headerAuthnDisk)]).Replace("-", "")); consoleEx.WriteLine(); consoleEx.WriteLine("== Inner Header =="); decryptor.ReadHeader( afterRawMetadata: (r) => consoleEx.WriteLine(JsonFormat(r))); consoleEx.WriteLine(); using Stream content = decryptor.GetContentStream(); ContentPreview(content, consoleEx, options.ContentFormat); consoleEx.WriteLine(); } //Reopen to calculate the checksum using (Stream fileIn = File.OpenRead(options.File)) using (HelixFileDecryptor decryptor = new HelixFileDecryptor(fileIn)) { decryptor.Initialize(DerivedBytesProvider.FromPassword(options.Password, options.KeyFile)); decryptor.ReadHeader(); using Stream content = decryptor.GetContentStream(); Checksum(content, consoleEx); return(0); } }
public HelixFileEncryptor(Stream streamOut, DerivedBytesProvider derivedBytesProvider, HelixFileVersion fileVersion = null) { if (streamOut == null) { throw new ArgumentNullException(nameof(streamOut)); } if (!streamOut.CanWrite) { throw new ArgumentException("streamOut must be writable", nameof(streamOut)); } if (derivedBytesProvider == null) { throw new ArgumentNullException(nameof(derivedBytesProvider)); } this.FileVersion = fileVersion ?? HelixFileVersion.Default; this.StreamOut = streamOut; this.DerivedBytes = derivedBytesProvider.GetDerivedBytes(FileVersion.DerivedBytesIterations); }
public void Save(string filePath, DerivedBytesProvider derivedBytesProvider, HelixFileVersion fileVersion = null) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException(nameof(filePath)); } if (derivedBytesProvider == null) { throw new ArgumentNullException(nameof(derivedBytesProvider)); } FileVersion = fileVersion ?? HelixFileVersion.Default; using Stream streamOut = File.Open(filePath, FileMode.Create, FileAccess.Write); using HelixFileEncryptor encryptor = new HelixFileEncryptor(streamOut, derivedBytesProvider, fileVersion); encryptor.WriteHeader(Header); var contentSerialized = JsonConvert.SerializeObject(this); encryptor.WriteContent(contentSerialized); }
public static string Decrypt(string encrPath, string decrPath, DerivedBytesProvider derivedBytesProvider, FileDecryptOptions options = null) { if (string.IsNullOrWhiteSpace(encrPath)) { throw new ArgumentNullException(nameof(encrPath)); } if (string.IsNullOrWhiteSpace(decrPath)) { throw new ArgumentNullException(nameof(decrPath)); } if (derivedBytesProvider == null) { throw new ArgumentNullException(nameof(derivedBytesProvider)); } options ??= new FileDecryptOptions(); using (FileStream inputStream = File.OpenRead(encrPath)) using (HelixFileDecryptor decryptor = new HelixFileDecryptor(inputStream)) { decryptor.Initialize(derivedBytesProvider); FileEntry header = decryptor.ReadHeader(); options?.AfterMetadataRead?.Invoke(header, options); if (!header.IsValid(out HeaderCorruptionException ex)) { throw ex; } string decrFullFileName = decrPath; string decrStagedFileName = decrPath + HelixConsts.StagedHxExtention; string decrBackupFileName = decrPath + HelixConsts.BackupExtention; if (header.EntryType == FileEntryType.File) { if (!string.IsNullOrEmpty(Path.GetDirectoryName(decrStagedFileName))) { Directory.CreateDirectory(Path.GetDirectoryName(decrStagedFileName)); } using (var contentStream = decryptor.GetContentStream()) using (var stagedFile = File.OpenWrite(decrStagedFileName)) { contentStream.CopyTo(stagedFile); } File.SetLastWriteTimeUtc(decrStagedFileName, header.LastWriteTimeUtc); if (File.Exists(decrFullFileName)) { File.Move(decrFullFileName, decrBackupFileName); } else if (Directory.Exists(decrFullFileName)) { if (Directory.GetFiles(decrFullFileName).Length > 0) { throw new IOException($"Unable to process entry, directory is not empty ({decrFullFileName})"); } Directory.Move(decrFullFileName, decrBackupFileName); } File.Move(decrStagedFileName, decrFullFileName); if (File.Exists(decrBackupFileName)) { File.SetAttributes(decrBackupFileName, FileAttributes.Normal); //incase it was read only File.Delete(decrBackupFileName); } else if (Directory.Exists(decrBackupFileName)) { Directory.Delete(decrBackupFileName); } } else if (header.EntryType == FileEntryType.Directory) { if (File.Exists(decrFullFileName)) { File.Move(decrFullFileName, decrBackupFileName); } if (Directory.Exists(decrFullFileName)) { //If there is a case difference need to delete the directory if (Path.GetFileName(decrFullFileName) != Path.GetFileName(new DirectoryInfo(decrFullFileName).Name)) { if (Directory.GetFiles(decrFullFileName).Length > 0) { throw new IOException($"Unable to process entry, directory is not empty ({decrFullFileName})"); } Directory.Move(decrFullFileName, decrBackupFileName); } } Directory.CreateDirectory(decrFullFileName); } else //purge or delete { if (File.Exists(decrFullFileName)) { File.Move(decrFullFileName, decrBackupFileName); } else if (Directory.Exists(decrFullFileName)) { if (Directory.GetFiles(decrFullFileName).Length > 0) { throw new IOException($"Unable to process entry, directory is not empty ({decrFullFileName})"); } Directory.Move(decrFullFileName, decrBackupFileName); } } if (File.Exists(decrBackupFileName)) { File.Delete(decrBackupFileName); } else if (Directory.Exists(decrBackupFileName)) { Directory.Delete(decrBackupFileName); } } return(decrPath); }
public static string Decrypt(string encrPath, string decrPath, string password, FileDecryptOptions options = null) { options = (options ?? new FileDecryptOptions()).Clone(); return(Decrypt(encrPath, decrPath, DerivedBytesProvider.FromPassword(password), options)); }
public static void Encrypt(string decrFile, string encrFile, string password, FileEncryptOptions options = null) { Encrypt(decrFile, encrFile, DerivedBytesProvider.FromPassword(password), options); }
/// <summary> /// Encrypts a file and returns the name of the encrypted file /// </summary> /// <param name="decrFilePath">The file name and path of the file to be encrypted</param> /// <param name="encrFilePath">The file name and path for th encrypted file to be saved</param> public static void Encrypt(string decrFilePath, string encrFilePath, DerivedBytesProvider derivedBytesProvider, FileEncryptOptions options = null) { if (string.IsNullOrWhiteSpace(decrFilePath)) { throw new ArgumentNullException(nameof(decrFilePath)); } if (string.IsNullOrEmpty(encrFilePath)) { throw new ArgumentNullException(nameof(encrFilePath)); } if (derivedBytesProvider == null) { throw new ArgumentNullException(nameof(derivedBytesProvider)); } options ??= new FileEncryptOptions(); var encrStagedFileName = encrFilePath + HelixConsts.StagedHxExtention; var encrBackupFileName = encrFilePath + HelixConsts.BackupExtention; if (!string.IsNullOrEmpty(Path.GetDirectoryName(encrStagedFileName))) { Directory.CreateDirectory(Path.GetDirectoryName(encrStagedFileName)); } FileEntry header = FileEntry.FromFile(decrFilePath, Path.GetDirectoryName(decrFilePath)); if (!string.IsNullOrWhiteSpace(options.StoredFileName)) { header.FileName = options.StoredFileName; } using (Stream streamIn = (header.EntryType != FileEntryType.File) ? (Stream)(new MemoryStream()) : File.Open(decrFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var streamOut = File.Open(encrStagedFileName, FileMode.CreateNew, FileAccess.Write)) using (var encryptor = new HelixFileEncryptor(streamOut, derivedBytesProvider, options.FileVersion)) { options.Log?.Invoke("Writing: Header"); options.BeforeWriteHeader?.Invoke(header); encryptor.WriteHeader(header); options.Log?.Invoke("Writing: Content"); encryptor.WriteContent(streamIn, streamIn.Length); } if (File.Exists(encrFilePath)) { if (File.Exists(encrBackupFileName)) { options.Log?.Invoke("Destaging: Removing incomplete backup"); File.Delete(encrBackupFileName); } options.Log?.Invoke("Destaging: Moving staged to normal"); if (File.Exists(encrFilePath)) { File.Move(encrFilePath, encrBackupFileName); } File.Move(encrStagedFileName, encrFilePath); options.Log?.Invoke("Destaging: Removing backup"); File.Delete(encrBackupFileName); } else { options.Log?.Invoke("Destaging: Moving staged to normal"); File.Move(encrStagedFileName, encrFilePath); } }
public void Initialize(DerivedBytesProvider derivedBytesProvider, Action <Dictionary <string, byte[]> > afterHeaderRead = null) { Decryptor.Initialize(derivedBytesProvider, afterHeaderRead); }
public static DirectoryPair Open(string decrDirectoryPath, string encrDirectoryPath, DerivedBytesProvider derivedBytesProvider, bool initialize = false, HelixFileVersion fileVersion = null) { if (derivedBytesProvider == null) { throw new ArgumentNullException(nameof(derivedBytesProvider)); } DirectoryPair pair = new DirectoryPair(decrDirectoryPath, encrDirectoryPath, derivedBytesProvider, false); if (initialize && pair.InitializeFullNeeded()) { pair.InitializeFull(null, fileVersion); } pair.OpenEncr(null); if (initialize && pair.InitializeDecrNeeded()) { pair.InitializeDecr(null); } pair.OpenDecr(null); return(pair); }
public HelixFileEncryptor(Stream streamOut, string password) : this(streamOut, DerivedBytesProvider.FromPassword(password)) { }
public static SyncSummary Sync(SyncOptions options, ConsoleEx consoleEx = null, HelixFileVersion fileVersion = null) { var sum = new SyncSummary(); consoleEx ??= new ConsoleEx(); consoleEx.Verbosity = options.Verbosity; consoleEx.WriteLine("Sync"); if (options.WhatIf) { consoleEx.WriteLine("..Options: WhatIf"); } consoleEx.WriteLine($"..DecrDir: {options.DecrDirectory}"); consoleEx.WriteLine($"..EncrDir: {options.EncrDirectory}"); //consoleEx.WriteLine($"..Direction: {options.Direction}"); consoleEx.WriteLine($"..Verbosity: {options.Verbosity}"); consoleEx.WriteLine(); if (options.WhatIf) { consoleEx.WriteLine("** WhatIf Mode - No Changes Made **"); consoleEx.WriteLine(""); } DerivedBytesProvider derivedBytesProvider = DerivedBytesProvider.FromPassword(options.Password, options.KeyFile); using DirectoryPair pair = new DirectoryPair(options.DecrDirectory, options.EncrDirectory, derivedBytesProvider, options.WhatIf); pair.PreInitializationCheck(); if (pair.InitializeFullNeeded()) { if (options.Initialize) { //continue, unprompted if (pair.InitializeMergeWarning()) { consoleEx.WriteLine("WARNING: Decrypted directory is not empty and will be merged"); } } else { consoleEx.WriteLine("Directories require initialization..."); if (!consoleEx.PromptBool("Initialized encrypted and decrypted directories now? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } if (pair.InitializeMergeWarning()) { if (!consoleEx.PromptBool("Decrypted directory is not empty and will be merged, continue? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } } } pair.InitializeFull(consoleEx); } pair.OpenEncr(consoleEx); if (pair.InitializeDecrNeeded()) { if (options.Initialize) { //continue, unprompted if (pair.InitializeMergeWarning()) { consoleEx.WriteLine("WARNING: Decrypted directory is not empty and will be merged"); } } else { consoleEx.WriteLine("Decrypted directory require initialization..."); if (!consoleEx.PromptBool("Initialized decrypted directories now? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } if (pair.InitializeMergeWarning()) { if (!consoleEx.PromptBool("Decrypted directory is not empty and will be merged, continue? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } } } pair.InitializeDecr(consoleEx); } pair.OpenDecr(consoleEx); pair.Cleanup(consoleEx); List <PreSyncDetails> changes = pair.FindChanges(clearCache: false, console: consoleEx); if (changes.Count == 0) { consoleEx.WriteLine("--No Changes--"); } var defaultConflictAction = ""; consoleEx.WriteLine(VerbosityLevel.Normal, 0, "Performing Sync..."); foreach (PreSyncDetails change in changes) { consoleEx.WriteLine(change); if (change.SyncMode == PreSyncMode.Conflict) { var decrModified = change.DecrInfo == null ? (object)null : change.DecrInfo.LastWriteTimeUtc.ToLocalTime(); var decrSize = change.DecrInfo == null ? (object)null : HelixUtil.FormatBytes5(change.DecrInfo.Length); var encrModified = change.EncrInfo == null ? (object)null : change.EncrInfo.LastWriteTimeUtc.ToLocalTime(); var encrSize = change.EncrHeader == null ? (object)null : HelixUtil.FormatBytes5(change.EncrHeader.Length); string response; if (defaultConflictAction != "") { response = defaultConflictAction; } else { consoleEx.WriteLine($" Decrypted - Modified: {decrModified}, Size: {decrSize}"); consoleEx.WriteLine($" Encrypted - Modified: {encrModified}, Size: {encrSize}"); consoleEx.WriteLine($""); consoleEx.WriteLine($" D - Decrypted, E - Encrypted, S - Skip, + Always perform this action"); //todo: support newer, support always response = consoleEx.PromptChoice(" Select Option [D,E,S,D+,E+,S+]? ", new string[] { "D", "E", "S", "D+", "E+", "S+" }, "S"); if (response.EndsWith("+")) { response = response.Substring(0, 1); defaultConflictAction = response; } } if (response == "D") { change.SyncMode = PreSyncMode.DecryptedSide; } else if (response == "E") { change.SyncMode = PreSyncMode.EncryptedSide; } if (change.SyncMode != PreSyncMode.Conflict) { consoleEx.WriteLine(change); } } if (change.SyncMode == PreSyncMode.EncryptedSide) { //todo: make display operation be the actual operation if (change.DisplayOperation == PreSyncOperation.Add) { sum.EncrAdd++; } else if (change.DisplayOperation == PreSyncOperation.Remove) { sum.EncrRemove++; } else if (change.DisplayOperation == PreSyncOperation.Change) { sum.EncrChange++; } else { sum.EncrOther++; } } else if (change.SyncMode == PreSyncMode.DecryptedSide) { if (change.DisplayOperation == PreSyncOperation.Add) { sum.DecrAdd++; } else if (change.DisplayOperation == PreSyncOperation.Remove) { sum.DecrRemove++; } else if (change.DisplayOperation == PreSyncOperation.Change) { sum.DecrChange++; } else { sum.DecrOther++; } } else if (change.SyncMode == PreSyncMode.Conflict) { sum.Conflict++; } var syncResult = pair.TrySync(change, consoleEx); //todo: add to error log if (syncResult.Exception != null) { consoleEx.WriteErrorLine("..." + syncResult.Exception.Message); } } consoleEx.WriteLine("== Summary =="); consoleEx.WriteLine(sum); //todo: fix unchanged consoleEx.WriteLine(); //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, ""); //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, "==Decr Directory=="); //foreach (var entry in decrDirectory.FSDirectory.GetEntries(SearchOption.AllDirectories)) //{ // if (entry is FSDirectory dirEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"<dir> {entry.RelativePath}"); // else if (entry is FSFile fileEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"{HelixUtil.FormatBytes5(entry.Length)} {entry.RelativePath}"); //} //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, ""); //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, "==Encr Directory=="); //foreach (var entry in encrDirectory.FSDirectory.GetEntries(SearchOption.AllDirectories)) //{ // if (entry is FSDirectory dirEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"<dir> {entry.RelativePath}"); // else if (entry is FSFile fileEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"{HelixUtil.FormatBytes5(entry.Length)} {entry.RelativePath}"); //} return(sum); }