public KeyCipher(IntPtr keePassWindowHandle) { _randomSeedBits = 256; _encryptionIV = new byte[16]; _cipherEngine = CipherPool.GlobalPool.GetCipher(StandardAesEngine.AesUuid); _cryptProvider = GetAuthProvider(keePassWindowHandle); }
private ICipherEngine GetCipher(out int cbEncKey, out int cbEncIV) { PwUuid pu = m_pwDatabase.DataCipherUuid; ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu); if(iCipher == null) // CryptographicExceptions are translated to "file corrupted" throw new Exception(KLRes.FileUnknownCipher + MessageService.NewParagraph + KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph + "UUID: " + pu.ToHexString() + "."); ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2); if(iCipher2 != null) { cbEncKey = iCipher2.KeyLength; if(cbEncKey < 0) throw new InvalidOperationException("EncKey.Length"); cbEncIV = iCipher2.IVLength; if(cbEncIV < 0) throw new InvalidOperationException("EncIV.Length"); } else { cbEncKey = 32; cbEncIV = 16; } return iCipher; }
public KeyCipher(string message, IntPtr windowHandle) { _randomSeedBits = 256; _encryptionIV = CryptoRandom.Instance.GetRandomBytes(16); _cipherEngine = CipherPool.GlobalPool.GetCipher(StandardAesEngine.AesUuid); _cryptProvider = AuthProviderFactory.GetInstance(message, windowHandle); }
/// <summary> /// Add a cipher engine to the pool. /// </summary> /// <param name="csEngine">Cipher engine to add. Must not be <c>null</c>.</param> public void AddCipher(ICipherEngine csEngine) { Debug.Assert(csEngine != null); if(csEngine == null) throw new ArgumentNullException("csEngine"); // Return if a cipher with that ID is registered already. for(int i = 0; i < m_vCiphers.Count; ++i) if(m_vCiphers[i].CipherUuid.Equals(csEngine.CipherUuid)) return; m_vCiphers.Add(csEngine); }
private Stream EncryptStream(Stream s, ICipherEngine iCipher, byte[] pbKey, int cbIV, bool bEncrypt) { byte[] pbIV = (m_pbEncryptionIV ?? MemUtil.EmptyByteArray); if(pbIV.Length != cbIV) { Debug.Assert(false); throw new Exception(KLRes.FileCorrupted); } if(bEncrypt) return iCipher.EncryptStream(s, pbKey, pbIV); return iCipher.DecryptStream(s, pbKey, pbIV); }
/// <summary> /// Add a cipher engine to the pool. /// </summary> /// <param name="c">Cipher engine to add. Must not be <c>null</c>.</param> public void AddCipher(ICipherEngine c) { if (c == null) { Debug.Assert(false); throw new ArgumentNullException("c"); } // Return if a cipher with that ID is registered already foreach (ICipherEngine cEx in m_lCiphers) { if (cEx.CipherUuid.Equals(c.CipherUuid)) { return; } } m_lCiphers.Add(c); }
private Stream AttachStreamEncryptor(Stream s) { MemoryStream ms = new MemoryStream(); Debug.Assert(m_pbMasterSeed != null); Debug.Assert(m_pbMasterSeed.Length == 32); ms.Write(m_pbMasterSeed, 0, 32); Debug.Assert(m_pwDatabase != null); Debug.Assert(m_pwDatabase.MasterKey != null); ProtectedBinary pbinKey = m_pwDatabase.MasterKey.GenerateKey32( m_pbTransformSeed, m_pwDatabase.KeyEncryptionRounds); Debug.Assert(pbinKey != null); if (pbinKey == null) { throw new SecurityException(KLRes.InvalidCompositeKey); } byte[] pKey32 = pbinKey.ReadData(); if ((pKey32 == null) || (pKey32.Length != 32)) { throw new SecurityException(KLRes.InvalidCompositeKey); } ms.Write(pKey32, 0, 32); #if KeePass2PCL var sha256 = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Sha256); var aesKey = sha256.HashData(ms.ToArray()); #else SHA256Managed sha256 = new SHA256Managed(); byte[] aesKey = sha256.ComputeHash(ms.ToArray()); #endif ms.Dispose(); Array.Clear(pKey32, 0, 32); Debug.Assert(CipherPool.GlobalPool != null); ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid); if (iEngine == null) { throw new SecurityException(KLRes.FileUnknownCipher); } return(iEngine.EncryptStream(s, aesKey, m_pbEncryptionIV)); }
/// <summary> /// Add a cipher engine to the pool. /// </summary> /// <param name="csEngine">Cipher engine to add. Must not be <c>null</c>.</param> public void AddCipher(ICipherEngine csEngine) { Debug.Assert(csEngine != null); if (csEngine == null) { throw new ArgumentNullException("csEngine"); } // Return if a cipher with that ID is registered already. for (int i = 0; i < m_vCiphers.Count; ++i) { if (m_vCiphers[i].CipherUuid.Equals(csEngine.CipherUuid)) { return; } } m_vCiphers.Add(csEngine); }
/// <summary> /// The attach stream decryptor. /// </summary> /// <param name="s"> /// The s. /// </param> /// <returns> /// The <see cref="Stream"/>. /// </returns> /// <exception cref="FormatException"> /// </exception> /// <exception cref="SecurityException"> /// </exception> private Stream AttachStreamDecryptor(Stream s, CancellationToken token) { MemoryStream ms = new MemoryStream(); Debug.Assert(this.m_pbMasterSeed.Length == 32); if (this.m_pbMasterSeed.Length != 32) { throw new FormatException(KLRes.MasterSeedLengthInvalid); } ms.Write(this.m_pbMasterSeed, 0, 32); byte[] pKey32 = this.m_pwDatabase.MasterKey.GenerateKey32(this.m_pbTransformSeed, this.m_pwDatabase.KeyEncryptionRounds, token).ReadData(); if ((pKey32 == null) || (pKey32.Length != 32)) { throw new SecurityException(KLRes.InvalidCompositeKey); } ms.Write(pKey32, 0, 32); var sha256 = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha256); byte[] aesKey = sha256.HashData(ms.ToArray().AsBuffer()).ToArray(); ms.Dispose(); Array.Clear(pKey32, 0, 32); if ((aesKey == null) || (aesKey.Length != 32)) { throw new SecurityException(KLRes.FinalKeyCreationFailed); } ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(this.m_pwDatabase.DataCipherUuid); if (iEngine == null) { throw new SecurityException(KLRes.FileUnknownCipher); } return(iEngine.DecryptStream(s, aesKey, this.m_pbEncryptionIV)); }
private Stream AttachStreamDecryptor(Stream s) { MemoryStream ms = new MemoryStream(); Debug.Assert(m_pbMasterSeed.Length == 32); if (m_pbMasterSeed.Length != 32) { throw new FormatException(KLRes.MasterSeedLengthInvalid); } ms.Write(m_pbMasterSeed, 0, 32); byte[] pKey32 = m_pwDatabase.MasterKey.GenerateKey32(m_pbTransformSeed, m_pwDatabase.KeyEncryptionRounds).ReadData(); if ((pKey32 == null) || (pKey32.Length != 32)) { throw new SecurityException(KLRes.InvalidCompositeKey); } ms.Write(pKey32, 0, 32); SHA256Managed sha256 = new SHA256Managed(); byte[] aesKey = sha256.ComputeHash(ms.ToArray()); ms.Close(); Array.Clear(pKey32, 0, 32); if ((aesKey == null) || (aesKey.Length != 32)) { throw new SecurityException(KLRes.FinalKeyCreationFailed); } ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid); if (iEngine == null) { throw new SecurityException(KLRes.FileUnknownCipher); } return(iEngine.DecryptStream(s, aesKey, m_pbEncryptionIV)); }
private Stream AttachStreamEncryptor(Stream s) { MemoryStream ms = new MemoryStream(); Debug.Assert(m_pbMasterSeed != null); Debug.Assert(m_pbMasterSeed.Length == 32); ms.Write(m_pbMasterSeed, 0, 32); Debug.Assert(m_pwDatabase != null); Debug.Assert(m_pwDatabase.MasterKey != null); ProtectedBinary pbinKey = m_pwDatabase.MasterKey.GenerateKey32( m_pbTransformSeed, m_pwDatabase.KeyEncryptionRounds); Debug.Assert(pbinKey != null); if (pbinKey == null) { throw new SecurityException(KLRes.InvalidCompositeKey); } byte[] pKey32 = pbinKey.ReadData(); if ((pKey32 == null) || (pKey32.Length != 32)) { throw new SecurityException(KLRes.InvalidCompositeKey); } ms.Write(pKey32, 0, 32); byte[] aesKey = Crypto.SHA256.ComputeHash(ms.ToArray()); ms.Close(); Array.Clear(pKey32, 0, 32); Debug.Assert(CipherPool.GlobalPool != null); ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid); if (iEngine == null) { throw new SecurityException(KLRes.FileUnknownCipher); } return(iEngine.EncryptStream(s, aesKey, m_pbEncryptionIV)); }
// 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 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(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); } }