/// <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 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); } }