private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream) { hashedStream.Close(); m_pbHashOfFileOnDisk = hashedStream.Hash; sSource.Close(); }
public void LoadHeader(Stream sSource, KdbxFormat kdbFormat) { Debug.Assert(sSource != null); if (sSource == null) { throw new ArgumentNullException("sSource"); } this.m_format = kdbFormat; var hashedStream = new HashingStreamEx(sSource, false, null); var encNoBom = StrUtil.Utf8; try { if (kdbFormat != KdbxFormat.Default) { return; } var br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted); this.ReadHeader(br); } catch (Exception) { throw; } finally { this.CommonCleanUpRead(sSource, hashedStream); } }
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream) { hashedStream.Close(); m_pbHashOfFileOnDisk = hashedStream.Hash; sSource.Close(); // Reset memory protection settings (to always use reasonable // defaults) m_pwDatabase.MemoryProtection = new MemoryProtectionConfig(); // Remove old backups (this call is required here in order to apply // the default history maintenance settings for people upgrading from // KeePass <= 2.14 to >= 2.15; also it ensures history integrity in // case a different application has created the KDBX file and ignored // the history maintenance settings) m_pwDatabase.MaintainBackups(); // Don't mark database as modified // Expand the root group, such that in case the user accidently // collapses the root group he can simply reopen the database PwGroup pgRoot = m_pwDatabase.RootGroup; if (pgRoot != null) { pgRoot.IsExpanded = true; } else { Debug.Assert(false); } m_pbHashOfHeader = null; }
public void TestRead() { // if we use larger size, StreamReader will read past newline and cause bad hash var bytes = new byte[data.Length + 1]; using (var ms = new MemoryStream(bytes)) { using (var sw = new StreamWriter(ms)) { // set NewLine to ensure we don't run into cross-platform issues on Windows sw.NewLine = "\n"; sw.WriteLine(data); } } using (var ms = new MemoryStream(bytes)) { using (var hs = new HashingStreamEx(ms, false, null)) { using (var sr = new StreamReader(hs)) { var read = sr.ReadLine(); Assert.That(read, Is.EqualTo(data)); } // When the StreamReader is disposed, it calls Dispose on the //HasingStreamEx, which computes the hash. Assert.That(hs.Hash, Is.EqualTo(sha256HashOfData)); } } }
public void TestWrite() { var bytes = new byte[16]; using (var ms = new MemoryStream(bytes)) { using (var hs = new HashingStreamEx(ms, true, null)) { using (var sw = new StreamWriter(hs)) { // set NewLine to ensure we don't run into cross-platform issues on Windows sw.NewLine = "\n"; sw.WriteLine(data); } // When the StreamWriter is disposed, it calls Dispose on the //HasingStreamEx, which computes the hash. Assert.That(hs.Hash, Is.EqualTo(sha256HashOfData)); } } using (var ms = new MemoryStream(bytes)) { using (var sr = new StreamReader(ms)) { var read = sr.ReadLine(); Assert.That(read, Is.EqualTo(data)); } } }
internal void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream) { hashedStream.Close(); m_pbHashOfFileOnDisk = hashedStream.Hash; sSaveTo.Close(); m_xmlWriter = null; }
private void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream) { hashedStream.Dispose(); m_pbHashOfFileOnDisk = hashedStream.Hash; sSaveTo.Dispose(); m_xmlWriter = null; m_pbHashOfHeader = null; }
private void CommonCleanUpWrite(List <Stream> lStreams, HashingStreamEx sHashing) { CloseStreams(lStreams); Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed m_pbHashOfFileOnDisk = sHashing.Hash; Debug.Assert(m_pbHashOfFileOnDisk != null); CleanUpInnerRandomStream(); m_xmlWriter = null; m_pbHashOfHeader = null; }
private static string HashStream(Stream sourceStream, Stream destStream) { string hash; using (var hashingRemoteStream = new HashingStreamEx(sourceStream, false, new SHA256Managed())) { hashingRemoteStream.CopyTo(destStream); hashingRemoteStream.Close(); hash = MemUtil.ByteArrayToHexString(hashingRemoteStream.Hash); destStream.Position = 0; } return(hash); }
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream) { hashedStream.Close(); m_pbHashOfFileOnDisk = hashedStream.Hash; sSource.Close(); // Reset memory protection settings (to always use reasonable // defaults) m_pwDatabase.MemoryProtection = new MemoryProtectionConfig(); // Remove old backups (this call is required here in order to apply // the default history maintenance settings for people upgrading from // KeePass <= 2.14 to >= 2.15; also it ensures history integrity in // case a different application has created the KDBX file and ignored // the history maintenance settings) m_pwDatabase.MaintainBackups(); // Don't mark database as modified }
public MemoryStream GetRemoteDataAndHash(IOConnectionInfo ioc, out string hash) { MemoryStream remoteData = new MemoryStream(); using (var remoteStream = _cachedStorage.OpenFileForRead(ioc)) { //note: directly copying to remoteData and hashing causes NullReferenceExceptions in FTP and with Digest auth // -> use the temp data approach: MemoryStream tempData = new MemoryStream(); remoteStream.CopyTo(tempData); tempData.Position = 0; HashingStreamEx hashingRemoteStream = new HashingStreamEx(tempData, false, new SHA256Managed()); hashingRemoteStream.CopyTo(remoteData); hashingRemoteStream.Close(); hash = MemUtil.ByteArrayToHexString(hashingRemoteStream.Hash); } remoteData.Position = 0; return(remoteData); }
public override void Run() { try { IOConnectionInfo ioc = _app.GetDb().Ioc; IFileStorage fileStorage = _app.GetFileStorage(ioc); if (fileStorage is CachingFileStorage) { throw new Exception("Cannot sync a cached database!"); } StatusLogger.UpdateMessage(UiStringKey.CheckingDatabaseForChanges); //download file from remote location and calculate hash: StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile)); MemoryStream remoteData = new MemoryStream(); using ( HashingStreamEx hashingRemoteStream = new HashingStreamEx(fileStorage.OpenFileForRead(ioc), false, new SHA256Managed())) { hashingRemoteStream.CopyTo(remoteData); hashingRemoteStream.Close(); if (!MemUtil.ArraysEqual(_app.GetDb().KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash)) { _app.TriggerReload(_context); Finish(true); } else { Finish(true, _app.GetResourceString(UiStringKey.RemoteDatabaseUnchanged)); } } } catch (Exception e) { Finish(false, e.Message); } }
private void CommonCleanUpWrite(List <Stream> lStreams, HashingStreamEx sHashing) { if (m_xmlWriter != null) { #if KeePassUWP m_xmlWriter.Dispose(); #else m_xmlWriter.Close(); #endif m_xmlWriter = null; } CloseStreams(lStreams); Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed m_pbHashOfFileOnDisk = sHashing.Hash; Debug.Assert(m_pbHashOfFileOnDisk != null); CleanUpInnerRandomStream(); m_pbHashOfHeader = null; }
/// <summary> /// copies the file in ioc to the local cache. Updates the cache version files and returns the new file hash. /// </summary> protected string UpdateCacheFromRemote(IOConnectionInfo ioc, string cachedFilePath) { //note: we might use the file version to check if it's already in the cache and if copying is required. //However, this is safer. string fileHash; //open stream: using (Stream remoteFile = _cachedStorage.OpenFileForRead(ioc)) { using (HashingStreamEx cachedFile = new HashingStreamEx(File.Create(cachedFilePath), true, new SHA256Managed())) { remoteFile.CopyTo(cachedFile); cachedFile.Close(); fileHash = MemUtil.ByteArrayToHexString(cachedFile.Hash); } } //save hash in cache files: File.WriteAllText(VersionFilePath(ioc), fileHash); File.WriteAllText(BaseVersionFilePath(ioc), fileHash); return(fileHash); }
public void CommitWrite() { _committed = true; _memoryStream.Close(); //write file to cache: //(note: this might overwrite local changes. It's assumed that a sync operation or check was performed before byte[] output = _memoryStream.ToArray(); string hash; using (var hashingStream = new HashingStreamEx(File.Create(_cachingFileStorage.CachedFilePath(_ioc)), true, new SHA256Managed())) { hashingStream.Write(output, 0, output.Length); hashingStream.Close(); hash = MemUtil.ByteArrayToHexString(hashingStream.Hash); } File.WriteAllText(_cachingFileStorage.VersionFilePath(_ioc), hash); //create another memory stream which is open for reading again MemoryStream openMemStream = new MemoryStream(output); //update file on remote. This might overwrite changes there as well, see above. if (_cachingFileStorage.IsCached(_ioc)) { //if the file already is in the cache, it's ok if writing to remote fails. _cachingFileStorage.TryUpdateRemoteFile(openMemStream, _ioc, _useFileTransaction, hash); } else { //if not, we don't accept a failure (e.g. invalid credentials would always remain a problem) _cachingFileStorage.UpdateRemoteFile(openMemStream, _ioc, _useFileTransaction, hash); } openMemStream.Dispose(); }
public override void Close() { if (_closed) { return; } //write file to cache: //(note: this might overwrite local changes. It's assumed that a sync operation or check was performed before string hash; using (var hashingStream = new HashingStreamEx(File.Create(_cachingFileStorage.CachedFilePath(ioc)), true, new SHA256Managed())) { Position = 0; CopyTo(hashingStream); hashingStream.Close(); hash = MemUtil.ByteArrayToHexString(hashingStream.Hash); } File.WriteAllText(_cachingFileStorage.VersionFilePath(ioc), hash); //update file on remote. This might overwrite changes there as well, see above. Position = 0; if (_cachingFileStorage.IsCached(ioc)) { //if the file already is in the cache, it's ok if writing to remote fails. _cachingFileStorage.TryUpdateRemoteFile(this, ioc, _useFileTransaction, hash); } else { //if not, we don't accept a failure (e.g. invalid credentials would always remain a problem) _cachingFileStorage.UpdateRemoteFile(this, ioc, _useFileTransaction, hash); } base.Close(); _closed = true; }
public void PopulateDatabaseFromStream(PwDatabase db, Stream s, IStatusLogger slLogger) { #if !EXCLUDE_KEYTRANSFORM var importer = new Com.Keepassdroid.Database.Load.ImporterV3(); var hashingStream = new HashingStreamEx(s, false, new SHA256Managed()); _metaStreams = new List <PwEntryV3>(); string password = ""; //no need to distinguish between null and "" because empty passwords are invalid (and null is not allowed) KcpPassword passwordKey = (KcpPassword)db.MasterKey.GetUserKey(typeof(KcpPassword)); if (passwordKey != null) { password = passwordKey.Password.ReadString(); } KcpKeyFile passwordKeyfile = (KcpKeyFile)db.MasterKey.GetUserKey(typeof(KcpKeyFile)); MemoryStream keyfileStream = null; if (passwordKeyfile != null) { keyfileStream = new MemoryStream(passwordKeyfile.RawFileData.ReadData()); } try { var dbv3 = importer.OpenDatabase(hashingStream, password, keyfileStream); db.Name = dbv3.Name; db.KdfParameters = (new AesKdf()).GetDefaultParameters(); db.KdfParameters.SetUInt64(AesKdf.ParamRounds, (ulong)dbv3.NumKeyEncRounds); db.RootGroup = ConvertGroup(dbv3.RootGroup); if (dbv3.Algorithm == PwEncryptionAlgorithm.Rjindal) { db.DataCipherUuid = StandardAesEngine.AesUuid; } else { db.DataCipherUuid = new PwUuid(TwofishCipher.TwofishCipherEngine.TwofishCipherUuidBytes); } } catch (Java.IO.FileNotFoundException e) { throw new FileNotFoundException( e.Message, e); } catch (Java.Lang.Exception e) { if (e.Message == "Invalid key!") { throw new InvalidCompositeKeyException(); } throw new Exception(e.LocalizedMessage ?? e.Message ?? e.GetType().Name, e); } HashOfLastStream = hashingStream.Hash; if (HashOfLastStream == null) { throw new Exception("hashing didn't work"); //todo remove } #else throw new Exception("Kdb is excluded with Key transform!"); #endif }
/// <summary> /// Load a KDB file from a stream. /// </summary> /// <param name="sSource">Stream to read the data from. Must contain /// a KDBX stream.</param> /// <param name="kdbFormat">Format specifier.</param> /// <param name="slLogger">Status logger (optional).</param> public void Load(Stream sSource, KdbxFormat kdbFormat, IStatusLogger slLogger) { Debug.Assert(sSource != null); if (sSource == null) { throw new ArgumentNullException("sSource"); } m_format = kdbFormat; m_slLogger = slLogger; HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null); UTF8Encoding encNoBom = StrUtil.Utf8; try { BinaryReaderEx br = null; BinaryReaderEx brDecrypted = null; Stream readerStream = null; if (kdbFormat == KdbxFormat.Default) { br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted); ReadHeader(br); Stream sDecrypted = AttachStreamDecryptor(hashedStream); if ((sDecrypted == null) || (sDecrypted == hashedStream)) { throw new SecurityException(KLRes.CryptoStreamFailed); } brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted); byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) { throw new InvalidDataException(); } for (int iStart = 0; iStart < 32; ++iStart) { if (pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart]) { throw new InvalidCompositeKeyException(); } } Stream sHashed = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode); if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { readerStream = new GZipStream(sHashed, CompressionMode.Decompress); } else { readerStream = sHashed; } } else if (kdbFormat == KdbxFormat.PlainXml) { readerStream = hashedStream; } else { Debug.Assert(false); throw new FormatException("KdbFormat"); } if (kdbFormat != KdbxFormat.PlainXml) // Is an encrypted format { if (m_pbProtectedStreamKey == null) { Debug.Assert(false); throw new SecurityException("Invalid protected stream key!"); } m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_pbProtectedStreamKey); } else { m_randomStream = null; // No random stream for plain-text files } #if KeePassDebug_WriteXml // FileStream fsOut = new FileStream("Raw.xml", FileMode.Create, // FileAccess.Write, FileShare.None); // try // { // while(true) // { // int b = readerStream.ReadByte(); // if(b == -1) break; // fsOut.WriteByte((byte)b); // } // } // catch(Exception) { } // fsOut.Close(); #endif ReadXmlStreamed(readerStream, hashedStream); // ReadXmlDom(readerStream); readerStream.Dispose(); // GC.KeepAlive(br); // GC.KeepAlive(brDecrypted); } #if !KeePass2PCL catch (CryptographicException) // Thrown on invalid padding { throw new CryptographicException(KLRes.FileCorrupted); } #endif finally { CommonCleanUpRead(sSource, hashedStream); } }
// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat fmt, // IStatusLogger slLogger) // { // bool bMadeUnhidden = UrlUtil.UnhideFile(strFile); // // IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); // this.Save(IOConnection.OpenWrite(ioc), pgDataSource, format, slLogger); // // if(bMadeUnhidden) UrlUtil.HideFile(strFile, true); // Hide again // } /// <summary> /// Save the contents of the current <c>PwDatabase</c> to a KDBX file. /// </summary> /// <param name="sSaveTo">Stream to write the KDBX file into.</param> /// <param name="pgDataSource">Group containing all groups and /// entries to write. If <c>null</c>, the complete database will /// be written.</param> /// <param name="fmt">Format of the file to create.</param> /// <param name="slLogger">Logger that recieves status information.</param> public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat fmt, IStatusLogger slLogger) { Debug.Assert(sSaveTo != null); if (sSaveTo == null) { throw new ArgumentNullException("sSaveTo"); } if (m_bUsedOnce) { throw new InvalidOperationException("Do not reuse KdbxFile objects!"); } m_bUsedOnce = true; m_format = fmt; m_slLogger = slLogger; m_xmlWriter = null; PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup); UTF8Encoding encNoBom = StrUtil.Utf8; CryptoRandom cr = CryptoRandom.Instance; byte[] pbCipherKey = null; byte[] pbHmacKey64 = null; m_pbsBinaries = new ProtectedBinarySet(true); m_pbsBinaries.AddFrom(pgRoot); List <Stream> lStreams = new List <Stream>(); lStreams.Add(sSaveTo); HashingStreamEx sHashing = new HashingStreamEx(sSaveTo, true, null); lStreams.Add(sHashing); try { // Fix history entries (should not be necessary; just for safety, // as e.g. XPath searches depend on correct history entry UUIDs) if (m_pwDatabase.MaintainBackups()) { Debug.Assert(false); } m_uFileVersion = GetMinKdbxVersion(); int cbEncKey, cbEncIV; ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV); m_pbMasterSeed = cr.GetRandomBytes(32); m_pbEncryptionIV = cr.GetRandomBytes((uint)cbEncIV); // m_pbTransformSeed = cr.GetRandomBytes(32); PwUuid puKdf = m_pwDatabase.KdfParameters.KdfUuid; KdfEngine kdf = KdfPool.Get(puKdf); if (kdf == null) { throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph + // KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph + "UUID: " + puKdf.ToHexString() + "."); } kdf.Randomize(m_pwDatabase.KdfParameters); if (m_format == KdbxFormat.Default) { if (m_uFileVersion < FileVersion32_4) { m_craInnerRandomStream = CrsAlgorithm.Salsa20; m_pbInnerRandomStreamKey = cr.GetRandomBytes(32); } else // KDBX >= 4 { m_craInnerRandomStream = CrsAlgorithm.ChaCha20; m_pbInnerRandomStreamKey = cr.GetRandomBytes(64); } m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_pbInnerRandomStreamKey); } if (m_uFileVersion < FileVersion32_4) { m_pbStreamStartBytes = cr.GetRandomBytes(32); } Stream sXml; if (m_format == KdbxFormat.Default || m_format == KdbxFormat.ProtocolBuffers) { byte[] pbHeader = GenerateHeader(); m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader); MemUtil.Write(sHashing, pbHeader); sHashing.Flush(); ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64); Stream sPlain; if (m_uFileVersion < FileVersion32_4) { Stream sEncrypted = EncryptStream(sHashing, iCipher, pbCipherKey, cbEncIV, true); if ((sEncrypted == null) || (sEncrypted == sHashing)) { throw new SecurityException(KLRes.CryptoStreamFailed); } lStreams.Add(sEncrypted); MemUtil.Write(sEncrypted, m_pbStreamStartBytes); sPlain = new HashedBlockStream(sEncrypted, true); } else // KDBX >= 4 { // For integrity checking (without knowing the master key) MemUtil.Write(sHashing, m_pbHashOfHeader); byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64); MemUtil.Write(sHashing, pbHeaderHmac); Stream sBlocks = new HmacBlockStream(sHashing, true, true, pbHmacKey64); lStreams.Add(sBlocks); sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey, cbEncIV, true); if ((sPlain == null) || (sPlain == sBlocks)) { throw new SecurityException(KLRes.CryptoStreamFailed); } } lStreams.Add(sPlain); if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { sXml = new GZipStream(sPlain, CompressionMode.Compress); lStreams.Add(sXml); } else { sXml = sPlain; } if (m_uFileVersion >= FileVersion32_4) { WriteInnerHeader(sXml); // Binary header before XML } } else if (m_format == KdbxFormat.PlainXml) { sXml = sHashing; } else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); } var stopWatch = Stopwatch.StartNew(); if (m_format == KdbxFormat.ProtocolBuffers) { KdbpFile.WriteDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader); } else { #if KeePassUAP XmlWriterSettings xws = new XmlWriterSettings(); xws.Encoding = encNoBom; xws.Indent = true; xws.IndentChars = "\t"; xws.NewLineOnAttributes = false; XmlWriter xw = XmlWriter.Create(sXml, xws); #else XmlTextWriter xw = new XmlTextWriter(sXml, encNoBom); xw.Formatting = Formatting.Indented; xw.IndentChar = '\t'; xw.Indentation = 1; #endif m_xmlWriter = xw; WriteDocument(pgRoot); m_xmlWriter.Flush(); m_xmlWriter.Close(); } Kp2aLog.Log(String.Format("{1}: {0}ms", stopWatch.ElapsedMilliseconds, m_format == KdbxFormat.ProtocolBuffers ? "KdbpFile.WriteDocument" : "Xml WriteDocument")); } finally { if (pbCipherKey != null) { MemUtil.ZeroByteArray(pbCipherKey); } if (pbHmacKey64 != null) { MemUtil.ZeroByteArray(pbHmacKey64); } CommonCleanUpWrite(lStreams, sHashing); } }
/// <summary> /// Load a KDB file from a stream. /// </summary> /// <param name="sSource">Stream to read the data from. Must contain /// a KDB4 stream.</param> /// <param name="kdbFormat">Format specifier.</param> /// <param name="slLogger">Status logger (optional).</param> public void Load(Stream sSource, Kdb4Format kdbFormat, IStatusLogger slLogger) { Debug.Assert(sSource != null); if (sSource == null) { throw new ArgumentNullException("sSource"); } m_format = kdbFormat; m_slLogger = slLogger; HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null); UTF8Encoding encNoBom = new UTF8Encoding(false, false); try { BinaryReaderEx br = null; BinaryReaderEx brDecrypted = null; Stream readerStream = null; if (kdbFormat == Kdb4Format.Default) { br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted); ReadHeader(br); Stream sDecrypted = AttachStreamDecryptor(hashedStream); if ((sDecrypted == null) || (sDecrypted == hashedStream)) { throw new SecurityException(KLRes.CryptoStreamFailed); } brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted); byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) { throw new InvalidDataException(); } for (int iStart = 0; iStart < 32; ++iStart) { if (pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart]) { throw new InvalidCompositeKeyException(); } } Stream sHashed = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode); if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { readerStream = new GZipStream(sHashed, CompressionMode.Decompress); } else { readerStream = sHashed; } } else if (kdbFormat == Kdb4Format.PlainXml) { readerStream = hashedStream; } else { Debug.Assert(false); throw new FormatException("KdbFormat"); } if (kdbFormat != Kdb4Format.PlainXml) // Is an encrypted format { if (m_pbProtectedStreamKey == null) { Debug.Assert(false); throw new SecurityException("Invalid protected stream key!"); } m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_pbProtectedStreamKey); } else { m_randomStream = null; // No random stream for plain text files } ReadXmlStreamed(readerStream, hashedStream); // ReadXmlDom(readerStream); GC.KeepAlive(brDecrypted); GC.KeepAlive(br); } catch (CryptographicException) // Thrown on invalid padding { throw new CryptographicException(KLRes.FileCorrupted); } finally { CommonCleanUpRead(sSource, hashedStream); } }
// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat format, // IStatusLogger slLogger) // { // bool bMadeUnhidden = UrlUtil.UnhideFile(strFile); // // IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); // this.Save(IOConnection.OpenWrite(ioc), pgDataSource, format, slLogger); // // if(bMadeUnhidden) UrlUtil.HideFile(strFile, true); // Hide again // } /// <summary> /// Save the contents of the current <c>PwDatabase</c> to a KDBX file. /// </summary> /// <param name="sSaveTo">Stream to write the KDBX file into.</param> /// <param name="pgDataSource">Group containing all groups and /// entries to write. If <c>null</c>, the complete database will /// be written.</param> /// <param name="format">Format of the file to create.</param> /// <param name="slLogger">Logger that recieves status information.</param> public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat format, IStatusLogger slLogger) { Debug.Assert(sSaveTo != null); if (sSaveTo == null) { throw new ArgumentNullException("sSaveTo"); } m_format = format; m_slLogger = slLogger; HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null); UTF8Encoding encNoBom = StrUtil.Utf8; CryptoRandom cr = CryptoRandom.Instance; try { m_pbMasterSeed = cr.GetRandomBytes(32); m_pbTransformSeed = cr.GetRandomBytes(32); m_pbEncryptionIV = cr.GetRandomBytes(16); m_pbProtectedStreamKey = cr.GetRandomBytes(32); m_craInnerRandomStream = CrsAlgorithm.Salsa20; m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_pbProtectedStreamKey); m_pbStreamStartBytes = cr.GetRandomBytes(32); Stream writerStream; if (m_format == KdbxFormat.Default) { WriteHeader(hashedStream); // Also flushes the stream Stream sEncrypted = AttachStreamEncryptor(hashedStream); if ((sEncrypted == null) || (sEncrypted == hashedStream)) { throw new SecurityException(KLRes.CryptoStreamFailed); } sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length); Stream sHashed = new HashedBlockStream(sEncrypted, true); if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { writerStream = new GZipStream(sHashed, CompressionMode.Compress); } else { writerStream = sHashed; } } else if (m_format == KdbxFormat.PlainXml) { writerStream = hashedStream; } else { Debug.Assert(false); throw new FormatException("KdbFormat"); } #if KeePass2PCL var settings = new XmlWriterSettings() { Encoding = encNoBom, Indent = true, IndentChars = "\t", NewLineChars = "\r\n", }; m_xmlWriter = XmlWriter.Create(writerStream, settings); #else m_xmlWriter = new XmlTextWriter(writerStream, encNoBom); #endif WriteDocument(pgDataSource); m_xmlWriter.Flush(); m_xmlWriter.Dispose(); writerStream.Dispose(); } finally { CommonCleanUpWrite(sSaveTo, hashedStream); } }
/// <summary> /// Load a KDBX file from a stream. /// </summary> /// <param name="sSource">Stream to read the data from. Must contain /// a KDBX stream.</param> /// <param name="fmt">Format.</param> /// <param name="slLogger">Status logger (optional).</param> public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger) { Debug.Assert(sSource != null); if (sSource == null) { throw new ArgumentNullException("sSource"); } if (m_bUsedOnce) { throw new InvalidOperationException("Do not reuse KdbxFile objects!"); } m_bUsedOnce = true; #if KDBX_BENCHMARK Stopwatch swTime = Stopwatch.StartNew(); #endif m_format = fmt; m_slLogger = slLogger; // Other applications might not perform a deduplication m_pbsBinaries = new ProtectedBinarySet(false); UTF8Encoding encNoBom = StrUtil.Utf8; byte[] pbCipherKey = null; byte[] pbHmacKey64 = null; List <Stream> lStreams = new List <Stream>(); lStreams.Add(sSource); HashingStreamEx sHashing = new HashingStreamEx(sSource, false, null); lStreams.Add(sHashing); try { Stream sXml; if (fmt == KdbxFormat.Default) { BinaryReaderEx br = new BinaryReaderEx(sHashing, encNoBom, KLRes.FileCorrupted); byte[] pbHeader = LoadHeader(br); m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader); int cbEncKey, cbEncIV; ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV); ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64); string strIncomplete = KLRes.FileHeaderCorrupted + " " + KLRes.FileIncomplete; Stream sPlain; if (m_uFileVersion < FileVersion32_4) { Stream sDecrypted = EncryptStream(sHashing, iCipher, pbCipherKey, cbEncIV, false); if ((sDecrypted == null) || (sDecrypted == sHashing)) { throw new SecurityException(KLRes.CryptoStreamFailed); } lStreams.Add(sDecrypted); BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, strIncomplete); byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) { throw new EndOfStreamException(strIncomplete); } if (!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes)) { throw new InvalidCompositeKeyException(); } sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode); } else // KDBX >= 4 { byte[] pbStoredHash = MemUtil.Read(sHashing, 32); if ((pbStoredHash == null) || (pbStoredHash.Length != 32)) { throw new EndOfStreamException(strIncomplete); } if (!MemUtil.ArraysEqual(m_pbHashOfHeader, pbStoredHash)) { throw new InvalidDataException(KLRes.FileHeaderCorrupted); } byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64); byte[] pbStoredHmac = MemUtil.Read(sHashing, 32); if ((pbStoredHmac == null) || (pbStoredHmac.Length != 32)) { throw new EndOfStreamException(strIncomplete); } if (!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac)) { throw new InvalidCompositeKeyException(); } HmacBlockStream sBlocks = new HmacBlockStream(sHashing, false, !m_bRepairMode, pbHmacKey64); lStreams.Add(sBlocks); sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey, cbEncIV, false); if ((sPlain == null) || (sPlain == sBlocks)) { throw new SecurityException(KLRes.CryptoStreamFailed); } } lStreams.Add(sPlain); if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { sXml = new GZipStream(sPlain, CompressionMode.Decompress); lStreams.Add(sXml); } else { sXml = sPlain; } if (m_uFileVersion >= FileVersion32_4) { LoadInnerHeader(sXml); // Binary header before XML } } else if (fmt == KdbxFormat.PlainXml) { sXml = sHashing; } else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); } if (fmt == KdbxFormat.Default) { if (m_pbInnerRandomStreamKey == null) { Debug.Assert(false); throw new SecurityException("Invalid inner random stream key!"); } m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_pbInnerRandomStreamKey); } #if KeePassDebug_WriteXml #warning XML output is enabled! /* using(FileStream fsOut = new FileStream("Raw.xml", FileMode.Create, * FileAccess.Write, FileShare.None)) * { * while(true) * { * int b = sXml.ReadByte(); * if(b == -1) throw new EndOfStreamException(); * fsOut.WriteByte((byte)b); * } * } */ #endif ReadXmlStreamed(sXml, sHashing); // ReadXmlDom(sXml); } catch (CryptographicException) // Thrown on invalid padding { throw new CryptographicException(KLRes.FileCorrupted); } finally { if (pbCipherKey != null) { MemUtil.ZeroByteArray(pbCipherKey); } if (pbHmacKey64 != null) { MemUtil.ZeroByteArray(pbHmacKey64); } CommonCleanUpRead(lStreams, sHashing); } #if KDBX_BENCHMARK swTime.Stop(); MessageService.ShowInfo("Loading KDBX took " + swTime.ElapsedMilliseconds.ToString() + " ms."); #endif }
public void Save(PwDatabase kpDatabase, Stream stream) { PwDatabaseV3 db = new PwDatabaseV3(); KcpPassword pwd = kpDatabase.MasterKey.GetUserKey <KcpPassword>(); string password = pwd != null?pwd.Password.ReadString() : ""; KcpKeyFile keyfile = kpDatabase.MasterKey.GetUserKey <KcpKeyFile>(); Stream keyfileContents = null; if (keyfile != null) { keyfileContents = new MemoryStream(keyfile.RawFileData.ReadData()); } db.SetMasterKey(password, keyfileContents); AesKdf kdf = new AesKdf(); if (!kdf.Uuid.Equals(kpDatabase.KdfParameters.KdfUuid)) { db.NumRounds = (uint)PwDefs.DefaultKeyEncryptionRounds; } else { ulong uRounds = kpDatabase.KdfParameters.GetUInt64( AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds); uRounds = Math.Min(uRounds, 0xFFFFFFFEUL); db.NumRounds = (uint)uRounds; } db.Name = kpDatabase.Name; if (kpDatabase.DataCipherUuid.Equals(StandardAesEngine.AesUuid)) { db.Algorithm = PwEncryptionAlgorithm.Rjindal; } else { db.Algorithm = PwEncryptionAlgorithm.Twofish; } //create groups db.Groups.Clear(); var fromGroups = kpDatabase.RootGroup.GetGroups(true); Dictionary <int, PwGroupV3> groupV3s = new Dictionary <int, PwGroupV3>(fromGroups.Count()); foreach (PwGroup g in fromGroups) { if (g == kpDatabase.RootGroup) { continue; } PwGroupV3 groupV3 = ConvertGroup(g, db); db.Groups.Add(groupV3); groupV3s[groupV3.Id.Id] = groupV3; } //traverse again and assign parents db.RootGroup = ConvertGroup(kpDatabase.RootGroup, db); db.RootGroup.Level = -1; AssignParent(kpDatabase.RootGroup, db, groupV3s); foreach (PwEntry e in kpDatabase.RootGroup.GetEntries(true)) { PwEntryV3 entryV3 = ConvertEntry(e, db); entryV3.Parent = groupV3s[_groupData[e.ParentGroup.Uuid].Id]; entryV3.Parent.ChildEntries.Add(entryV3); entryV3.GroupId = entryV3.Parent.Id.Id; db.Entries.Add(entryV3); } //add meta stream entries: if (db.Groups.Any()) { foreach (var metaEntry in _metaStreams) { metaEntry.GroupId = db.Groups.First().Id.Id; db.Entries.Add(metaEntry); } } HashingStreamEx hashedStream = new HashingStreamEx(stream, true, null); PwDbV3Output output = new PwDbV3Output(db, hashedStream); output.Output(); hashedStream.Close(); HashOfLastStream = hashedStream.Hash; kpDatabase.HashOfLastIO = kpDatabase.HashOfFileOnDisk = HashOfLastStream; stream.Close(); }
// public void Save(string strFile, PwGroup pgDataSource, Kdb4Format format, // IStatusLogger slLogger) // { // bool bMadeUnhidden = UrlUtil.UnhideFile(strFile); // // IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); // this.Save(IOConnection.OpenWrite(ioc), pgDataSource, format, slLogger); // // if(bMadeUnhidden) UrlUtil.HideFile(strFile, true); // Hide again // } /// <summary> /// Save the contents of the current <c>PwDatabase</c> to a KDB file. /// </summary> /// <param name="sSaveTo">Stream to write the KDB file into.</param> /// <param name="pgDataSource">Group containing all groups and /// entries to write. If <c>null</c>, the complete database will /// be written.</param> /// <param name="format">Format of the file to create.</param> /// <param name="slLogger">Logger that recieves status information.</param> public void Save(Stream sSaveTo, PwGroup pgDataSource, Kdb4Format format, IStatusLogger slLogger) { Debug.Assert(sSaveTo != null); if (sSaveTo == null) { throw new ArgumentNullException("sSaveTo"); } m_format = format; m_slLogger = slLogger; HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null); UTF8Encoding encNoBom = new UTF8Encoding(false, false); CryptoRandom cr = CryptoRandom.Instance; try { m_pbMasterSeed = cr.GetRandomBytes(32); m_pbTransformSeed = cr.GetRandomBytes(32); m_pbEncryptionIV = cr.GetRandomBytes(16); m_pbProtectedStreamKey = cr.GetRandomBytes(32); m_craInnerRandomStream = CrsAlgorithm.Salsa20; m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_pbProtectedStreamKey); m_pbStreamStartBytes = cr.GetRandomBytes(32); Stream writerStream; BinaryWriter bw = null; if (m_format == Kdb4Format.Default) { bw = new BinaryWriter(hashedStream, encNoBom); WriteHeader(bw); // Also flushes bw Stream sEncrypted = AttachStreamEncryptor(hashedStream); if ((sEncrypted == null) || (sEncrypted == hashedStream)) { throw new SecurityException(KLRes.CryptoStreamFailed); } sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length); Stream sHashed = new HashedBlockStream(sEncrypted, true); if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { writerStream = new GZipStream(sHashed, CompressionMode.Compress); } else { writerStream = sHashed; } } else if (m_format == Kdb4Format.PlainXml) { writerStream = hashedStream; } else { Debug.Assert(false); throw new FormatException("KdbFormat"); } m_xmlWriter = new XmlTextWriter(writerStream, encNoBom); WriteDocument(pgDataSource); m_xmlWriter.Flush(); m_xmlWriter.Close(); writerStream.Close(); GC.KeepAlive(bw); } finally { CommonCleanUpWrite(sSaveTo, hashedStream); } }
// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat fmt, // IStatusLogger slLogger) // { // bool bMadeUnhidden = UrlUtil.UnhideFile(strFile); // // IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); // this.Save(IOConnection.OpenWrite(ioc), pgDataSource, format, slLogger); // // if(bMadeUnhidden) UrlUtil.HideFile(strFile, true); // Hide again // } /// <summary> /// Save the contents of the current <c>PwDatabase</c> to a KDBX file. /// </summary> /// <param name="sSaveTo">Stream to write the KDBX file into.</param> /// <param name="pgDataSource">Group containing all groups and /// entries to write. If <c>null</c>, the complete database will /// be written.</param> /// <param name="fmt">Format of the file to create.</param> /// <param name="slLogger">Logger that recieves status information.</param> public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat fmt, IStatusLogger slLogger) { Debug.Assert(sSaveTo != null); if (sSaveTo == null) { throw new ArgumentNullException("sSaveTo"); } if (m_bUsedOnce) { throw new InvalidOperationException("Do not reuse KdbxFile objects!"); } m_bUsedOnce = true; m_format = fmt; m_slLogger = slLogger; m_xmlWriter = null; PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup); UTF8Encoding encNoBom = StrUtil.Utf8; CryptoRandom cr = CryptoRandom.Instance; byte[] pbCipherKey = null; byte[] pbHmacKey64 = null; m_pbsBinaries.Clear(); m_pbsBinaries.AddFrom(pgRoot); List <Stream> lStreams = new List <Stream>(); lStreams.Add(sSaveTo); HashingStreamEx sHashing = new HashingStreamEx(sSaveTo, true, null); lStreams.Add(sHashing); try { m_uFileVersion = GetMinKdbxVersion(); int cbEncKey, cbEncIV; ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV); m_pbMasterSeed = cr.GetRandomBytes(32); m_pbEncryptionIV = cr.GetRandomBytes((uint)cbEncIV); // m_pbTransformSeed = cr.GetRandomBytes(32); PwUuid puKdf = m_pwDatabase.KdfParameters.KdfUuid; KdfEngine kdf = KdfPool.Get(puKdf); if (kdf == null) { throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph + // KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph + "UUID: " + puKdf.ToHexString() + "."); } kdf.Randomize(m_pwDatabase.KdfParameters); if (m_format == KdbxFormat.Default) { if (m_uFileVersion < FileVersion32_4) { m_craInnerRandomStream = CrsAlgorithm.Salsa20; m_pbInnerRandomStreamKey = cr.GetRandomBytes(32); } else // KDBX >= 4 { m_craInnerRandomStream = CrsAlgorithm.ChaCha20; m_pbInnerRandomStreamKey = cr.GetRandomBytes(64); } m_randomStream = new CryptoRandomStream(m_craInnerRandomStream, m_pbInnerRandomStreamKey); } if (m_uFileVersion < FileVersion32_4) { m_pbStreamStartBytes = cr.GetRandomBytes(32); } Stream sXml; if (m_format == KdbxFormat.Default) { byte[] pbHeader = GenerateHeader(); m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader); MemUtil.Write(sHashing, pbHeader); sHashing.Flush(); ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64); Stream sPlain; if (m_uFileVersion < FileVersion32_4) { Stream sEncrypted = EncryptStream(sHashing, iCipher, pbCipherKey, cbEncIV, true); if ((sEncrypted == null) || (sEncrypted == sHashing)) { throw new SecurityException(KLRes.CryptoStreamFailed); } lStreams.Add(sEncrypted); MemUtil.Write(sEncrypted, m_pbStreamStartBytes); sPlain = new HashedBlockStream(sEncrypted, true); } else // KDBX >= 4 { // For integrity checking (without knowing the master key) MemUtil.Write(sHashing, m_pbHashOfHeader); byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64); MemUtil.Write(sHashing, pbHeaderHmac); Stream sBlocks = new HmacBlockStream(sHashing, true, true, pbHmacKey64); lStreams.Add(sBlocks); sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey, cbEncIV, true); if ((sPlain == null) || (sPlain == sBlocks)) { throw new SecurityException(KLRes.CryptoStreamFailed); } } lStreams.Add(sPlain); if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { sXml = new GZipStream(sPlain, CompressionMode.Compress); lStreams.Add(sXml); } else { sXml = sPlain; } if (m_uFileVersion >= FileVersion32_4) { WriteInnerHeader(sXml); // Binary header before XML } } else if (m_format == KdbxFormat.PlainXml) { sXml = sHashing; } else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); } m_xmlWriter = XmlUtilEx.CreateXmlWriter(sXml); WriteDocument(pgRoot); m_xmlWriter.Flush(); } finally { CommonCleanUpWrite(lStreams, sHashing); if (pbCipherKey != null) { MemUtil.ZeroByteArray(pbCipherKey); } if (pbHmacKey64 != null) { MemUtil.ZeroByteArray(pbHmacKey64); } } }
/// <summary> /// Load a KDB file from a stream. /// </summary> /// <param name="sSource"> /// Stream to read the data from. Must contain /// a KDBX stream. /// </param> /// <param name="kdbFormat"> /// Format specifier. /// </param> /// <param name="slLogger"> /// Status logger (optional). /// </param> public void Load(Stream sSource, KdbxFormat kdbFormat, IStatusLogger slLogger, CancellationToken token) { Debug.Assert(sSource != null); if (sSource == null) { throw new ArgumentNullException("sSource"); } this.m_format = kdbFormat; this.m_slLogger = slLogger; var hashedStream = new HashingStreamEx(sSource, false, null); var encNoBom = StrUtil.Utf8; try { BinaryReaderEx br = null; BinaryReaderEx brDecrypted = null; Stream readerStream = null; if (kdbFormat == KdbxFormat.Default) { br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted); this.ReadHeader(br); Stream sDecrypted = this.AttachStreamDecryptor(hashedStream, token); if ((sDecrypted == null) || (sDecrypted == hashedStream)) { throw new SecurityException(KLRes.CryptoStreamFailed); } brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted); byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); if ((this.m_pbStreamStartBytes == null) || (this.m_pbStreamStartBytes.Length != 32)) { throw new InvalidDataException(); } for (int iStart = 0; iStart < 32; ++iStart) { if (pbStoredStartBytes[iStart] != this.m_pbStreamStartBytes[iStart]) { throw new InvalidCompositeKeyException(); } } Stream sHashed = new HashedBlockStream(sDecrypted, false, 0, !this.m_bRepairMode); if (this.m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) { readerStream = new GZipStream(sHashed, CompressionMode.Decompress); } else { readerStream = sHashed; } } else if (kdbFormat == KdbxFormat.PlainXml) { readerStream = hashedStream; } else { Debug.Assert(false); throw new FormatException("KdbFormat"); } if (kdbFormat != KdbxFormat.PlainXml) { // Is an encrypted format if (this.m_pbProtectedStreamKey == null) { Debug.Assert(false); throw new SecurityException("Invalid protected stream key!"); } this.m_randomStream = new CryptoRandomStream( this.m_craInnerRandomStream, this.m_pbProtectedStreamKey); } else { this.m_randomStream = null; // No random stream for plain-text files } this.ReadXmlStreamed(readerStream, hashedStream, token); readerStream.Dispose(); } finally { this.CommonCleanUpRead(sSource, hashedStream); } }