Keepass original source code version 2.34

This commit is contained in:
Philipp Crocoll
2016-08-30 04:09:53 +02:00
parent 3585d4f61f
commit 6d1e28e502
84 changed files with 6370 additions and 1972 deletions

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,10 +18,16 @@
*/
using System;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Threading;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Native;
using KeePassLib.Utility;
#if KeePassLibSD
@@ -30,6 +36,17 @@ using KeePassLibSD;
namespace KeePassLib.Security
{
[Flags]
public enum PbCryptFlags
{
None = 0,
Encrypt = 1,
Decrypt = 2
}
public delegate void PbCryptDelegate(byte[] pbData, PbCryptFlags cf,
long lID);
/// <summary>
/// Represents a protected binary, i.e. a byte array that is encrypted
/// in memory. A <c>ProtectedBinary</c> object is immutable and
@@ -37,26 +54,98 @@ namespace KeePassLib.Security
/// </summary>
public sealed class ProtectedBinary : IEquatable<ProtectedBinary>
{
private const int PmBlockSize = 16;
private const int BlockSize = 16;
// In-memory protection is supported only on Windows 2000 SP3 and
// higher.
private static bool m_bProtectionSupported;
private static PbCryptDelegate g_fExtCrypt = null;
/// <summary>
/// A plugin can provide a custom memory protection method
/// by assigning a non-null delegate to this property.
/// </summary>
public static PbCryptDelegate ExtCrypt
{
get { return g_fExtCrypt; }
set { g_fExtCrypt = value; }
}
// Local copy of the delegate that was used for encryption,
// in order to allow correct decryption even when the global
// delegate changes
private PbCryptDelegate m_fExtCrypt = null;
private enum PbMemProt
{
None = 0,
ProtectedMemory,
Salsa20,
ExtCrypt
}
// ProtectedMemory is supported only on Windows 2000 SP3 and higher
#if !KeePassLibSD
private static bool? g_obProtectedMemorySupported = null;
#endif
private static bool ProtectedMemorySupported
{
get
{
#if KeePassLibSD
return false;
#else
bool? ob = g_obProtectedMemorySupported;
if(ob.HasValue) return ob.Value;
// Mono does not implement any encryption for ProtectedMemory;
// https://sourceforge.net/p/keepass/feature-requests/1907/
if(NativeLib.IsUnix())
{
g_obProtectedMemorySupported = false;
return false;
}
ob = false;
try // Test whether ProtectedMemory is supported
{
// BlockSize * 3 in order to test encryption for multiple
// blocks, but not introduce a power of 2 as factor
byte[] pb = new byte[ProtectedBinary.BlockSize * 3];
for(int i = 0; i < pb.Length; ++i) pb[i] = (byte)i;
ProtectedMemory.Protect(pb, MemoryProtectionScope.SameProcess);
for(int i = 0; i < pb.Length; ++i)
{
if(pb[i] != (byte)i) { ob = true; break; }
}
}
catch(Exception) { } // Windows 98 / ME
g_obProtectedMemorySupported = ob;
return ob.Value;
#endif
}
}
private static long g_lCurID = 0;
private long m_lID;
private byte[] m_pbData; // Never null
// The real length of the data. This value can be different than
// The real length of the data; this value can be different from
// m_pbData.Length, as the length of m_pbData always is a multiple
// of PmBlockSize (required for fast in-memory protection).
// of BlockSize (required for ProtectedMemory)
private uint m_uDataLen;
private bool m_bProtected;
private bool m_bProtected; // Protection requested by the caller
private PbMemProt m_mp = PbMemProt.None; // Actual protection
private object m_objSync = new object();
private static byte[] g_pbKey32 = null;
/// <summary>
/// A flag specifying whether the <c>ProtectedBinary</c> object has
/// turned on in-memory protection or not.
/// turned on memory protection or not.
/// </summary>
public bool IsProtected
{
@@ -71,23 +160,9 @@ namespace KeePassLib.Security
get { return m_uDataLen; }
}
static ProtectedBinary()
{
try // Test whether ProtectedMemory is supported
{
byte[] pbDummy = new byte[PmBlockSize * 2];
ProtectedMemory.Protect(pbDummy, MemoryProtectionScope.SameProcess);
m_bProtectionSupported = true;
}
catch(Exception) // Windows 98 / ME
{
m_bProtectionSupported = false;
}
}
/// <summary>
/// Construct a new, empty protected binary data object. Protection
/// is disabled.
/// Construct a new, empty protected binary data object.
/// Protection is disabled.
/// </summary>
public ProtectedBinary()
{
@@ -116,34 +191,103 @@ namespace KeePassLib.Security
/// <param name="bEnableProtection">Enable protection or not.</param>
/// <param name="xbProtected"><c>XorredBuffer</c> object used to
/// initialize the <c>ProtectedBinary</c> object.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public ProtectedBinary(bool bEnableProtection, XorredBuffer xbProtected)
{
Debug.Assert(xbProtected != null); if(xbProtected == null) throw new ArgumentNullException("xbProtected");
Debug.Assert(xbProtected != null);
if(xbProtected == null) throw new ArgumentNullException("xbProtected");
byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb);
MemUtil.ZeroByteArray(pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
}
private void Init(bool bEnableProtection, byte[] pbData)
{
if(pbData == null) throw new ArgumentNullException("pbData");
#if KeePassLibSD
m_lID = ++g_lCurID;
#else
m_lID = Interlocked.Increment(ref g_lCurID);
#endif
m_bProtected = bEnableProtection;
m_uDataLen = (uint)pbData.Length;
int nBlocks = (int)m_uDataLen / PmBlockSize;
if((nBlocks * PmBlockSize) < (int)m_uDataLen) ++nBlocks;
Debug.Assert((nBlocks * PmBlockSize) >= (int)m_uDataLen);
const int bs = ProtectedBinary.BlockSize;
int nBlocks = (int)m_uDataLen / bs;
if((nBlocks * bs) < (int)m_uDataLen) ++nBlocks;
Debug.Assert((nBlocks * bs) >= (int)m_uDataLen);
m_pbData = new byte[nBlocks * PmBlockSize];
m_pbData = new byte[nBlocks * bs];
Array.Copy(pbData, m_pbData, (int)m_uDataLen);
// Data size must be > 0, otherwise 'Protect' throws
if(m_bProtected && m_bProtectionSupported && (m_uDataLen > 0))
Encrypt();
}
private void Encrypt()
{
Debug.Assert(m_mp == PbMemProt.None);
// Nothing to do if caller didn't request protection
if(!m_bProtected) return;
// ProtectedMemory.Protect throws for data size == 0
if(m_pbData.Length == 0) return;
PbCryptDelegate f = g_fExtCrypt;
if(f != null)
{
f(m_pbData, PbCryptFlags.Encrypt, m_lID);
m_fExtCrypt = f;
m_mp = PbMemProt.ExtCrypt;
return;
}
if(ProtectedBinary.ProtectedMemorySupported)
{
ProtectedMemory.Protect(m_pbData, MemoryProtectionScope.SameProcess);
m_mp = PbMemProt.ProtectedMemory;
return;
}
byte[] pbKey32 = g_pbKey32;
if(pbKey32 == null)
{
pbKey32 = CryptoRandom.Instance.GetRandomBytes(32);
byte[] pbUpd = Interlocked.Exchange<byte[]>(ref g_pbKey32, pbKey32);
if(pbUpd != null) pbKey32 = pbUpd;
}
Salsa20Cipher s = new Salsa20Cipher(pbKey32,
BitConverter.GetBytes(m_lID));
s.Encrypt(m_pbData, m_pbData.Length, true);
s.Dispose();
m_mp = PbMemProt.Salsa20;
}
private void Decrypt()
{
if(m_pbData.Length == 0) return;
if(m_mp == PbMemProt.ProtectedMemory)
ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess);
else if(m_mp == PbMemProt.Salsa20)
{
Salsa20Cipher s = new Salsa20Cipher(g_pbKey32,
BitConverter.GetBytes(m_lID));
s.Encrypt(m_pbData, m_pbData.Length, true);
s.Dispose();
}
else if(m_mp == PbMemProt.ExtCrypt)
m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID);
else { Debug.Assert(m_mp == PbMemProt.None); }
m_mp = PbMemProt.None;
}
/// <summary>
@@ -160,16 +304,12 @@ namespace KeePassLib.Security
byte[] pbReturn = new byte[m_uDataLen];
if(m_bProtected && m_bProtectionSupported)
lock(m_objSync)
{
lock(m_objSync)
{
ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess);
Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
ProtectedMemory.Protect(m_pbData, MemoryProtectionScope.SameProcess);
}
Decrypt();
Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
Encrypt();
}
else Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
return pbReturn;
}
@@ -179,9 +319,6 @@ namespace KeePassLib.Security
/// of bytes generated by a random stream.
/// </summary>
/// <param name="crsRandomSource">Random number source.</param>
/// <returns>Protected data.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public byte[] ReadXorredData(CryptoRandomStream crsRandomSource)
{
Debug.Assert(crsRandomSource != null);
@@ -191,7 +328,7 @@ namespace KeePassLib.Security
uint uLen = (uint)pbData.Length;
byte[] randomPad = crsRandomSource.GetRandomBytes(uLen);
Debug.Assert(randomPad.Length == uLen);
Debug.Assert(randomPad.Length == pbData.Length);
for(uint i = 0; i < uLen; ++i)
pbData[i] ^= randomPad[i];
@@ -199,8 +336,11 @@ namespace KeePassLib.Security
return pbData;
}
private int? m_hash = null;
public override int GetHashCode()
{
if(m_hash.HasValue) return m_hash.Value;
int h = (m_bProtected ? 0x7B11D289 : 0);
byte[] pb = ReadData();
@@ -211,6 +351,7 @@ namespace KeePassLib.Security
}
MemUtil.ZeroByteArray(pb);
m_hash = h;
return h;
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,8 +18,8 @@
*/
using System;
using System.Text;
using System.Diagnostics;
using System.Text;
using KeePassLib.Cryptography;
using KeePassLib.Utility;
@@ -47,7 +47,7 @@ namespace KeePassLib.Security
private bool m_bIsProtected;
private static ProtectedString m_psEmpty = new ProtectedString();
private static readonly ProtectedString m_psEmpty = new ProtectedString();
public static ProtectedString Empty
{
get { return m_psEmpty; }
@@ -55,7 +55,7 @@ namespace KeePassLib.Security
/// <summary>
/// A flag specifying whether the <c>ProtectedString</c> object
/// has turned on in-memory protection or not.
/// has turned on memory protection or not.
/// </summary>
public bool IsProtected
{
@@ -112,10 +112,9 @@ namespace KeePassLib.Security
/// to the value supplied in the parameters.
/// </summary>
/// <param name="bEnableProtection">If this parameter is <c>true</c>,
/// the string will be protected in-memory (encrypted). If it
/// the string will be protected in memory (encrypted). If it
/// is <c>false</c>, the string will be stored as plain-text.</param>
/// <param name="strValue">The initial string value. This
/// parameter won't be modified.</param>
/// <param name="strValue">The initial string value.</param>
public ProtectedString(bool bEnableProtection, string strValue)
{
Init(bEnableProtection, strValue);
@@ -126,7 +125,7 @@ namespace KeePassLib.Security
/// to the value supplied in the parameters (UTF-8 encoded string).
/// </summary>
/// <param name="bEnableProtection">If this parameter is <c>true</c>,
/// the string will be protected in-memory (encrypted). If it
/// the string will be protected in memory (encrypted). If it
/// is <c>false</c>, the string will be stored as plain-text.</param>
/// <param name="vUtf8Value">The initial string value, encoded as
/// UTF-8 byte array. This parameter won't be modified; the caller
@@ -144,15 +143,15 @@ namespace KeePassLib.Security
/// <param name="xbProtected"><c>XorredBuffer</c> object containing the
/// string in UTF-8 representation. The UTF-8 string must not
/// be <c>null</c>-terminated.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public ProtectedString(bool bEnableProtection, XorredBuffer xbProtected)
{
Debug.Assert(xbProtected != null);
if(xbProtected == null) throw new ArgumentNullException("xbProtected");
byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb);
MemUtil.ZeroByteArray(pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
}
private void Init(bool bEnableProtection, string str)
@@ -222,8 +221,6 @@ namespace KeePassLib.Security
/// </summary>
/// <param name="crsRandomSource">Random number source.</param>
/// <returns>Protected string.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
{
Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource");
@@ -232,7 +229,7 @@ namespace KeePassLib.Security
uint uLen = (uint)pbData.Length;
byte[] randomPad = crsRandomSource.GetRandomBytes(uLen);
Debug.Assert(randomPad.Length == uLen);
Debug.Assert(randomPad.Length == pbData.Length);
for(uint i = 0; i < uLen; ++i)
pbData[i] ^= randomPad[i];
@@ -246,7 +243,103 @@ namespace KeePassLib.Security
byte[] pb = ReadUtf8();
ProtectedString ps = new ProtectedString(bProtect, pb);
MemUtil.ZeroByteArray(pb);
if(bProtect) MemUtil.ZeroByteArray(pb);
return ps;
}
public ProtectedString Insert(int iStart, string strInsert)
{
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(strInsert == null) throw new ArgumentNullException("strInsert");
if(strInsert.Length == 0) return this;
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Insert(
iStart, strInsert));
UTF8Encoding utf8 = StrUtil.Utf8;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
try
{
if(iStart > v.Length)
throw new ArgumentOutOfRangeException("iStart");
char[] vIns = strInsert.ToCharArray();
vNew = new char[v.Length + vIns.Length];
Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(vIns, 0, vNew, iStart, vIns.Length);
Array.Copy(v, iStart, vNew, iStart + vIns.Length,
v.Length - iStart);
}
finally
{
Array.Clear(v, 0, v.Length);
MemUtil.ZeroByteArray(pb);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
Array.Clear(vNew, 0, vNew.Length);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
public ProtectedString Remove(int iStart, int nCount)
{
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return this;
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Remove(
iStart, nCount));
UTF8Encoding utf8 = StrUtil.Utf8;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
try
{
if((iStart + nCount) > v.Length)
throw new ArgumentException("iStart + nCount");
vNew = new char[v.Length - nCount];
Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
(iStart + nCount));
}
finally
{
Array.Clear(v, 0, v.Length);
MemUtil.ZeroByteArray(pb);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
Array.Clear(vNew, 0, vNew.Length);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by