public override void Erase(Stream stream, long erasureLength, IPrng prng, ErasureMethodProgressFunction callback) { //Randomize the order of the passes ErasureMethodPass[] randomizedPasses = PassesSet; if (RandomizePasses) { randomizedPasses = ShufflePasses(randomizedPasses); } //Remember the starting position of the stream. long strmStart = stream.Position; long strmLength = Math.Min(stream.Length - strmStart, erasureLength); long totalData = CalculateEraseDataSize(null, strmLength); //Allocate memory for a buffer holding data for the pass. byte[] buffer = new byte[Math.Min(DiskOperationUnit, strmLength)]; //Run every pass! for (int pass = 0; pass < Passes; ++pass) { //Do a progress callback first. if (callback != null) { callback(0, totalData, pass + 1); } //Start from the beginning again stream.Seek(strmStart, SeekOrigin.Begin); //Write the buffer to disk. long toWrite = strmLength; int dataStopped = buffer.Length; while (toWrite > 0) { //Calculate how much of the buffer to write to disk. int amount = (int)Math.Min(toWrite, buffer.Length - dataStopped); //If we have no data left, get more! if (amount == 0) { randomizedPasses[pass].Execute(buffer, prng); dataStopped = 0; continue; } //Write the data. stream.Write(buffer, dataStopped, amount); stream.Flush(); dataStopped += amount; toWrite -= amount; //Do a progress callback. if (callback != null) { callback(amount, totalData, pass + 1); } } } }
/// <summary> /// Generates a random file name with the given length. /// </summary> /// <remarks>The generated file name is guaranteed not to exist.</remarks> /// <param name="info">The directory to generate the file name in. This /// parameter can be null to indicate merely a random file name</param> /// <param name="length">The length of the file name to generate.</param> /// <returns>A full path to a file containing random file name.</returns> public static string GenerateRandomFileName(DirectoryInfo info, int length) { //Get the PRNG we are going to use IPrng prng = Host.Instance.Prngs.ActivePrng; //Initialsie the base name, if any. string resultPrefix = info == null ? string.Empty : info.FullName + Path.DirectorySeparatorChar; //Variables to store the intermediates. byte[] resultAry = new byte[length]; string result = string.Empty; List <string> prohibitedFileNames = new List <string>(new string[] { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" }); do { prng.NextBytes(resultAry); //Validate the name string validFileNameChars = "0123456789abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ _+=-()[]{}',`~!"; for (int j = 0, k = resultAry.Length; j < k; ++j) { resultAry[j] = (byte)validFileNameChars[ (int)resultAry[j] % validFileNameChars.Length]; if (j == 0 || j == k - 1) { //The first or last character cannot be whitespace while (Char.IsWhiteSpace((char)resultAry[j])) { resultAry[j] = (byte)validFileNameChars[ (int)resultAry[j] % validFileNameChars.Length]; } } } result = Encoding.UTF8.GetString(resultAry); }while (info != null && prohibitedFileNames.IndexOf(Path.GetFileNameWithoutExtension(result)) != -1 || (Directory.Exists(resultPrefix + result) || File.Exists(resultPrefix + result))); return(resultPrefix + result); }
public override void Erase(Stream strm, long erasureLength, IPrng prng, ErasureMethodProgressFunction callback) { //If we have no default or we are the default then throw an exception if (method == null || method.Guid == Guid) { throw new InvalidOperationException(S._("The First/last 16KB erasure method " + "requires another erasure method to erase the file.\n\nThis must " + "be set in the Plugin Settings dialog.")); } //Make sure that the erasureLength passed in here is the maximum value //for the size of long, since we don't want to write extra or write //less. if (erasureLength != long.MaxValue) { throw new ArgumentException("The amount of data erased should not be " + "limited, since this is a self-limiting erasure method."); } //If the target stream is shorter than or equal to 32kb, just forward it to //the default function. if (strm.Length < DataSize * 2) { method.Erase(strm, erasureLength, prng, callback); return; } //We need to intercept the callback function as we run the erasure method //twice on two parts of the file. long dataSize = method.CalculateEraseDataSize(null, DataSize * 2); ErasureMethodProgressFunction customCallback = delegate(long lastWritten, long totalData, int currentPass) { callback(lastWritten, dataSize, currentPass); }; //Seek to the beginning and write 16kb. strm.Seek(0, SeekOrigin.Begin); method.Erase(strm, dataSize, prng, callback == null ? null: customCallback); //Seek to the end - 16kb, and write. strm.Seek(-dataSize, SeekOrigin.End); method.Erase(strm, long.MaxValue, prng, callback == null ? null : customCallback); }
/// <summary> /// Shuffles the passes in the input array, effectively randomizing the /// order or rewrites. /// </summary> /// <param name="passes">The input set of passes.</param> /// <returns>The shuffled set of passes.</returns> protected static ErasureMethodPass[] ShufflePasses(ErasureMethodPass[] passes) { //Make a copy. ErasureMethodPass[] result = new ErasureMethodPass[passes.Length]; passes.CopyTo(result, 0); //Randomize. IPrng rand = Host.Instance.Prngs.ActivePrng; for (int i = 0; i < result.Length; ++i) { int val = rand.Next(result.Length - 1); ErasureMethodPass tmpPass = result[val]; result[val] = result[i]; result[i] = tmpPass; } return(result); }
/// <summary> /// Gets a random file from within the provided directory. /// </summary> /// <param name="info">The directory to get a random file name from.</param> /// <returns>A string containing the full path to the file.</returns> public static string GetRandomFile(DirectoryInfo info) { //First retrieve the list of files and folders in the provided directory. FileSystemInfo[] entries = null; try { entries = info.GetFileSystemInfos(); } catch (DirectoryNotFoundException) { return(string.Empty); } if (entries.Length == 0) { return(string.Empty); } //Find a random entry. IPrng prng = Host.Instance.Prngs.ActivePrng; string result = string.Empty; while (result.Length == 0) { int index = prng.Next(entries.Length - 1); if (entries[index] is DirectoryInfo) { result = GetRandomFile((DirectoryInfo)entries[index]); } else { result = ((FileInfo)entries[index]).FullName; } } return(result); }
public abstract void Erase(Stream stream, long erasureLength, IPrng prng, ErasureMethodProgressFunction callback);
/// <summary> /// Executes the pass. /// </summary> /// <param name="buffer">The buffer to populate with the data to write.</param> /// <param name="prng">The PRNG used for random passes.</param> public void Execute(byte[] buffer, IPrng prng) { Function(buffer, OpaqueValue == null ? prng : OpaqueValue); }
public virtual void EraseDriveSpace(Stream stream, IPrng prng, ErasureMethodProgressFunction callback) { Erase(stream, long.MaxValue, prng, callback); }
private void saveSettings_Click(object sender, EventArgs e) { EraserSettings settings = EraserSettings.Get(); //Save the settings that don't fail first. Host.Instance.Settings.ForceUnlockLockedFiles = lockedForceUnlock.Checked; ManagerLibrary.Instance.Settings.ExecuteMissedTasksImmediately = schedulerMissedImmediate.Checked; settings.ClearCompletedTasks = schedulerClearCompleted.Checked; bool pluginApprovalsChanged = false; IDictionary <Guid, bool> pluginApprovals = ManagerLibrary.Instance.Settings.PluginApprovals; foreach (ListViewItem item in pluginsManager.Items) { PluginInfo plugin = (PluginInfo)item.Tag; Guid guid = plugin.AssemblyInfo.Guid; if (!pluginApprovals.ContainsKey(guid)) { if (plugin.Loaded != item.Checked) { pluginApprovals.Add(guid, item.Checked); pluginApprovalsChanged = true; } } else if (pluginApprovals[guid] != item.Checked) { pluginApprovals[guid] = item.Checked; pluginApprovalsChanged = true; } } if (pluginApprovalsChanged) { MessageBox.Show(this, S._("Plugins which have just been approved will only be loaded " + "the next time Eraser is started."), S._("Eraser"), MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, Localisation.IsRightToLeft(this) ? MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0); } //Error checks for the rest that do. errorProvider.Clear(); if (uiLanguage.SelectedIndex == -1) { errorProvider.SetError(uiLanguage, S._("An invalid language was selected.")); return; } else if (eraseFilesMethod.SelectedIndex == -1) { errorProvider.SetError(eraseFilesMethod, S._("An invalid file erasure method " + "was selected.")); return; } else if (eraseDriveMethod.SelectedIndex == -1) { errorProvider.SetError(eraseDriveMethod, S._("An invalid drive erasure method " + "was selected.")); return; } else if (erasePRNG.SelectedIndex == -1) { errorProvider.SetError(erasePRNG, S._("An invalid randomness data source was " + "selected.")); return; } else if (plausibleDeniability.Checked && plausibleDeniabilityFiles.Items.Count == 0) { errorProvider.SetError(plausibleDeniabilityFiles, S._("Erasures with plausible deniability " + "was selected, but no files were selected to be used as decoys.")); errorProvider.SetIconPadding(plausibleDeniabilityFiles, -16); return; } if (CultureInfo.CurrentUICulture.Name != ((CultureInfo)uiLanguage.SelectedItem).Name) { settings.Language = ((CultureInfo)uiLanguage.SelectedItem).Name; MessageBox.Show(this, S._("The new UI language will take only effect when " + "Eraser is restarted."), S._("Eraser"), MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, Localisation.IsRightToLeft(this) ? MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0); } settings.IntegrateWithShell = uiContextMenu.Checked; Host.Instance.Settings.DefaultFileErasureMethod = ((IErasureMethod)eraseFilesMethod.SelectedItem).Guid; Host.Instance.Settings.DefaultDriveErasureMethod = ((IErasureMethod)eraseDriveMethod.SelectedItem).Guid; IPrng newPRNG = (IPrng)erasePRNG.SelectedItem; if (newPRNG.Guid != Host.Instance.Prngs.ActivePrng.Guid) { MessageBox.Show(this, S._("The new randomness data source will only be used when " + "the next task is run.\nCurrently running tasks will use the old source."), S._("Eraser"), MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, Localisation.IsRightToLeft(this) ? MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0); Host.Instance.Settings.ActivePrng = newPRNG.Guid; } Host.Instance.Settings.PlausibleDeniability = plausibleDeniability.Checked; IList <string> plausibleDeniabilityFilesList = Host.Instance.Settings.PlausibleDeniabilityFiles; plausibleDeniabilityFilesList.Clear(); foreach (string str in this.plausibleDeniabilityFiles.Items) { plausibleDeniabilityFilesList.Add(str); } }
public void Erase(Stream strm, long erasureLength, IPrng prng, ErasureMethodProgressFunction callback) { throw new InvalidOperationException("The DefaultMethod class should never " + "be used and should instead be replaced before execution!"); }
/// <summary> /// Writes a file for plausible deniability over the current stream. /// </summary> /// <param name="stream">The stream to write the data to.</param> private void DeleteFileWithPlausibleDeniability(FileInfo info) { ObfuscateFileSystemInfoName(info); //Get the template file to copy FileInfo shadowFileInfo; { string shadowFile = null; List <string> entries = new List <string>( Host.Instance.Settings.PlausibleDeniabilityFiles); IPrng prng = Host.Instance.Prngs.ActivePrng; do { if (entries.Count == 0) { throw new FatalException(S._("Plausible deniability was selected, " + "but no decoy files were found. The current file has been only " + "replaced with random data.")); } //Get an item from the list of files, and then check that the item exists. int index = prng.Next(entries.Count - 1); shadowFile = entries[index]; if (File.Exists(shadowFile) || Directory.Exists(shadowFile)) { if ((File.GetAttributes(shadowFile) & FileAttributes.Directory) != 0) { DirectoryInfo dir = new DirectoryInfo(shadowFile); FileInfo[] files = dir.GetFiles("*", SearchOption.AllDirectories); entries.Capacity += files.Length; foreach (FileInfo f in files) { entries.Add(f.FullName); } shadowFile = null; } else { shadowFile = entries[index]; } } else { shadowFile = null; } entries.RemoveAt(index); }while (string.IsNullOrEmpty(shadowFile)); shadowFileInfo = new FileInfo(shadowFile); } //Copy the contents of the shadow. using (Stream stream = info.OpenWrite()) { //Dump the copy (the first 4MB, or less, depending on the file size and size of //the original file) long amountToCopy = Math.Min(4 * 1024 * 1024, shadowFileInfo.Length); using (FileStream shadowFileStream = shadowFileInfo.Open( FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { while (stream.Position < amountToCopy) { byte[] buf = new byte[524288]; int bytesRead = shadowFileStream.Read(buf, 0, buf.Length); //Stop bothering if the input stream is at the end if (bytesRead == 0) { break; } //Dump the read contents onto the file to be deleted stream.Write(buf, 0, (int)Math.Min(bytesRead, amountToCopy - stream.Position)); } } //Make the file to be deleted have the same length as the file //which is being used as a decoy. The end of the file might contain //noise, which should make the decoy more convincing. stream.SetLength(shadowFileInfo.Length); } //Make the file to be deleted look like the shadow. info.MoveTo(Path.Combine(info.DirectoryName, shadowFileInfo.Name)); if (shadowFileInfo.IsCompressed()) { info.Compress(); } info.SetAccessControl(shadowFileInfo.GetAccessControl()); info.Attributes = shadowFileInfo.Attributes; info.CopyTimes(shadowFileInfo); //Just delete the file. info.Delete(); }
/// <summary> /// Create a new RandomizedList of type T /// </summary> /// <param name="prng">A class that implements IPrng and can create strong pseduo-random numbers</param> public RandomizedList(IPrng prng) { Prng = prng; }