/// <summary> /// Write password data with filtering original data. Available filters must be added to instance before this method is called. /// </summary> public virtual void WritePasswordToFile(byte[] masterPasswordHash, PasswordFileBody passwordData) { if (!PasswordFile.CheckDirectoryWritable(Directory.GetParent(this.Filepath).FullName)) { throw new IOException(); } MemoryStream decryptedData = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); // Apply data filters FilteredData filteredData = IOFilterProcessor.ApplyFilter(passwordData, this.FilterOrder); // Convert FilteredData object into MemoryStream formatter.Serialize(decryptedData, filteredData); decryptedData.Position = 0; // Get token byte[] token = Utility.Get16BytesHash(DateTime.Now); // Do encryption here byte[] encryptedBytes = Utility.Encrypt(decryptedData.ToArray(), masterPasswordHash, token); // Construct header PasswordHeader header = new PasswordHeader(); header.Token = token; header.CombinedMasterPasswordHash = Utility.Scramble(masterPasswordHash, token, LocalConfig.MasterPasswordHashedKeySize); // Write filtered data to the file using (FileStream fs = new FileStream(this.Filepath, FileMode.Create, FileAccess.Write)) { // Write header using (BinaryWriter writer = new BinaryWriter(fs)) { writer.Write(header.Token); // Write token writer.Write(header.CombinedMasterPasswordHash); // Write hash // Write Filtered data which has been encrypted writer.Write(encryptedBytes); } } }
/// <summary> /// Check whether password hash is valid for specified password file. /// </summary> /// <param name="filePath"></param> /// <returns></returns> public static bool ChallengeHashedMasterPassword(string filePath, byte[] challengingPasswordHash) { // When password file does not exist, throw an exception. if (!File.Exists(filePath)) { throw new FileNotFoundException(filePath); } // Parse file header using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(fs)) { PasswordHeader header = new PasswordHeader(); header.Token = reader.ReadBytes(LocalConfig.HeaderTokenSize); header.CombinedMasterPasswordHash = reader.ReadBytes(LocalConfig.MasterPasswordHashedKeySize); return CompareHashes(header.CombinedMasterPasswordHash, Utility.Scramble(challengingPasswordHash, header.Token, LocalConfig.MasterPasswordHashedKeySize)); } } }
/// <summary> /// Read password file with removing data filter. Available filters must be added to instance before this method is called. /// </summary> /// <exception cref="FileNotFoundException">Password file does not exist</exception> /// <exception cref="InvalidMasterPasswordException">Master password is invalid</exception> public virtual PasswordFileBody ReadPasswordFromFile(byte[] masterPasswordHash, PasswordFileBody defaultPasswordFileBody = null) { if (masterPasswordHash.Length != LocalConfig.MasterPasswordHashedKeySize) { throw new ArgumentException(); } // When password file does not exist, throw an exception. if (!File.Exists(this.Filepath)) { throw new FileNotFoundException(this.Filepath); } FilteredData filteredData; // Parse file header using (FileStream fs = new FileStream(this.Filepath, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(fs)) { PasswordHeader header = new PasswordHeader(); header.Token = reader.ReadBytes(LocalConfig.HeaderTokenSize); header.CombinedMasterPasswordHash = reader.ReadBytes(LocalConfig.MasterPasswordHashedKeySize); // Check masterPasswordHash is valid if (!CheckMasterPasswordHash(header.CombinedMasterPasswordHash, masterPasswordHash, header.Token)) { throw new InvalidMasterPasswordException(); } MemoryStream encryptedData = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); encryptedData = Utility.GetMemoryStream(reader); encryptedData.Position = 0; // Do decryption against loaded MemoryStream byte[] decryptedBytes = Utility.Decrypt(encryptedData.ToArray(), masterPasswordHash, header.Token); MemoryStream decryptedData = new MemoryStream(decryptedBytes); decryptedData.Position = 0; // Get filtered data filteredData = (FilteredData)formatter.Deserialize(decryptedData); } } // Parse filter data return (PasswordFileBody)IOFilterProcessor.RemoveFilter(filteredData); }