Keepass Orig KeePass_160826

This commit is contained in:
Philipp Crocoll
2016-08-30 04:11:48 +02:00
parent 6d1e28e502
commit a2f2e3d6f8
43 changed files with 4888 additions and 877 deletions

View File

@@ -35,8 +35,10 @@ using System.IO.Compression;
using KeePassLibSD;
#endif
using KeePassLib.Collections;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Interfaces;
using KeePassLib.Keys;
using KeePassLib.Resources;
@@ -50,74 +52,108 @@ namespace KeePassLib.Serialization
public sealed partial class KdbxFile
{
/// <summary>
/// Load a KDB file from a file.
/// Load a KDBX file.
/// </summary>
/// <param name="strFilePath">File to load.</param>
/// <param name="kdbFormat">Format specifier.</param>
/// <param name="fmt">Format.</param>
/// <param name="slLogger">Status logger (optional).</param>
public void Load(string strFilePath, KdbxFormat kdbFormat, IStatusLogger slLogger)
public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
{
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
Load(IOConnection.OpenRead(ioc), kdbFormat, slLogger);
Load(IOConnection.OpenRead(ioc), fmt, slLogger);
}
/// <summary>
/// Load a KDB file from a stream.
/// 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="kdbFormat">Format specifier.</param>
/// <param name="fmt">Format.</param>
/// <param name="slLogger">Status logger (optional).</param>
public void Load(Stream sSource, KdbxFormat kdbFormat, IStatusLogger slLogger)
public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
{
Debug.Assert(sSource != null);
if(sSource == null) throw new ArgumentNullException("sSource");
m_format = kdbFormat;
m_format = fmt;
m_slLogger = slLogger;
HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null);
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
{
BinaryReaderEx br = null;
BinaryReaderEx brDecrypted = null;
Stream readerStream = null;
if(kdbFormat == KdbxFormat.Default)
Stream sXml;
if(fmt == KdbxFormat.Default)
{
br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
ReadHeader(br);
BinaryReaderEx br = new BinaryReaderEx(sHashing,
encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
Stream sDecrypted = AttachStreamDecryptor(hashedStream);
if((sDecrypted == null) || (sDecrypted == hashedStream))
throw new SecurityException(KLRes.CryptoStreamFailed);
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new InvalidDataException();
for(int iStart = 0; iStart < 32; ++iStart)
Stream sPlain;
if(m_uFileVersion <= FileVersion32_3)
{
if(pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart])
throw new InvalidCompositeKeyException();
}
Stream sDecrypted = EncryptStream(sHashing, iCipher,
pbCipherKey, cbEncIV, false);
if((sDecrypted == null) || (sDecrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
lStreams.Add(sDecrypted);
Stream sHashed = new HashedBlockStream(sDecrypted, false, 0,
!m_bRepairMode);
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
encNoBom, KLRes.FileCorrupted);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new InvalidDataException();
if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
throw new InvalidCompositeKeyException();
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
}
else // KDBX >= 4
{
byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
if((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
throw new InvalidDataException();
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)
readerStream = new GZipStream(sHashed, CompressionMode.Decompress);
else readerStream = sHashed;
{
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
lStreams.Add(sXml);
}
else sXml = sPlain;
}
else if(kdbFormat == KdbxFormat.PlainXml)
readerStream = hashedStream;
else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
else if(fmt == KdbxFormat.PlainXml)
sXml = sHashing;
else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
if(kdbFormat != KdbxFormat.PlainXml) // Is an encrypted format
if(fmt == KdbxFormat.Default)
{
if(m_pbProtectedStreamKey == null)
{
@@ -137,7 +173,7 @@ namespace KeePassLib.Serialization
// {
// while(true)
// {
// int b = readerStream.ReadByte();
// int b = sXml.ReadByte();
// if(b == -1) break;
// fsOut.WriteByte((byte)b);
// }
@@ -146,26 +182,29 @@ namespace KeePassLib.Serialization
// fsOut.Close();
#endif
ReadXmlStreamed(readerStream, hashedStream);
// ReadXmlDom(readerStream);
readerStream.Close();
// GC.KeepAlive(br);
// GC.KeepAlive(brDecrypted);
ReadXmlStreamed(sXml, sHashing);
// ReadXmlDom(sXml);
}
catch(CryptographicException) // Thrown on invalid padding
{
throw new CryptographicException(KLRes.FileCorrupted);
}
finally { CommonCleanUpRead(sSource, hashedStream); }
finally
{
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
CommonCleanUpRead(lStreams, sHashing);
}
}
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
private void CommonCleanUpRead(List<Stream> lStreams, HashingStreamEx sHashing)
{
hashedStream.Close();
m_pbHashOfFileOnDisk = hashedStream.Hash;
CloseStreams(lStreams);
sSource.Close();
Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
m_pbHashOfFileOnDisk = sHashing.Hash;
Debug.Assert(m_pbHashOfFileOnDisk != null);
// Reset memory protection settings (to always use reasonable
// defaults)
@@ -187,7 +226,7 @@ namespace KeePassLib.Serialization
m_pbHashOfHeader = null;
}
private void ReadHeader(BinaryReaderEx br)
private byte[] LoadHeader(BinaryReaderEx br)
{
MemoryStream msHeader = new MemoryStream();
Debug.Assert(br.CopyDataTo == null);
@@ -212,18 +251,19 @@ namespace KeePassLib.Serialization
if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
throw new FormatException(KLRes.FileVersionUnsupported +
MessageService.NewParagraph + KLRes.FileNewVerReq);
m_uFileVersion = uVersion;
while(true)
{
if(ReadHeaderField(br) == false)
break;
if(!ReadHeaderField(br)) break;
}
br.CopyDataTo = null;
byte[] pbHeader = msHeader.ToArray();
msHeader.Close();
SHA256Managed sha256 = new SHA256Managed();
m_pbHashOfHeader = sha256.ComputeHash(pbHeader);
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
return pbHeader;
}
private bool ReadHeaderField(BinaryReaderEx brSource)
@@ -232,15 +272,21 @@ namespace KeePassLib.Serialization
if(brSource == null) throw new ArgumentNullException("brSource");
byte btFieldID = brSource.ReadByte();
ushort uSize = MemUtil.BytesToUInt16(brSource.ReadBytes(2));
byte[] pbData = null;
if(uSize > 0)
int cbSize;
Debug.Assert(m_uFileVersion > 0);
if(m_uFileVersion <= FileVersion32_3)
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)
{
string strPrevExcpText = brSource.ReadExceptionText;
brSource.ReadExceptionText = KLRes.FileHeaderEndEarly;
pbData = brSource.ReadBytes(uSize);
pbData = brSource.ReadBytes(cbSize);
brSource.ReadExceptionText = strPrevExcpText;
}
@@ -266,13 +312,27 @@ namespace KeePassLib.Serialization
CryptoRandom.Instance.AddEntropy(pbData);
break;
// Obsolete; for backward compatibility only
case KdbxHeaderFieldID.TransformSeed:
m_pbTransformSeed = pbData;
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:
m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
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:
@@ -285,6 +345,7 @@ namespace KeePassLib.Serialization
break;
case KdbxHeaderFieldID.StreamStartBytes:
Debug.Assert(m_uFileVersion <= FileVersion32_3);
m_pbStreamStartBytes = pbData;
break;
@@ -292,6 +353,15 @@ namespace KeePassLib.Serialization
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)
@@ -305,7 +375,7 @@ namespace KeePassLib.Serialization
private void SetCipher(byte[] pbID)
{
if((pbID == null) || (pbID.Length != 16))
if((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
throw new FormatException(KLRes.FileUnknownCipher);
m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
@@ -329,35 +399,6 @@ namespace KeePassLib.Serialization
m_craInnerRandomStream = (CrsAlgorithm)uID;
}
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);
}
[Obsolete]
public static List<PwEntry> ReadEntries(PwDatabase pwDatabase, Stream msData)
{