private bool ReadInnerHeaderField(BinaryReaderEx br) { Debug.Assert(br != null); if (br == null) { throw new ArgumentNullException("br"); } byte btFieldID = br.ReadByte(); int cbSize = MemUtil.BytesToInt32(br.ReadBytes(4)); if (cbSize < 0) { throw new FormatException(KLRes.FileCorrupted); } byte[] pbData = MemUtil.EmptyByteArray; if (cbSize > 0) { pbData = br.ReadBytes(cbSize); } bool bResult = true; KdbxInnerHeaderFieldID kdbID = (KdbxInnerHeaderFieldID)btFieldID; switch (kdbID) { case KdbxInnerHeaderFieldID.EndOfHeader: bResult = false; // Returning false indicates end of header break; case KdbxInnerHeaderFieldID.InnerRandomStreamID: SetInnerRandomStreamID(pbData); break; case KdbxInnerHeaderFieldID.InnerRandomStreamKey: Debug.Assert(m_pbInnerRandomStreamKey == null); m_pbInnerRandomStreamKey = pbData; CryptoRandom.Instance.AddEntropy(pbData); break; case KdbxInnerHeaderFieldID.Binary: if (pbData.Length < 1) { throw new FormatException(); } KdbxBinaryFlags f = (KdbxBinaryFlags)pbData[0]; bool bProt = ((f & KdbxBinaryFlags.Protected) != KdbxBinaryFlags.None); ProtectedBinary pb = new ProtectedBinary(bProt, pbData, 1, pbData.Length - 1); Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication? m_pbsBinaries.Add(pb); if (bProt) { MemUtil.ZeroByteArray(pbData); } break; default: Debug.Assert(false); break; } return(bResult); }
/// <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 }
private bool ReadHeaderField(BinaryReaderEx brSource) { Debug.Assert(brSource != null); if (brSource == null) { throw new ArgumentNullException("brSource"); } byte btFieldID = brSource.ReadByte(); int cbSize; Debug.Assert(m_uFileVersion > 0); if (m_uFileVersion < FileVersion32_4) { cbSize = (int)MemUtil.BytesToUInt16(brSource.ReadBytes(2)); } else { cbSize = MemUtil.BytesToInt32(brSource.ReadBytes(4)); } if (cbSize < 0) { throw new FormatException(KLRes.FileCorrupted); } byte[] pbData = MemUtil.EmptyByteArray; if (cbSize > 0) { pbData = brSource.ReadBytes(cbSize); } bool bResult = true; KdbxHeaderFieldID kdbID = (KdbxHeaderFieldID)btFieldID; switch (kdbID) { case KdbxHeaderFieldID.EndOfHeader: bResult = false; // Returning false indicates end of header break; case KdbxHeaderFieldID.CipherID: SetCipher(pbData); break; case KdbxHeaderFieldID.CompressionFlags: SetCompressionFlags(pbData); break; case KdbxHeaderFieldID.MasterSeed: m_pbMasterSeed = pbData; CryptoRandom.Instance.AddEntropy(pbData); break; // Obsolete; for backward compatibility only case KdbxHeaderFieldID.TransformSeed: Debug.Assert(m_uFileVersion < FileVersion32_4); AesKdf kdfS = new AesKdf(); if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid)) { m_pwDatabase.KdfParameters = kdfS.GetDefaultParameters(); } // m_pbTransformSeed = pbData; m_pwDatabase.KdfParameters.SetByteArray(AesKdf.ParamSeed, pbData); CryptoRandom.Instance.AddEntropy(pbData); break; // Obsolete; for backward compatibility only case KdbxHeaderFieldID.TransformRounds: Debug.Assert(m_uFileVersion < FileVersion32_4); AesKdf kdfR = new AesKdf(); if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid)) { m_pwDatabase.KdfParameters = kdfR.GetDefaultParameters(); } // m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData); m_pwDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds, MemUtil.BytesToUInt64(pbData)); break; case KdbxHeaderFieldID.EncryptionIV: m_pbEncryptionIV = pbData; break; case KdbxHeaderFieldID.InnerRandomStreamKey: Debug.Assert(m_uFileVersion < FileVersion32_4); Debug.Assert(m_pbInnerRandomStreamKey == null); m_pbInnerRandomStreamKey = pbData; CryptoRandom.Instance.AddEntropy(pbData); break; case KdbxHeaderFieldID.StreamStartBytes: Debug.Assert(m_uFileVersion < FileVersion32_4); m_pbStreamStartBytes = pbData; break; case KdbxHeaderFieldID.InnerRandomStreamID: Debug.Assert(m_uFileVersion < FileVersion32_4); SetInnerRandomStreamID(pbData); break; case KdbxHeaderFieldID.KdfParameters: m_pwDatabase.KdfParameters = KdfParameters.DeserializeExt(pbData); break; case KdbxHeaderFieldID.PublicCustomData: Debug.Assert(m_pwDatabase.PublicCustomData.Count == 0); m_pwDatabase.PublicCustomData = VariantDictionary.Deserialize(pbData); break; default: Debug.Assert(false); if (m_slLogger != null) { m_slLogger.SetText(KLRes.UnknownHeaderId + ": " + kdbID.ToString() + "!", LogStatusType.Warning); } break; } return(bResult); }
private byte[] LoadHeader(BinaryReaderEx br) { string strPrevExcpText = br.ReadExceptionText; br.ReadExceptionText = KLRes.FileHeaderCorrupted + " " + KLRes.FileIncompleteExpc; MemoryStream msHeader = new MemoryStream(); Debug.Assert(br.CopyDataTo == null); br.CopyDataTo = msHeader; byte[] pbSig1 = br.ReadBytes(4); uint uSig1 = MemUtil.BytesToUInt32(pbSig1); byte[] pbSig2 = br.ReadBytes(4); uint uSig2 = MemUtil.BytesToUInt32(pbSig2); if ((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2)) { throw new OldFormatException(PwDefs.ShortProductName + @" 1.x", OldFormatException.OldFormatType.KeePass1x); } if ((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) { } else if ((uSig1 == FileSignaturePreRelease1) && (uSig2 == FileSignaturePreRelease2)) { } else { throw new FormatException(KLRes.FileSigInvalid); } byte[] pb = br.ReadBytes(4); uint uVer = MemUtil.BytesToUInt32(pb); uint uVerMajor = uVer & FileVersionCriticalMask; uint uVerMinor = uVer & ~FileVersionCriticalMask; const uint uVerMaxMajor = FileVersion32 & FileVersionCriticalMask; const uint uVerMaxMinor = FileVersion32 & ~FileVersionCriticalMask; if (uVerMajor > uVerMaxMajor) { throw new FormatException(KLRes.FileVersionUnsupported + MessageService.NewParagraph + KLRes.FileNewVerReq); } if ((uVerMajor == uVerMaxMajor) && (uVerMinor > uVerMaxMinor) && (g_fConfirmOpenUnkVer != null)) { if (!g_fConfirmOpenUnkVer()) { throw new OperationCanceledException(); } } m_uFileVersion = uVer; while (true) { if (!ReadHeaderField(br)) { break; } } br.CopyDataTo = null; byte[] pbHeader = msHeader.ToArray(); msHeader.Close(); br.ReadExceptionText = strPrevExcpText; return(pbHeader); }
/// <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 = StrUtil.Utf8; 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); readerStream.Close(); GC.KeepAlive(brDecrypted); GC.KeepAlive(br); } catch (CryptographicException) // Thrown on invalid padding { throw new CryptographicException(KLRes.FileCorrupted); } finally { CommonCleanUpRead(sSource, hashedStream); } }
private bool ReadHeaderField(BinaryReaderEx brSource) { Debug.Assert(brSource != null); if (brSource == null) { throw new ArgumentNullException("brSource"); } byte btFieldID = brSource.ReadByte(); ushort uSize = MemUtil.BytesToUInt16(brSource.ReadBytes(2)); byte[] pbData = null; if (uSize > 0) { string strPrevExcpText = brSource.ReadExceptionText; brSource.ReadExceptionText = KLRes.FileHeaderEndEarly; pbData = brSource.ReadBytes(uSize); brSource.ReadExceptionText = strPrevExcpText; } bool bResult = true; Kdb4HeaderFieldID kdbID = (Kdb4HeaderFieldID)btFieldID; switch (kdbID) { case Kdb4HeaderFieldID.EndOfHeader: bResult = false; // Returning false indicates end of header break; case Kdb4HeaderFieldID.CipherID: SetCipher(pbData); break; case Kdb4HeaderFieldID.CompressionFlags: SetCompressionFlags(pbData); break; case Kdb4HeaderFieldID.MasterSeed: m_pbMasterSeed = pbData; CryptoRandom.Instance.AddEntropy(pbData); break; case Kdb4HeaderFieldID.TransformSeed: m_pbTransformSeed = pbData; CryptoRandom.Instance.AddEntropy(pbData); break; case Kdb4HeaderFieldID.TransformRounds: m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData); break; case Kdb4HeaderFieldID.EncryptionIV: m_pbEncryptionIV = pbData; break; case Kdb4HeaderFieldID.ProtectedStreamKey: m_pbProtectedStreamKey = pbData; CryptoRandom.Instance.AddEntropy(pbData); break; case Kdb4HeaderFieldID.StreamStartBytes: m_pbStreamStartBytes = pbData; break; case Kdb4HeaderFieldID.InnerRandomStreamID: SetInnerRandomStreamID(pbData); break; default: Debug.Assert(false); if (m_slLogger != null) { m_slLogger.SetText(KLRes.UnknownHeaderId + @": " + kdbID.ToString() + "!", LogStatusType.Warning); } break; } return(bResult); }
/// <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); } }