Merge branch 'Keepass_Orig' + adapt to changes in KeePassLib. This introduced KDBX4 in Keepass2Android.

NOTE: seems like merging broke the capability to read KDBX<=3.

Conflicts:
	src/KeePassLib2Android/Collections/AutoTypeConfig.cs
	src/KeePassLib2Android/Collections/ProtectedBinaryDictionary.cs
	src/KeePassLib2Android/Collections/ProtectedStringDictionary.cs
	src/KeePassLib2Android/Collections/PwObjectList.cs
	src/KeePassLib2Android/Collections/PwObjectPool.cs
	src/KeePassLib2Android/Collections/StringDictionaryEx.cs
	src/KeePassLib2Android/Cryptography/Cipher/CipherPool.cs
	src/KeePassLib2Android/Cryptography/Cipher/ICipherEngine.cs
	src/KeePassLib2Android/Cryptography/Cipher/Salsa20Cipher.cs
	src/KeePassLib2Android/Cryptography/Cipher/StandardAesEngine.cs
	src/KeePassLib2Android/Cryptography/CryptoRandom.cs
	src/KeePassLib2Android/Cryptography/CryptoRandomStream.cs
	src/KeePassLib2Android/Cryptography/HashingStreamEx.cs
	src/KeePassLib2Android/Cryptography/HmacOtp.cs
	src/KeePassLib2Android/Cryptography/PasswordGenerator/CharSetBasedGenerator.cs
	src/KeePassLib2Android/Cryptography/PasswordGenerator/CustomPwGenerator.cs
	src/KeePassLib2Android/Cryptography/PasswordGenerator/CustomPwGeneratorPool.cs
	src/KeePassLib2Android/Cryptography/PasswordGenerator/PatternBasedGenerator.cs
	src/KeePassLib2Android/Cryptography/PasswordGenerator/PwCharSet.cs
	src/KeePassLib2Android/Cryptography/PasswordGenerator/PwGenerator.cs
	src/KeePassLib2Android/Cryptography/PasswordGenerator/PwProfile.cs
	src/KeePassLib2Android/Cryptography/PopularPasswords.cs
	src/KeePassLib2Android/Cryptography/QualityEstimation.cs
	src/KeePassLib2Android/Cryptography/SelfTest.cs
	src/KeePassLib2Android/Delegates/Handlers.cs
	src/KeePassLib2Android/Interfaces/IDeepCloneable.cs
	src/KeePassLib2Android/Interfaces/IStatusLogger.cs
	src/KeePassLib2Android/Interfaces/IStructureItem.cs
	src/KeePassLib2Android/Interfaces/ITimeLogger.cs
	src/KeePassLib2Android/Interfaces/IUIOperations.cs
	src/KeePassLib2Android/Interfaces/IXmlSerializerEx.cs
	src/KeePassLib2Android/Keys/CompositeKey.cs
	src/KeePassLib2Android/Keys/IUserKey.cs
	src/KeePassLib2Android/Keys/KcpCustomKey.cs
	src/KeePassLib2Android/Keys/KcpKeyFile.cs
	src/KeePassLib2Android/Keys/KcpPassword.cs
	src/KeePassLib2Android/Keys/KcpUserAccount.cs
	src/KeePassLib2Android/Keys/KeyProvider.cs
	src/KeePassLib2Android/Keys/KeyProviderPool.cs
	src/KeePassLib2Android/Keys/KeyValidator.cs
	src/KeePassLib2Android/Keys/KeyValidatorPool.cs
	src/KeePassLib2Android/Keys/UserKeyType.cs
	src/KeePassLib2Android/Native/NativeLib.cs
	src/KeePassLib2Android/Native/NativeMethods.cs
	src/KeePassLib2Android/Properties/AssemblyInfo.cs
	src/KeePassLib2Android/PwCustomIcon.cs
	src/KeePassLib2Android/PwDatabase.cs
	src/KeePassLib2Android/PwDefs.cs
	src/KeePassLib2Android/PwDeletedObject.cs
	src/KeePassLib2Android/PwEntry.cs
	src/KeePassLib2Android/PwEnums.cs
	src/KeePassLib2Android/PwGroup.cs
	src/KeePassLib2Android/PwUuid.cs
	src/KeePassLib2Android/Resources/KLRes.Generated.cs
	src/KeePassLib2Android/Security/ProtectedBinary.cs
	src/KeePassLib2Android/Security/ProtectedString.cs
	src/KeePassLib2Android/Security/XorredBuffer.cs
	src/KeePassLib2Android/Serialization/BinaryReaderEx.cs
	src/KeePassLib2Android/Serialization/FileLock.cs
	src/KeePassLib2Android/Serialization/FileTransactionEx.cs
	src/KeePassLib2Android/Serialization/HashedBlockStream.cs
	src/KeePassLib2Android/Serialization/IOConnection.cs
	src/KeePassLib2Android/Serialization/IOConnectionInfo.cs
	src/KeePassLib2Android/Serialization/KdbxFile.Read.Streamed.cs
	src/KeePassLib2Android/Serialization/KdbxFile.Read.cs
	src/KeePassLib2Android/Serialization/KdbxFile.Write.cs
	src/KeePassLib2Android/Serialization/KdbxFile.cs
	src/KeePassLib2Android/Serialization/OldFormatException.cs
	src/KeePassLib2Android/Translation/KPControlCustomization.cs
	src/KeePassLib2Android/Translation/KPFormCustomization.cs
	src/KeePassLib2Android/Translation/KPStringTable.cs
	src/KeePassLib2Android/Translation/KPStringTableItem.cs
	src/KeePassLib2Android/Translation/KPTranslation.cs
	src/KeePassLib2Android/Translation/KPTranslationProperties.cs
	src/KeePassLib2Android/Utility/AppLogEx.cs
	src/KeePassLib2Android/Utility/GfxUtil.cs
	src/KeePassLib2Android/Utility/MemUtil.cs
	src/KeePassLib2Android/Utility/MessageService.cs
	src/KeePassLib2Android/Utility/StrUtil.cs
	src/KeePassLib2Android/Utility/TimeUtil.cs
	src/KeePassLib2Android/Utility/UrlUtil.cs
This commit is contained in:
Philipp Crocoll 2016-08-31 06:55:53 +02:00
commit 2e8c76d0c4
73 changed files with 7914 additions and 1981 deletions

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -194,13 +194,6 @@ namespace KeePassLib.Collections
return true;
}
public void Add(AutoTypeAssociation a)
{
Debug.Assert(a != null); if(a == null) throw new ArgumentNullException("a");
m_lWindowAssocs.Add(a);
}
public AutoTypeAssociation GetAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
@ -209,6 +202,22 @@ namespace KeePassLib.Collections
return m_lWindowAssocs[iIndex];
}
public void Add(AutoTypeAssociation a)
{
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Add(a);
}
public void Insert(int iIndex, AutoTypeAssociation a)
{
if((iIndex < 0) || (iIndex > m_lWindowAssocs.Count))
throw new ArgumentOutOfRangeException("iIndex");
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
m_lWindowAssocs.Insert(iIndex, a);
}
public void RemoveAt(int iIndex)
{
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
@ -216,5 +225,20 @@ namespace KeePassLib.Collections
m_lWindowAssocs.RemoveAt(iIndex);
}
// public void Sort()
// {
// m_lWindowAssocs.Sort(AutoTypeConfig.AssocCompareFn);
// }
// private static int AssocCompareFn(AutoTypeAssociation x,
// AutoTypeAssociation y)
// {
// if(x == null) { Debug.Assert(false); return ((y == null) ? 0 : -1); }
// if(y == null) { Debug.Assert(false); return 1; }
// int cn = x.WindowName.CompareTo(y.WindowName);
// if(cn != 0) return cn;
// return x.Sequence.CompareTo(y.Sequence);
// }
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -25,7 +25,6 @@ using System.Diagnostics;
using KeePassLib.Interfaces;
using KeePassLib.Security;
using KeePassLib.Utility;
#if KeePassLibSD
using KeePassLibSD;

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -284,11 +284,7 @@ namespace KeePassLib.Collections
public List<string> GetKeys()
{
List<string> v = new List<string>();
foreach(string strKey in m_vStrings.Keys) v.Add(strKey);
return v;
return new List<string>(m_vStrings.Keys);
}
public void EnableProtection(string strField, bool bProtect)
@ -300,7 +296,8 @@ namespace KeePassLib.Collections
{
byte[] pbData = ps.ReadUtf8();
Set(strField, new ProtectedString(bProtect, pbData));
MemUtil.ZeroByteArray(pbData);
if(bProtect) MemUtil.ZeroByteArray(pbData);
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -233,7 +233,7 @@ namespace KeePassLib.Collections
if(nCount <= 1) return;
int nIndex = m_vObjects.IndexOf(tObject);
Debug.Assert(nIndex >= 0);
if(nIndex < 0) { Debug.Assert(false); return; }
if(bUp && (nIndex > 0)) // No assert for top item
{
@ -249,6 +249,68 @@ namespace KeePassLib.Collections
}
}
public void MoveOne(T[] vObjects, bool bUp)
{
Debug.Assert(vObjects != null);
if(vObjects == null) throw new ArgumentNullException("vObjects");
/// <summary>
List<int> lIndices = new List<int>();
foreach(T t in vObjects)
{
if(t == null) { Debug.Assert(false); continue; }
/// Move some of the objects in this list to the top/bottom.
int p = IndexOf(t);
if(p >= 0) lIndices.Add(p);
else { Debug.Assert(false); }
}
/// </summary>
MoveOne(lIndices.ToArray(), bUp);
}
/// <param name="vObjects">List of objects to be moved.</param>
public void MoveOne(int[] vIndices, bool bUp)
{
Debug.Assert(vIndices != null);
if(vIndices == null) throw new ArgumentNullException("vIndices");
/// <param name="bTop">Move to top. If <c>false</c>, move to bottom.</param>
int n = m_vObjects.Count;
if(n <= 1) return; // No moving possible
int m = vIndices.Length;
if(m == 0) return; // Nothing to move
int[] v = new int[m];
Array.Copy(vIndices, v, m);
Array.Sort<int>(v);
if((bUp && (v[0] <= 0)) || (!bUp && (v[m - 1] >= (n - 1))))
return; // Moving as a block is not possible
int iStart = (bUp ? 0 : (m - 1));
int iExcl = (bUp ? m : -1);
int iStep = (bUp ? 1 : -1);
for(int i = iStart; i != iExcl; i += iStep)
{
int p = v[i];
if((p < 0) || (p >= n)) { Debug.Assert(false); continue; }
T t = m_vObjects[p];
if(bUp)
{
Debug.Assert(p > 0);
m_vObjects.RemoveAt(p);
m_vObjects.Insert(p - 1, t);
}
else // Down
{
Debug.Assert(p < (n - 1));
m_vObjects.RemoveAt(p);
m_vObjects.Insert(p + 1, t);
}
}
}
/// <summary>
/// Move some of the objects in this list to the top/bottom.
/// </summary>

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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,6 +18,7 @@
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
@ -77,4 +78,154 @@ namespace KeePassLib.Collections
return true;
}
}
internal sealed class PwObjectPoolEx
{
private Dictionary<PwUuid, ulong> m_dUuidToId =
new Dictionary<PwUuid, ulong>();
private Dictionary<ulong, IStructureItem> m_dIdToItem =
new Dictionary<ulong, IStructureItem>();
private PwObjectPoolEx()
{
}
public static PwObjectPoolEx FromGroup(PwGroup pg)
{
PwObjectPoolEx p = new PwObjectPoolEx();
if(pg == null) { Debug.Assert(false); return p; }
ulong uFreeId = 2; // 0 = "not found", 1 is a hole
p.m_dUuidToId[pg.Uuid] = uFreeId;
p.m_dIdToItem[uFreeId] = pg;
uFreeId += 2; // Make hole
p.AddGroupRec(pg, ref uFreeId);
return p;
}
private void AddGroupRec(PwGroup pg, ref ulong uFreeId)
{
if(pg == null) { Debug.Assert(false); return; }
ulong uId = uFreeId;
// Consecutive entries must have consecutive IDs
foreach(PwEntry pe in pg.Entries)
{
Debug.Assert(!m_dUuidToId.ContainsKey(pe.Uuid));
Debug.Assert(!m_dIdToItem.ContainsValue(pe));
m_dUuidToId[pe.Uuid] = uId;
m_dIdToItem[uId] = pe;
++uId;
}
++uId; // Make hole
// Consecutive groups must have consecutive IDs
foreach(PwGroup pgSub in pg.Groups)
{
Debug.Assert(!m_dUuidToId.ContainsKey(pgSub.Uuid));
Debug.Assert(!m_dIdToItem.ContainsValue(pgSub));
m_dUuidToId[pgSub.Uuid] = uId;
m_dIdToItem[uId] = pgSub;
++uId;
}
++uId; // Make hole
foreach(PwGroup pgSub in pg.Groups)
{
AddGroupRec(pgSub, ref uId);
}
uFreeId = uId;
}
public ulong GetIdByUuid(PwUuid pwUuid)
{
if(pwUuid == null) { Debug.Assert(false); return 0; }
ulong uId;
m_dUuidToId.TryGetValue(pwUuid, out uId);
return uId;
}
public IStructureItem GetItemByUuid(PwUuid pwUuid)
{
if(pwUuid == null) { Debug.Assert(false); return null; }
ulong uId;
if(!m_dUuidToId.TryGetValue(pwUuid, out uId)) return null;
Debug.Assert(uId != 0);
return GetItemById(uId);
}
public IStructureItem GetItemById(ulong uId)
{
IStructureItem p;
m_dIdToItem.TryGetValue(uId, out p);
return p;
}
}
internal sealed class PwObjectBlock<T> : IEnumerable<T>
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
{
private List<T> m_l = new List<T>();
public T PrimaryItem
{
get { return ((m_l.Count > 0) ? m_l[0] : null); }
}
private DateTime m_dtLocationChanged = DateTime.MinValue;
public DateTime LocationChanged
{
get { return m_dtLocationChanged; }
}
private PwObjectPoolEx m_poolAssoc = null;
public PwObjectPoolEx PoolAssoc
{
get { return m_poolAssoc; }
}
public PwObjectBlock()
{
}
#if DEBUG
public override string ToString()
{
return ("PwObjectBlock, Count = " + m_l.Count.ToString());
}
#endif
IEnumerator IEnumerable.GetEnumerator()
{
return m_l.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return m_l.GetEnumerator();
}
public void Add(T t, DateTime dtLoc, PwObjectPoolEx pool)
{
if(t == null) { Debug.Assert(false); return; }
m_l.Add(t);
if(dtLoc > m_dtLocationChanged)
{
m_dtLocationChanged = dtLoc;
m_poolAssoc = pool;
}
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -32,14 +32,14 @@ using KeePassLibSD;
namespace KeePassLib.Collections
{
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
IEnumerable<KeyValuePair<string, string>>
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
{
private SortedDictionary<string, string> m_vDict =
private SortedDictionary<string, string> m_dict =
new SortedDictionary<string, string>();
public int Count
{
get { return m_vDict.Count; }
get { return m_dict.Count; }
}
public StringDictionaryEx()
@ -48,39 +48,53 @@ namespace KeePassLib.Collections
IEnumerator IEnumerable.GetEnumerator()
{
return m_vDict.GetEnumerator();
return m_dict.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return m_vDict.GetEnumerator();
return m_dict.GetEnumerator();
}
public StringDictionaryEx CloneDeep()
{
StringDictionaryEx plNew = new StringDictionaryEx();
StringDictionaryEx sdNew = new StringDictionaryEx();
foreach(KeyValuePair<string, string> kvpStr in m_vDict)
plNew.Set(kvpStr.Key, kvpStr.Value);
foreach(KeyValuePair<string, string> kvp in m_dict)
sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
return plNew;
return sdNew;
}
public bool Equals(StringDictionaryEx sdOther)
{
if(sdOther == null) { Debug.Assert(false); return false; }
if(m_dict.Count != sdOther.m_dict.Count) return false;
foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
{
string str = Get(kvp.Key);
if((str == null) || (str != kvp.Value)) return false;
}
return true;
}
public string Get(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
string s;
if(m_vDict.TryGetValue(strName, out s)) return s;
if(m_dict.TryGetValue(strName, out s)) return s;
return null;
}
public bool Exists(string strName)
{
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
return m_vDict.ContainsKey(strName);
return m_dict.ContainsKey(strName);
}
/// <summary>
@ -92,10 +106,10 @@ namespace KeePassLib.Collections
/// parameters is <c>null</c>.</exception>
public void Set(string strField, string strNewValue)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
Debug.Assert(strNewValue != null); if(strNewValue == null) throw new ArgumentNullException("strNewValue");
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
m_vDict[strField] = strNewValue;
m_dict[strField] = strNewValue;
}
/// <summary>
@ -108,9 +122,9 @@ namespace KeePassLib.Collections
/// parameter is <c>null</c>.</exception>
public bool Remove(string strField)
{
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
return m_vDict.Remove(strField);
return m_dict.Remove(strField);
}
}
}

View File

@ -0,0 +1,415 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using KeePassLib.Resources;
using KeePassLib.Utility;
namespace KeePassLib.Collections
{
public class VariantDictionary : ICloneable
{
private const ushort VdVersion = 0x0100;
private const ushort VdmCritical = 0xFF00;
private const ushort VdmInfo = 0x00FF;
private Dictionary<string, object> m_d = new Dictionary<string, object>();
private enum VdType : byte
{
None = 0,
// Byte = 0x02,
// UInt16 = 0x03,
UInt32 = 0x04,
UInt64 = 0x05,
// Signed mask: 0x08
Bool = 0x08,
// SByte = 0x0A,
// Int16 = 0x0B,
Int32 = 0x0C,
Int64 = 0x0D,
// Float = 0x10,
// Double = 0x11,
// Decimal = 0x12,
// Char = 0x17, // 16-bit Unicode character
String = 0x18,
// Array mask: 0x40
ByteArray = 0x42
}
public int Count
{
get { return m_d.Count; }
}
public VariantDictionary()
{
Debug.Assert((VdmCritical & VdmInfo) == ushort.MinValue);
Debug.Assert((VdmCritical | VdmInfo) == ushort.MaxValue);
}
private bool Get<T>(string strName, out T t)
{
t = default(T);
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
object o;
if(!m_d.TryGetValue(strName, out o)) return false; // No assert
if(o == null) { Debug.Assert(false); return false; }
if(o.GetType() != typeof(T)) { Debug.Assert(false); return false; }
t = (T)o;
return true;
}
private void SetStruct<T>(string strName, T t)
where T : struct
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
#if DEBUG
T tEx;
Get<T>(strName, out tEx); // Assert same type
#endif
m_d[strName] = t;
}
private void SetRef<T>(string strName, T t)
where T : class
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
if(t == null) { Debug.Assert(false); return; }
#if DEBUG
T tEx;
Get<T>(strName, out tEx); // Assert same type
#endif
m_d[strName] = t;
}
public bool Remove(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
return m_d.Remove(strName);
}
public void CopyTo(VariantDictionary d)
{
if(d == null) { Debug.Assert(false); return; }
// Do not clear the target
foreach(KeyValuePair<string, object> kvp in m_d)
{
d.m_d[kvp.Key] = kvp.Value;
}
}
public Type GetTypeOf(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
object o;
m_d.TryGetValue(strName, out o);
if(o == null) return null; // No assert
return o.GetType();
}
public uint GetUInt32(string strName, uint uDefault)
{
uint u;
if(Get<uint>(strName, out u)) return u;
return uDefault;
}
public void SetUInt32(string strName, uint uValue)
{
SetStruct<uint>(strName, uValue);
}
public ulong GetUInt64(string strName, ulong uDefault)
{
ulong u;
if(Get<ulong>(strName, out u)) return u;
return uDefault;
}
public void SetUInt64(string strName, ulong uValue)
{
SetStruct<ulong>(strName, uValue);
}
public bool GetBool(string strName, bool bDefault)
{
bool b;
if(Get<bool>(strName, out b)) return b;
return bDefault;
}
public void SetBool(string strName, bool bValue)
{
SetStruct<bool>(strName, bValue);
}
public int GetInt32(string strName, int iDefault)
{
int i;
if(Get<int>(strName, out i)) return i;
return iDefault;
}
public void SetInt32(string strName, int iValue)
{
SetStruct<int>(strName, iValue);
}
public long GetInt64(string strName, long lDefault)
{
long l;
if(Get<long>(strName, out l)) return l;
return lDefault;
}
public void SetInt64(string strName, long lValue)
{
SetStruct<long>(strName, lValue);
}
public string GetString(string strName)
{
string str;
Get<string>(strName, out str);
return str;
}
public void SetString(string strName, string strValue)
{
SetRef<string>(strName, strValue);
}
public byte[] GetByteArray(string strName)
{
byte[] pb;
Get<byte[]>(strName, out pb);
return pb;
}
public void SetByteArray(string strName, byte[] pbValue)
{
SetRef<byte[]>(strName, pbValue);
}
/// <summary>
/// Create a deep copy.
/// </summary>
public virtual object Clone()
{
VariantDictionary vdNew = new VariantDictionary();
foreach(KeyValuePair<string, object> kvp in m_d)
{
object o = kvp.Value;
if(o == null) { Debug.Assert(false); continue; }
Type t = o.GetType();
if(t == typeof(byte[]))
{
byte[] p = (byte[])o;
byte[] pNew = new byte[p.Length];
if(p.Length > 0) Array.Copy(p, pNew, p.Length);
o = pNew;
}
vdNew.m_d[kvp.Key] = o;
}
return vdNew;
}
public static byte[] Serialize(VariantDictionary p)
{
if(p == null) { Debug.Assert(false); return null; }
byte[] pbRet;
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, MemUtil.UInt16ToBytes(VdVersion));
foreach(KeyValuePair<string, object> kvp in p.m_d)
{
string strName = kvp.Key;
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); continue; }
byte[] pbName = StrUtil.Utf8.GetBytes(strName);
object o = kvp.Value;
if(o == null) { Debug.Assert(false); continue; }
Type t = o.GetType();
VdType vt = VdType.None;
byte[] pbValue = null;
if(t == typeof(uint))
{
vt = VdType.UInt32;
pbValue = MemUtil.UInt32ToBytes((uint)o);
}
else if(t == typeof(ulong))
{
vt = VdType.UInt64;
pbValue = MemUtil.UInt64ToBytes((ulong)o);
}
else if(t == typeof(bool))
{
vt = VdType.Bool;
pbValue = new byte[1];
pbValue[0] = ((bool)o ? (byte)1 : (byte)0);
}
else if(t == typeof(int))
{
vt = VdType.Int32;
pbValue = MemUtil.Int32ToBytes((int)o);
}
else if(t == typeof(long))
{
vt = VdType.Int64;
pbValue = MemUtil.Int64ToBytes((long)o);
}
else if(t == typeof(string))
{
vt = VdType.String;
pbValue = StrUtil.Utf8.GetBytes((string)o);
}
else if(t == typeof(byte[]))
{
vt = VdType.ByteArray;
pbValue = (byte[])o;
}
else { Debug.Assert(false); continue; } // Unknown type
ms.WriteByte((byte)vt);
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbName.Length));
MemUtil.Write(ms, pbName);
MemUtil.Write(ms, MemUtil.Int32ToBytes(pbValue.Length));
MemUtil.Write(ms, pbValue);
}
ms.WriteByte((byte)VdType.None);
pbRet = ms.ToArray();
}
return pbRet;
}
public static VariantDictionary Deserialize(byte[] pb)
{
if(pb == null) { Debug.Assert(false); return null; }
VariantDictionary d = new VariantDictionary();
using(MemoryStream ms = new MemoryStream(pb, false))
{
ushort uVersion = MemUtil.BytesToUInt16(MemUtil.Read(ms, 2));
if((uVersion & VdmCritical) > (VdVersion & VdmCritical))
throw new FormatException(KLRes.FileNewVerReq);
while(true)
{
int iType = ms.ReadByte();
if(iType < 0) throw new EndOfStreamException(KLRes.FileCorrupted);
byte btType = (byte)iType;
if(btType == (byte)VdType.None) break;
int cbName = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
byte[] pbName = MemUtil.Read(ms, cbName);
if(pbName.Length != cbName)
throw new EndOfStreamException(KLRes.FileCorrupted);
string strName = StrUtil.Utf8.GetString(pbName);
int cbValue = MemUtil.BytesToInt32(MemUtil.Read(ms, 4));
byte[] pbValue = MemUtil.Read(ms, cbValue);
if(pbValue.Length != cbValue)
throw new EndOfStreamException(KLRes.FileCorrupted);
switch(btType)
{
case (byte)VdType.UInt32:
if(cbValue == 4)
d.SetUInt32(strName, MemUtil.BytesToUInt32(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.UInt64:
if(cbValue == 8)
d.SetUInt64(strName, MemUtil.BytesToUInt64(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.Bool:
if(cbValue == 1)
d.SetBool(strName, (pbValue[0] != 0));
else { Debug.Assert(false); }
break;
case (byte)VdType.Int32:
if(cbValue == 4)
d.SetInt32(strName, MemUtil.BytesToInt32(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.Int64:
if(cbValue == 8)
d.SetInt64(strName, MemUtil.BytesToInt64(pbValue));
else { Debug.Assert(false); }
break;
case (byte)VdType.String:
d.SetString(strName, StrUtil.Utf8.GetString(pbValue));
break;
case (byte)VdType.ByteArray:
d.SetByteArray(strName, pbValue);
break;
default:
Debug.Assert(false); // Unknown type
break;
}
}
Debug.Assert(ms.ReadByte() < 0);
}
return d;
}
}
}

View File

@ -0,0 +1,251 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using KeePassLib.Resources;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.Cipher
{
/// <summary>
/// Implementation of the ChaCha20 cipher with a 96-bit nonce,
/// as specified in RFC 7539.
/// https://tools.ietf.org/html/rfc7539
/// </summary>
public sealed class ChaCha20Cipher : CtrBlockCipher
{
private uint[] m_s = new uint[16]; // State
private uint[] m_x = new uint[16]; // Working buffer
private bool m_bLargeCounter; // See constructor documentation
private static readonly uint[] g_sigma = new uint[4] {
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
};
private const string StrNameRfc = "ChaCha20 (RFC 7539)";
public override int BlockSize
{
get { return 64; }
}
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12) :
this(pbKey32, pbIV12, false)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="pbKey32">Key (32 bytes).</param>
/// <param name="pbIV12">Nonce (12 bytes).</param>
/// <param name="bLargeCounter">If <c>false</c>, the RFC 7539 version
/// of ChaCha20 is used. In this case, only 256 GB of data can be
/// encrypted securely (because the block counter is a 32-bit variable);
/// an attempt to encrypt more data throws an exception.
/// If <paramref name="bLargeCounter" /> is <c>true</c>, the 32-bit
/// counter overflows to another 32-bit variable (i.e. the counter
/// effectively is a 64-bit variable), like in the original ChaCha20
/// specification by D. J. Bernstein (which has a 64-bit counter and a
/// 64-bit nonce). To be compatible with this version, the 64-bit nonce
/// must be stored in the last 8 bytes of <paramref name="pbIV12" />
/// and the first 4 bytes must be 0.
/// If the IV was generated randomly, a 12-byte IV and a large counter
/// can be used to securely encrypt more than 256 GB of data (but note
/// this is incompatible with RFC 7539 and the original specification).</param>
public ChaCha20Cipher(byte[] pbKey32, byte[] pbIV12, bool bLargeCounter) :
base()
{
if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
if(pbIV12 == null) throw new ArgumentNullException("pbIV12");
if(pbIV12.Length != 12) throw new ArgumentOutOfRangeException("pbIV12");
m_bLargeCounter = bLargeCounter;
// Key setup
m_s[4] = MemUtil.BytesToUInt32(pbKey32, 0);
m_s[5] = MemUtil.BytesToUInt32(pbKey32, 4);
m_s[6] = MemUtil.BytesToUInt32(pbKey32, 8);
m_s[7] = MemUtil.BytesToUInt32(pbKey32, 12);
m_s[8] = MemUtil.BytesToUInt32(pbKey32, 16);
m_s[9] = MemUtil.BytesToUInt32(pbKey32, 20);
m_s[10] = MemUtil.BytesToUInt32(pbKey32, 24);
m_s[11] = MemUtil.BytesToUInt32(pbKey32, 28);
m_s[0] = g_sigma[0];
m_s[1] = g_sigma[1];
m_s[2] = g_sigma[2];
m_s[3] = g_sigma[3];
// IV setup
m_s[12] = 0; // Counter
m_s[13] = MemUtil.BytesToUInt32(pbIV12, 0);
m_s[14] = MemUtil.BytesToUInt32(pbIV12, 4);
m_s[15] = MemUtil.BytesToUInt32(pbIV12, 8);
}
protected override void Dispose(bool bDisposing)
{
MemUtil.ZeroArray<uint>(m_s);
MemUtil.ZeroArray<uint>(m_x);
base.Dispose(bDisposing);
}
protected override void NextBlock(byte[] pBlock)
{
if(pBlock == null) throw new ArgumentNullException("pBlock");
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
// x is a local alias for the working buffer; with this,
// the compiler/runtime might remove some checks
uint[] x = m_x;
if(x == null) throw new InvalidOperationException();
if(x.Length < 16) throw new InvalidOperationException();
uint[] s = m_s;
if(s == null) throw new InvalidOperationException();
if(s.Length < 16) throw new InvalidOperationException();
Array.Copy(s, x, 16);
unchecked
{
// 10 * 8 quarter rounds = 20 rounds
for(int i = 0; i < 10; ++i)
{
// Column quarter rounds
x[ 0] += x[ 4];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 16);
x[ 8] += x[12];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 12);
x[ 0] += x[ 4];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 0], 8);
x[ 8] += x[12];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 8], 7);
x[ 1] += x[ 5];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 16);
x[ 9] += x[13];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 12);
x[ 1] += x[ 5];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 1], 8);
x[ 9] += x[13];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[ 9], 7);
x[ 2] += x[ 6];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 16);
x[10] += x[14];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 12);
x[ 2] += x[ 6];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 2], 8);
x[10] += x[14];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[10], 7);
x[ 3] += x[ 7];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 16);
x[11] += x[15];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 12);
x[ 3] += x[ 7];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 3], 8);
x[11] += x[15];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[11], 7);
// Diagonal quarter rounds
x[ 0] += x[ 5];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 16);
x[10] += x[15];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 12);
x[ 0] += x[ 5];
x[15] = MemUtil.RotateLeft32(x[15] ^ x[ 0], 8);
x[10] += x[15];
x[ 5] = MemUtil.RotateLeft32(x[ 5] ^ x[10], 7);
x[ 1] += x[ 6];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 16);
x[11] += x[12];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 12);
x[ 1] += x[ 6];
x[12] = MemUtil.RotateLeft32(x[12] ^ x[ 1], 8);
x[11] += x[12];
x[ 6] = MemUtil.RotateLeft32(x[ 6] ^ x[11], 7);
x[ 2] += x[ 7];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 16);
x[ 8] += x[13];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 12);
x[ 2] += x[ 7];
x[13] = MemUtil.RotateLeft32(x[13] ^ x[ 2], 8);
x[ 8] += x[13];
x[ 7] = MemUtil.RotateLeft32(x[ 7] ^ x[ 8], 7);
x[ 3] += x[ 4];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 16);
x[ 9] += x[14];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 12);
x[ 3] += x[ 4];
x[14] = MemUtil.RotateLeft32(x[14] ^ x[ 3], 8);
x[ 9] += x[14];
x[ 4] = MemUtil.RotateLeft32(x[ 4] ^ x[ 9], 7);
}
for(int i = 0; i < 16; ++i) x[i] += s[i];
for(int i = 0; i < 16; ++i)
{
int i4 = i << 2;
uint xi = x[i];
pBlock[i4] = (byte)xi;
pBlock[i4 + 1] = (byte)(xi >> 8);
pBlock[i4 + 2] = (byte)(xi >> 16);
pBlock[i4 + 3] = (byte)(xi >> 24);
}
++s[12];
if(s[12] == 0)
{
if(!m_bLargeCounter)
throw new InvalidOperationException(
KLRes.EncDataTooLarge.Replace(@"{PARAM}", StrNameRfc));
++s[13]; // Increment high half of large counter
}
}
}
public long Seek(long lOffset, SeekOrigin so)
{
if(so != SeekOrigin.Begin) throw new NotSupportedException();
if((lOffset < 0) || ((lOffset & 63) != 0) ||
((lOffset >> 6) > (long)uint.MaxValue))
throw new ArgumentOutOfRangeException("lOffset");
m_s[12] = (uint)(lOffset >> 6);
InvalidateBlock();
return lOffset;
}
}
}

View File

@ -0,0 +1,174 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using KeePassLib.Resources;
namespace KeePassLib.Cryptography.Cipher
{
public sealed class ChaCha20Engine : ICipherEngine2
{
private PwUuid m_uuid = new PwUuid(new byte[] {
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A
});
public PwUuid CipherUuid
{
get { return m_uuid; }
}
public string DisplayName
{
get
{
return ("ChaCha20 (" + KLRes.KeyBits.Replace(@"{PARAM}",
"256") + ", RFC 7539)");
}
}
public int KeyLength
{
get { return 32; }
}
public int IVLength
{
get { return 12; } // 96 bits
}
public Stream EncryptStream(Stream sPlainText, byte[] pbKey, byte[] pbIV)
{
return new ChaCha20Stream(sPlainText, true, pbKey, pbIV);
}
public Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV)
{
return new ChaCha20Stream(sEncrypted, false, pbKey, pbIV);
}
}
internal sealed class ChaCha20Stream : Stream
{
private Stream m_sBase;
private readonly bool m_bWriting;
private ChaCha20Cipher m_c;
private byte[] m_pbBuffer = null;
public override bool CanRead
{
get { return !m_bWriting; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return m_bWriting; }
}
public override long Length
{
get { Debug.Assert(false); throw new NotSupportedException(); }
}
public override long Position
{
get { Debug.Assert(false); throw new NotSupportedException(); }
set { Debug.Assert(false); throw new NotSupportedException(); }
}
public ChaCha20Stream(Stream sBase, bool bWriting, byte[] pbKey32,
byte[] pbIV12)
{
if(sBase == null) throw new ArgumentNullException("sBase");
m_sBase = sBase;
m_bWriting = bWriting;
m_c = new ChaCha20Cipher(pbKey32, pbIV12);
}
protected override void Dispose(bool bDisposing)
{
if(!bDisposing) return;
if(m_sBase != null)
{
m_c.Dispose();
m_c = null;
m_sBase.Close();
m_sBase = null;
}
m_pbBuffer = null;
}
public override void Flush()
{
Debug.Assert(m_sBase != null);
if(m_bWriting && (m_sBase != null)) m_sBase.Flush();
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
Debug.Assert(false);
throw new NotImplementedException();
}
public override void SetLength(long lValue)
{
Debug.Assert(false);
throw new NotImplementedException();
}
public override int Read(byte[] pbBuffer, int iOffset, int nCount)
{
if(m_bWriting) throw new InvalidOperationException();
int cbRead = m_sBase.Read(pbBuffer, iOffset, nCount);
m_c.Decrypt(pbBuffer, iOffset, cbRead);
return cbRead;
}
public override void Write(byte[] pbBuffer, int iOffset, int nCount)
{
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return;
if(!m_bWriting) throw new InvalidOperationException();
if((m_pbBuffer == null) || (m_pbBuffer.Length < nCount))
m_pbBuffer = new byte[nCount];
Array.Copy(pbBuffer, iOffset, m_pbBuffer, 0, nCount);
m_c.Encrypt(m_pbBuffer, 0, nCount);
m_sBase.Write(m_pbBuffer, 0, nCount);
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -22,7 +22,6 @@ using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Security;
namespace KeePassLib.Cryptography.Cipher
{
@ -41,12 +40,17 @@ namespace KeePassLib.Cryptography.Cipher
{
get
{
if(m_poolGlobal != null) return m_poolGlobal;
CipherPool cp = m_poolGlobal;
if(cp == null)
{
cp = new CipherPool();
cp.AddCipher(new StandardAesEngine());
cp.AddCipher(new ChaCha20Engine());
m_poolGlobal = new CipherPool();
m_poolGlobal.AddCipher(new StandardAesEngine());
m_poolGlobal = cp;
}
return m_poolGlobal;
return cp;
}
}

View File

@ -0,0 +1,101 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.Cipher
{
public abstract class CtrBlockCipher : IDisposable
{
private byte[] m_pBlock;
private int m_iBlockPos;
public abstract int BlockSize
{
get;
}
public CtrBlockCipher()
{
int cb = this.BlockSize;
if(cb <= 0) throw new InvalidOperationException("this.BlockSize");
m_pBlock = new byte[cb];
m_iBlockPos = cb;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool bDisposing)
{
MemUtil.ZeroByteArray(m_pBlock);
m_iBlockPos = m_pBlock.Length;
}
protected void InvalidateBlock()
{
m_iBlockPos = m_pBlock.Length;
}
protected abstract void NextBlock(byte[] pBlock);
public void Encrypt(byte[] m, int iOffset, int cb)
{
if(m == null) throw new ArgumentNullException("m");
if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
if(cb < 0) throw new ArgumentOutOfRangeException("cb");
if(iOffset > (m.Length - cb)) throw new ArgumentOutOfRangeException("cb");
int cbBlock = m_pBlock.Length;
while(cb > 0)
{
Debug.Assert(m_iBlockPos <= cbBlock);
if(m_iBlockPos == cbBlock)
{
NextBlock(m_pBlock);
m_iBlockPos = 0;
}
int cbCopy = Math.Min(cbBlock - m_iBlockPos, cb);
Debug.Assert(cbCopy > 0);
MemUtil.XorArray(m_pBlock, m_iBlockPos, m, iOffset, cbCopy);
m_iBlockPos += cbCopy;
iOffset += cbCopy;
cb -= cbCopy;
}
}
public void Decrypt(byte[] m, int iOffset, int cb)
{
Encrypt(m, iOffset, cb);
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -63,4 +63,25 @@ namespace KeePassLib.Cryptography.Cipher
/// <returns>Stream, from which the decrypted data can be read.</returns>
Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV);
}
public interface ICipherEngine2 : ICipherEngine
{
/// <summary>
/// Length of an encryption key in bytes.
/// The base <c>ICipherEngine</c> assumes 32.
/// </summary>
int KeyLength
{
get;
}
/// <summary>
/// Length of the initialization vector in bytes.
/// The base <c>ICipherEngine</c> assumes 16.
/// </summary>
int IVLength
{
get;
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,169 +19,143 @@
// Implementation of the Salsa20 cipher, based on the eSTREAM submission.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.Cipher
{
public sealed class Salsa20Cipher
public sealed class Salsa20Cipher : CtrBlockCipher
{
private uint[] m_state = new uint[16];
private uint[] m_s = new uint[16]; // State
private uint[] m_x = new uint[16]; // Working buffer
private byte[] m_output = new byte[64];
private int m_outputPos = 64;
private static readonly uint[] m_sigma = new uint[4]{
private static readonly uint[] g_sigma = new uint[4] {
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
};
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8)
public override int BlockSize
{
KeySetup(pbKey32);
IvSetup(pbIV8);
get { return 64; }
}
~Salsa20Cipher()
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) : base()
{
if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
if(pbIV8 == null) throw new ArgumentNullException("pbIV8");
if(pbIV8.Length != 8) throw new ArgumentOutOfRangeException("pbIV8");
// Clear sensitive data
Array.Clear(m_state, 0, m_state.Length);
Array.Clear(m_x, 0, m_x.Length);
// Key setup
m_s[1] = MemUtil.BytesToUInt32(pbKey32, 0);
m_s[2] = MemUtil.BytesToUInt32(pbKey32, 4);
m_s[3] = MemUtil.BytesToUInt32(pbKey32, 8);
m_s[4] = MemUtil.BytesToUInt32(pbKey32, 12);
m_s[11] = MemUtil.BytesToUInt32(pbKey32, 16);
m_s[12] = MemUtil.BytesToUInt32(pbKey32, 20);
m_s[13] = MemUtil.BytesToUInt32(pbKey32, 24);
m_s[14] = MemUtil.BytesToUInt32(pbKey32, 28);
m_s[0] = g_sigma[0];
m_s[5] = g_sigma[1];
m_s[10] = g_sigma[2];
m_s[15] = g_sigma[3];
// IV setup
m_s[6] = MemUtil.BytesToUInt32(pbIV8, 0);
m_s[7] = MemUtil.BytesToUInt32(pbIV8, 4);
m_s[8] = 0; // Counter, low
m_s[9] = 0; // Counter, high
}
private void NextOutput()
protected override void Dispose(bool bDisposing)
{
uint[] x = m_x; // Local alias for working buffer
MemUtil.ZeroArray<uint>(m_s);
MemUtil.ZeroArray<uint>(m_x);
base.Dispose(bDisposing);
}
// Compiler/runtime might remove array bound checks after this
protected override void NextBlock(byte[] pBlock)
{
if(pBlock == null) throw new ArgumentNullException("pBlock");
if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
// x is a local alias for the working buffer; with this,
// the compiler/runtime might remove some checks
uint[] x = m_x;
if(x == null) throw new InvalidOperationException();
if(x.Length < 16) throw new InvalidOperationException();
Array.Copy(m_state, x, 16);
uint[] s = m_s;
if(s == null) throw new InvalidOperationException();
if(s.Length < 16) throw new InvalidOperationException();
Array.Copy(s, x, 16);
unchecked
{
// 10 * 8 quarter rounds = 20 rounds
for(int i = 0; i < 10; ++i) // (int i = 20; i > 0; i -= 2)
{
x[ 4] ^= Rotl32(x[ 0] + x[12], 7);
x[ 8] ^= Rotl32(x[ 4] + x[ 0], 9);
x[12] ^= Rotl32(x[ 8] + x[ 4], 13);
x[ 0] ^= Rotl32(x[12] + x[ 8], 18);
x[ 9] ^= Rotl32(x[ 5] + x[ 1], 7);
x[13] ^= Rotl32(x[ 9] + x[ 5], 9);
x[ 1] ^= Rotl32(x[13] + x[ 9], 13);
x[ 5] ^= Rotl32(x[ 1] + x[13], 18);
x[14] ^= Rotl32(x[10] + x[ 6], 7);
x[ 2] ^= Rotl32(x[14] + x[10], 9);
x[ 6] ^= Rotl32(x[ 2] + x[14], 13);
x[10] ^= Rotl32(x[ 6] + x[ 2], 18);
x[ 3] ^= Rotl32(x[15] + x[11], 7);
x[ 7] ^= Rotl32(x[ 3] + x[15], 9);
x[11] ^= Rotl32(x[ 7] + x[ 3], 13);
x[15] ^= Rotl32(x[11] + x[ 7], 18);
x[ 1] ^= Rotl32(x[ 0] + x[ 3], 7);
x[ 2] ^= Rotl32(x[ 1] + x[ 0], 9);
x[ 3] ^= Rotl32(x[ 2] + x[ 1], 13);
x[ 0] ^= Rotl32(x[ 3] + x[ 2], 18);
x[ 6] ^= Rotl32(x[ 5] + x[ 4], 7);
x[ 7] ^= Rotl32(x[ 6] + x[ 5], 9);
x[ 4] ^= Rotl32(x[ 7] + x[ 6], 13);
x[ 5] ^= Rotl32(x[ 4] + x[ 7], 18);
x[11] ^= Rotl32(x[10] + x[ 9], 7);
x[ 8] ^= Rotl32(x[11] + x[10], 9);
x[ 9] ^= Rotl32(x[ 8] + x[11], 13);
x[10] ^= Rotl32(x[ 9] + x[ 8], 18);
x[12] ^= Rotl32(x[15] + x[14], 7);
x[13] ^= Rotl32(x[12] + x[15], 9);
x[14] ^= Rotl32(x[13] + x[12], 13);
x[15] ^= Rotl32(x[14] + x[13], 18);
}
x[ 4] ^= MemUtil.RotateLeft32(x[ 0] + x[12], 7);
x[ 8] ^= MemUtil.RotateLeft32(x[ 4] + x[ 0], 9);
x[12] ^= MemUtil.RotateLeft32(x[ 8] + x[ 4], 13);
x[ 0] ^= MemUtil.RotateLeft32(x[12] + x[ 8], 18);
x[ 9] ^= MemUtil.RotateLeft32(x[ 5] + x[ 1], 7);
x[13] ^= MemUtil.RotateLeft32(x[ 9] + x[ 5], 9);
x[ 1] ^= MemUtil.RotateLeft32(x[13] + x[ 9], 13);
x[ 5] ^= MemUtil.RotateLeft32(x[ 1] + x[13], 18);
x[14] ^= MemUtil.RotateLeft32(x[10] + x[ 6], 7);
x[ 2] ^= MemUtil.RotateLeft32(x[14] + x[10], 9);
x[ 6] ^= MemUtil.RotateLeft32(x[ 2] + x[14], 13);
x[10] ^= MemUtil.RotateLeft32(x[ 6] + x[ 2], 18);
x[ 3] ^= MemUtil.RotateLeft32(x[15] + x[11], 7);
x[ 7] ^= MemUtil.RotateLeft32(x[ 3] + x[15], 9);
x[11] ^= MemUtil.RotateLeft32(x[ 7] + x[ 3], 13);
x[15] ^= MemUtil.RotateLeft32(x[11] + x[ 7], 18);
x[ 1] ^= MemUtil.RotateLeft32(x[ 0] + x[ 3], 7);
x[ 2] ^= MemUtil.RotateLeft32(x[ 1] + x[ 0], 9);
x[ 3] ^= MemUtil.RotateLeft32(x[ 2] + x[ 1], 13);
x[ 0] ^= MemUtil.RotateLeft32(x[ 3] + x[ 2], 18);
x[ 6] ^= MemUtil.RotateLeft32(x[ 5] + x[ 4], 7);
x[ 7] ^= MemUtil.RotateLeft32(x[ 6] + x[ 5], 9);
x[ 4] ^= MemUtil.RotateLeft32(x[ 7] + x[ 6], 13);
x[ 5] ^= MemUtil.RotateLeft32(x[ 4] + x[ 7], 18);
x[11] ^= MemUtil.RotateLeft32(x[10] + x[ 9], 7);
x[ 8] ^= MemUtil.RotateLeft32(x[11] + x[10], 9);
x[ 9] ^= MemUtil.RotateLeft32(x[ 8] + x[11], 13);
x[10] ^= MemUtil.RotateLeft32(x[ 9] + x[ 8], 18);
x[12] ^= MemUtil.RotateLeft32(x[15] + x[14], 7);
x[13] ^= MemUtil.RotateLeft32(x[12] + x[15], 9);
x[14] ^= MemUtil.RotateLeft32(x[13] + x[12], 13);
x[15] ^= MemUtil.RotateLeft32(x[14] + x[13], 18);
}
for(int i = 0; i < 16; ++i) x[i] += s[i];
for(int i = 0; i < 16; ++i)
x[i] += m_state[i];
{
int i4 = i << 2;
uint xi = x[i];
for(int i = 0; i < 16; ++i)
{
m_output[i << 2] = (byte)x[i];
m_output[(i << 2) + 1] = (byte)(x[i] >> 8);
m_output[(i << 2) + 2] = (byte)(x[i] >> 16);
m_output[(i << 2) + 3] = (byte)(x[i] >> 24);
}
m_outputPos = 0;
++m_state[8];
if(m_state[8] == 0) ++m_state[9];
}
pBlock[i4] = (byte)xi;
pBlock[i4 + 1] = (byte)(xi >> 8);
pBlock[i4 + 2] = (byte)(xi >> 16);
pBlock[i4 + 3] = (byte)(xi >> 24);
}
private static uint Rotl32(uint x, int b)
{
unchecked
{
return ((x << b) | (x >> (32 - b)));
}
}
private static uint U8To32Little(byte[] pb, int iOffset)
{
unchecked
{
return ((uint)pb[iOffset] | ((uint)pb[iOffset + 1] << 8) |
((uint)pb[iOffset + 2] << 16) | ((uint)pb[iOffset + 3] << 24));
}
}
private void KeySetup(byte[] k)
{
if(k == null) throw new ArgumentNullException("k");
if(k.Length != 32) throw new ArgumentException();
m_state[1] = U8To32Little(k, 0);
m_state[2] = U8To32Little(k, 4);
m_state[3] = U8To32Little(k, 8);
m_state[4] = U8To32Little(k, 12);
m_state[11] = U8To32Little(k, 16);
m_state[12] = U8To32Little(k, 20);
m_state[13] = U8To32Little(k, 24);
m_state[14] = U8To32Little(k, 28);
m_state[0] = m_sigma[0];
m_state[5] = m_sigma[1];
m_state[10] = m_sigma[2];
m_state[15] = m_sigma[3];
}
private void IvSetup(byte[] pbIV)
{
if(pbIV == null) throw new ArgumentNullException("pbIV");
if(pbIV.Length != 8) throw new ArgumentException();
m_state[6] = U8To32Little(pbIV, 0);
m_state[7] = U8To32Little(pbIV, 4);
m_state[8] = 0;
m_state[9] = 0;
}
public void Encrypt(byte[] m, int nByteCount, bool bXor)
{
if(m == null) throw new ArgumentNullException("m");
if(nByteCount > m.Length) throw new ArgumentException();
int nBytesRem = nByteCount, nOffset = 0;
while(nBytesRem > 0)
{
Debug.Assert((m_outputPos >= 0) && (m_outputPos <= 64));
if(m_outputPos == 64) NextOutput();
Debug.Assert(m_outputPos < 64);
int nCopy = Math.Min(64 - m_outputPos, nBytesRem);
if(bXor) MemUtil.XorArray(m_output, m_outputPos, m, nOffset, nCopy);
else Array.Copy(m_output, m_outputPos, m, nOffset, nCopy);
m_outputPos += nCopy;
nBytesRem -= nCopy;
nOffset += nCopy;
++s[8];
if(s[8] == 0) ++s[9];
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -24,15 +24,8 @@ using System.IO;
using System.Security;
using System.Diagnostics;
#if !KeePassRT
#if !KeePassUAP
using System.Security.Cryptography;
#else
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
#endif
using KeePassLib.Resources;
@ -44,12 +37,12 @@ namespace KeePassLib.Cryptography.Cipher
/// </summary>
public sealed class StandardAesEngine : ICipherEngine
{
#if !KeePassRT
#if !KeePassUAP
private const CipherMode m_rCipherMode = CipherMode.CBC;
private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7;
#endif
private static PwUuid m_uuidAes = null;
private static PwUuid g_uuidAes = null;
/// <summary>
/// UUID of the cipher engine. This ID uniquely identifies the
@ -59,14 +52,16 @@ namespace KeePassLib.Cryptography.Cipher
{
get
{
if(m_uuidAes == null)
PwUuid pu = g_uuidAes;
if(pu == null)
{
m_uuidAes = new PwUuid(new byte[]{
pu = new PwUuid(new byte[] {
0x31, 0xC1, 0xF2, 0xE6, 0xBF, 0x71, 0x43, 0x50,
0xBE, 0x58, 0x05, 0x21, 0x6A, 0xFC, 0x5A, 0xFF });
g_uuidAes = pu;
}
return m_uuidAes;
return pu;
}
}
@ -81,7 +76,14 @@ namespace KeePassLib.Cryptography.Cipher
/// <summary>
/// Get a displayable name describing this cipher engine.
/// </summary>
public string DisplayName { get { return KLRes.EncAlgorithmAes; } }
public string DisplayName
{
get
{
return ("AES/Rijndael (" + KLRes.KeyBits.Replace(@"{PARAM}",
"256") + ", FIPS 197)");
}
}
private static void ValidateArguments(Stream stream, bool bEncrypt, byte[] pbKey, byte[] pbIV)
{
@ -98,12 +100,12 @@ namespace KeePassLib.Cryptography.Cipher
if(bEncrypt)
{
Debug.Assert(stream.CanWrite);
if(stream.CanWrite == false) throw new ArgumentException("Stream must be writable!");
if(!stream.CanWrite) throw new ArgumentException("Stream must be writable!");
}
else // Decrypt
{
Debug.Assert(stream.CanRead);
if(stream.CanRead == false) throw new ArgumentException("Encrypted stream must be readable!");
if(!stream.CanRead) throw new ArgumentException("Encrypted stream must be readable!");
}
}
@ -117,7 +119,9 @@ namespace KeePassLib.Cryptography.Cipher
byte[] pbLocalKey = new byte[32];
Array.Copy(pbKey, pbLocalKey, 32);
#if !KeePassRT
#if KeePassUAP
return StandardAesEngineExt.CreateStream(s, bEncrypt, pbLocalKey, pbLocalIV);
#else
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
@ -137,18 +141,6 @@ namespace KeePassLib.Cryptography.Cipher
return new CryptoStream(s, iTransform, bEncrypt ? CryptoStreamMode.Write :
CryptoStreamMode.Read);
#else
AesEngine aes = new AesEngine();
CbcBlockCipher cbc = new CbcBlockCipher(aes);
PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(cbc,
new Pkcs7Padding());
KeyParameter kp = new KeyParameter(pbLocalKey);
ParametersWithIV prmIV = new ParametersWithIV(kp, pbLocalIV);
bc.Init(bEncrypt, prmIV);
IBufferedCipher cpRead = (bEncrypt ? null : bc);
IBufferedCipher cpWrite = (bEncrypt ? bc : null);
return new CipherStream(s, cpRead, cpWrite);
#endif
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -20,11 +20,13 @@
*/
using System;
using System.Security;
using System.Security.Cryptography;
using System.IO;
using System.Diagnostics;
using System.IO;
#if !KeePassUAP
using System.Drawing;
using System.Security.Cryptography;
#endif
using KeePassLib.Native;
using KeePassLib.Utility;
@ -39,21 +41,30 @@ namespace KeePassLib.Cryptography
public sealed class CryptoRandom
{
private byte[] m_pbEntropyPool = new byte[64];
private uint m_uCounter;
private ulong m_uCounter;
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
private ulong m_uGeneratedBytesCount = 0;
private static object g_oSyncRoot = new object();
private object m_oSyncRoot = new object();
private static CryptoRandom m_pInstance = null;
private static CryptoRandom g_pInstance = null;
public static CryptoRandom Instance
{
get
{
if(m_pInstance != null) return m_pInstance;
CryptoRandom cr;
lock(g_oSyncRoot)
{
cr = g_pInstance;
if(cr == null)
{
cr = new CryptoRandom();
g_pInstance = cr;
}
}
m_pInstance = new CryptoRandom();
return m_pInstance;
return cr;
}
}
@ -80,10 +91,12 @@ namespace KeePassLib.Cryptography
private CryptoRandom()
{
Random r = new Random();
m_uCounter = (uint)r.Next();
Random rWeak = new Random();
byte[] pb = new byte[8];
rWeak.NextBytes(pb);
m_uCounter = MemUtil.BytesToUInt64(pb);
AddEntropy(GetSystemData(r));
AddEntropy(GetSystemData(rWeak));
AddEntropy(GetCspData());
}
@ -99,32 +112,40 @@ namespace KeePassLib.Cryptography
if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
byte[] pbNewData = pbEntropy;
if(pbEntropy.Length >= 64)
if(pbEntropy.Length > 64)
{
#if !KeePassLibSD
SHA512Managed shaNew = new SHA512Managed();
#if KeePassLibSD
using(SHA256Managed shaNew = new SHA256Managed())
#else
SHA256Managed shaNew = new SHA256Managed();
using(SHA512Managed shaNew = new SHA512Managed())
#endif
{
pbNewData = shaNew.ComputeHash(pbEntropy);
}
}
MemoryStream ms = new MemoryStream();
lock(m_oSyncRoot)
{
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
ms.Write(pbNewData, 0, pbNewData.Length);
int cbPool = m_pbEntropyPool.Length;
int cbNew = pbNewData.Length;
byte[] pbFinal = ms.ToArray();
#if !KeePassLibSD
Debug.Assert(pbFinal.Length == (64 + pbNewData.Length));
SHA512Managed shaPool = new SHA512Managed();
byte[] pbCmp = new byte[cbPool + cbNew];
Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew);
MemUtil.ZeroByteArray(m_pbEntropyPool);
#if KeePassLibSD
using(SHA256Managed shaPool = new SHA256Managed())
#else
SHA256Managed shaPool = new SHA256Managed();
using(SHA512Managed shaPool = new SHA512Managed())
#endif
m_pbEntropyPool = shaPool.ComputeHash(pbFinal);
{
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
}
ms.Close();
MemUtil.ZeroByteArray(pbCmp);
}
}
private static byte[] GetSystemData(Random rWeak)
@ -132,71 +153,104 @@ namespace KeePassLib.Cryptography
MemoryStream ms = new MemoryStream();
byte[] pb;
pb = MemUtil.UInt32ToBytes((uint)Environment.TickCount);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.Int32ToBytes(Environment.TickCount);
MemUtil.Write(ms, pb);
pb = TimeUtil.PackTime(DateTime.Now);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
MemUtil.Write(ms, pb);
pb = MemUtil.UInt32ToBytes((uint)rWeak.Next());
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
ms.Write(pb, 0, pb.Length);
#if (!KeePassLibSD && !KeePassRT)
#if !KeePassLibSD
/*Not supported on Android
// In try-catch for systems without GUI;
// https://sourceforge.net/p/keepass/discussion/329221/thread/20335b73/
try
{
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)Environment.WorkingSet);
ms.Write(pb, 0, pb.Length);
Version v = Environment.OSVersion.Version;
int nv = (v.Major << 28) + (v.MajorRevision << 24) +
(v.Minor << 20) + (v.MinorRevision << 16) +
(v.Revision << 12) + v.Build;
pb = MemUtil.UInt32ToBytes((uint)nv);
ms.Write(pb, 0, pb.Length);
Process p = Process.GetCurrentProcess();
pb = MemUtil.UInt64ToBytes((ulong)p.Handle.ToInt64());
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt32ToBytes((uint)p.HandleCount);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt32ToBytes((uint)p.Id);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.NonpagedSystemMemorySize64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.PagedMemorySize64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.PagedSystemMemorySize64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.PeakPagedMemorySize64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.PeakVirtualMemorySize64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.PeakWorkingSet64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.PrivateMemorySize64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.StartTime.ToBinary());
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.VirtualMemorySize64);
ms.Write(pb, 0, pb.Length);
pb = MemUtil.UInt64ToBytes((ulong)p.WorkingSet64);
ms.Write(pb, 0, pb.Length);
// Not supported in Mono 1.2.6:
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// ms.Write(pb, 0, pb.Length);
Point pt = Cursor.Position;
pb = MemUtil.Int32ToBytes(pt.X);
MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(pt.Y);
MemUtil.Write(ms, pb);
}
catch(Exception) { }
*/
#endif
pb = MemUtil.Int32ToBytes(rWeak.Next());
MemUtil.Write(ms, pb);
pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
MemUtil.Write(ms, pb);
try
{
pb = MemUtil.Int32ToBytes(Environment.ProcessorCount);
MemUtil.Write(ms, pb);
#if KeePassUAP
Version v = EnvironmentExt.OSVersion.Version;
#else
Version v = Environment.OSVersion.Version;
#endif
pb = MemUtil.Int32ToBytes(v.GetHashCode());
MemUtil.Write(ms, pb);
#if !KeePassUAP
pb = MemUtil.Int64ToBytes(Environment.WorkingSet);
MemUtil.Write(ms, pb);
#endif
}
catch(Exception) { Debug.Assert(false); }
#if KeePassUAP
pb = DiagnosticsExt.GetProcessEntropy();
MemUtil.Write(ms, pb);
#elif !KeePassLibSD
Process p = null;
try
{
p = Process.GetCurrentProcess();
// Not supported in Mono 1.2.6:
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(p.HandleCount);
MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(p.Id);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.WorkingSet64);
MemUtil.Write(ms, pb);
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// ms.Write(pb, 0, pb.Length);
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// MemUtil.Write(ms, pb);
}
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
finally
{
try { if(p != null) p.Dispose(); }
catch(Exception) { Debug.Assert(false); }
}
#endif
pb = Guid.NewGuid().ToByteArray();
ms.Write(pb, 0, pb.Length);
MemUtil.Write(ms, pb);
byte[] pbAll = ms.ToArray();
ms.Close();
@ -215,28 +269,31 @@ namespace KeePassLib.Cryptography
if(this.GenerateRandom256Pre != null)
this.GenerateRandom256Pre(this, EventArgs.Empty);
byte[] pbFinal;
byte[] pbCmp;
lock(m_oSyncRoot)
{
unchecked { m_uCounter += 386047; } // Prime number
byte[] pbCounter = MemUtil.UInt32ToBytes(m_uCounter);
m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
byte[] pbCspRandom = GetCspData();
MemoryStream ms = new MemoryStream();
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
ms.Write(pbCounter, 0, pbCounter.Length);
ms.Write(pbCspRandom, 0, pbCspRandom.Length);
pbFinal = ms.ToArray();
Debug.Assert(pbFinal.Length == (m_pbEntropyPool.Length +
pbCounter.Length + pbCspRandom.Length));
ms.Close();
int cbPool = m_pbEntropyPool.Length;
int cbCtr = pbCounter.Length;
int cbCsp = pbCspRandom.Length;
pbCmp = new byte[cbPool + cbCtr + cbCsp];
Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
Array.Copy(pbCspRandom, 0, pbCmp, cbPool + cbCtr, cbCsp);
MemUtil.ZeroByteArray(pbCspRandom);
m_uGeneratedBytesCount += 32;
}
SHA256Managed sha256 = new SHA256Managed();
return sha256.ComputeHash(pbFinal);
byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
MemUtil.ZeroByteArray(pbCmp);
return pbRet;
}
/// <summary>
@ -248,29 +305,32 @@ namespace KeePassLib.Cryptography
/// random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedBytes)
{
if(uRequestedBytes == 0) return new byte[0]; // Allow zero-length array
if(uRequestedBytes == 0) return MemUtil.EmptyByteArray;
if(uRequestedBytes > (uint)int.MaxValue)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("uRequestedBytes");
}
byte[] pbRes = new byte[uRequestedBytes];
long lPos = 0;
int cbRem = (int)uRequestedBytes;
byte[] pbRes = new byte[cbRem];
int iPos = 0;
while(uRequestedBytes != 0)
while(cbRem != 0)
{
byte[] pbRandom256 = GenerateRandom256();
Debug.Assert(pbRandom256.Length == 32);
long lCopy = (long)((uRequestedBytes < 32) ? uRequestedBytes : 32);
int cbCopy = Math.Min(cbRem, 32);
Array.Copy(pbRandom256, 0, pbRes, iPos, cbCopy);
#if (!KeePassLibSD && !KeePassRT)
Array.Copy(pbRandom256, 0, pbRes, lPos, lCopy);
#else
Array.Copy(pbRandom256, 0, pbRes, (int)lPos, (int)lCopy);
#endif
MemUtil.ZeroByteArray(pbRandom256);
lPos += lCopy;
uRequestedBytes -= (uint)lCopy;
iPos += cbCopy;
cbRem -= cbCopy;
}
Debug.Assert((int)lPos == pbRes.Length);
Debug.Assert(iPos == pbRes.Length);
return pbRes;
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,9 +19,13 @@
using System;
using System.Diagnostics;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
@ -38,6 +42,7 @@ namespace KeePassLib.Cryptography
/// <summary>
/// A variant of the ARCFour algorithm (RC4 incompatible).
/// </summary>
/// </summary>
ArcFourVariant = 1,
/// <summary>
@ -45,7 +50,12 @@ namespace KeePassLib.Cryptography
/// </summary>
Salsa20 = 2,
Count = 3
/// <summary>
/// ChaCha20 stream cipher algorithm.
/// </summary>
ChaCha20 = 3,
Count = 4
}
/// <summary>
@ -56,13 +66,14 @@ namespace KeePassLib.Cryptography
/// </summary>
public sealed class CryptoRandomStream
{
private CrsAlgorithm m_crsAlgorithm;
private readonly CrsAlgorithm m_crsAlgorithm;
private byte[] m_pbState = null;
private byte m_i = 0;
private byte m_j = 0;
private Salsa20Cipher m_salsa20 = null;
private ChaCha20Cipher m_chacha20 = null;
/// <summary>
/// Construct a new cryptographically secure random stream object.
@ -70,31 +81,53 @@ namespace KeePassLib.Cryptography
/// <param name="genAlgorithm">Algorithm to use.</param>
/// <param name="pbKey">Initialization key. Must not be <c>null</c> and
/// must contain at least 1 byte.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
/// <exception cref="System.ArgumentException">Thrown if the
/// <paramref name="pbKey" /> parameter contains no bytes or the
/// algorithm is unknown.</exception>
public CryptoRandomStream(CrsAlgorithm genAlgorithm, byte[] pbKey)
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
{
m_crsAlgorithm = genAlgorithm;
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
/// <exception cref="System.ArgumentNullException">Thrown if the
int cbKey = pbKey.Length;
if(cbKey <= 0)
{
Debug.Assert(false); // Need at least one byte
throw new ArgumentOutOfRangeException("pbKey");
}
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
m_crsAlgorithm = a;
/// <exception cref="System.ArgumentException">Thrown if the
if(a == CrsAlgorithm.ChaCha20)
{
byte[] pbKey32 = new byte[32];
byte[] pbIV12 = new byte[12];
/// <paramref name="pbKey" /> parameter contains no bytes or the
using(SHA512Managed h = new SHA512Managed())
{
byte[] pbHash = h.ComputeHash(pbKey);
Array.Copy(pbHash, pbKey32, 32);
Array.Copy(pbHash, 32, pbIV12, 0, 12);
MemUtil.ZeroByteArray(pbHash);
}
/// algorithm is unknown.</exception>
m_chacha20 = new ChaCha20Cipher(pbKey32, pbIV12, true);
}
else if(a == CrsAlgorithm.Salsa20)
{
byte[] pbKey32 = CryptoUtil.HashSha256(pbKey);
byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
Debug.Assert(pbKey != null); if(pbKey == null) throw new ArgumentNullException("pbKey");
uint uKeyLen = (uint)pbKey.Length;
Debug.Assert(uKeyLen != 0); if(uKeyLen == 0) throw new ArgumentException();
if(genAlgorithm == CrsAlgorithm.ArcFourVariant)
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8);
}
else if(a == CrsAlgorithm.ArcFourVariant)
{
// Fill the state linearly
m_pbState = new byte[256];
for(uint w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
for(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
unchecked
{
byte j = 0, t;
uint inxKey = 0;
for(uint w = 0; w < 256; ++w) // Key setup
int inxKey = 0;
for(int w = 0; w < 256; ++w) // Key setup
{
j += (byte)(m_pbState[w] + pbKey[inxKey]);
@ -103,25 +136,16 @@ namespace KeePassLib.Cryptography
m_pbState[j] = t;
++inxKey;
if(inxKey >= uKeyLen) inxKey = 0;
if(inxKey >= cbKey) inxKey = 0;
}
}
GetRandomBytes(512); // Increases security, see cryptanalysis
}
else if(genAlgorithm == CrsAlgorithm.Salsa20)
{
SHA256Managed sha256 = new SHA256Managed();
byte[] pbKey32 = sha256.ComputeHash(pbKey);
byte[] pbIV = new byte[]{ 0xE8, 0x30, 0x09, 0x4B,
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV);
}
else // Unknown algorithm
{
Debug.Assert(false);
throw new ArgumentException();
throw new ArgumentOutOfRangeException("a");
}
}
@ -132,15 +156,23 @@ namespace KeePassLib.Cryptography
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedCount)
{
if(uRequestedCount == 0) return new byte[0];
if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
byte[] pbRet = new byte[uRequestedCount];
if(uRequestedCount > (uint)int.MaxValue)
throw new ArgumentOutOfRangeException("uRequestedCount");
int cb = (int)uRequestedCount;
if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
byte[] pbRet = new byte[cb];
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
m_chacha20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
m_salsa20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
{
unchecked
{
for(uint w = 0; w < uRequestedCount; ++w)
for(int w = 0; w < cb; ++w)
{
++m_i;
m_j += m_pbState[m_i];
@ -154,8 +186,6 @@ namespace KeePassLib.Cryptography
}
}
}
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
m_salsa20.Encrypt(pbRet, pbRet.Length, false);
else { Debug.Assert(false); }
return pbRet;
@ -164,15 +194,8 @@ namespace KeePassLib.Cryptography
public ulong GetRandomUInt64()
{
byte[] pb = GetRandomBytes(8);
unchecked
{
return ((ulong)pb[0]) | ((ulong)pb[1] << 8) |
((ulong)pb[2] << 16) | ((ulong)pb[3] << 24) |
((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) |
((ulong)pb[6] << 48) | ((ulong)pb[7] << 56);
return MemUtil.BytesToUInt64(pb);
}
}
#if CRSBENCHMARK
public static string Benchmark()

View File

@ -0,0 +1,126 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
public static class CryptoUtil
{
public static byte[] HashSha256(byte[] pbData)
{
if(pbData == null) throw new ArgumentNullException("pbData");
return HashSha256(pbData, 0, pbData.Length);
}
public static byte[] HashSha256(byte[] pbData, int iOffset, int cbCount)
{
if(pbData == null) throw new ArgumentNullException("pbData");
#if DEBUG
byte[] pbCopy = new byte[pbData.Length];
Array.Copy(pbData, pbCopy, pbData.Length);
#endif
byte[] pbHash;
using(SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(pbData, iOffset, cbCount);
}
#if DEBUG
// Ensure the data has not been modified
Debug.Assert(MemUtil.ArraysEqual(pbData, pbCopy));
Debug.Assert((pbHash != null) && (pbHash.Length == 32));
byte[] pbZero = new byte[32];
Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero));
#endif
return pbHash;
}
/// <summary>
/// Create a cryptographic key of length <paramref name="cbOut" />
/// (in bytes) from <paramref name="pbIn" />.
/// </summary>
public static byte[] ResizeKey(byte[] pbIn, int iInOffset,
int cbIn, int cbOut)
{
if(pbIn == null) throw new ArgumentNullException("pbIn");
if(cbOut < 0) throw new ArgumentOutOfRangeException("cbOut");
if(cbOut == 0) return MemUtil.EmptyByteArray;
byte[] pbHash;
if(cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn);
else
{
using(SHA512Managed h = new SHA512Managed())
{
pbHash = h.ComputeHash(pbIn, iInOffset, cbIn);
}
}
if(cbOut == pbHash.Length) return pbHash;
byte[] pbRet = new byte[cbOut];
if(cbOut < pbHash.Length)
Array.Copy(pbHash, pbRet, cbOut);
else
{
int iPos = 0;
ulong r = 0;
while(iPos < cbOut)
{
Debug.Assert(pbHash.Length == 64);
using(HMACSHA256 h = new HMACSHA256(pbHash))
{
byte[] pbR = MemUtil.UInt64ToBytes(r);
byte[] pbPart = h.ComputeHash(pbR);
int cbCopy = Math.Min(cbOut - iPos, pbPart.Length);
Debug.Assert(cbCopy > 0);
Array.Copy(pbPart, 0, pbRet, iPos, cbCopy);
iPos += cbCopy;
++r;
MemUtil.ZeroByteArray(pbPart);
}
}
Debug.Assert(iPos == cbOut);
}
#if DEBUG
byte[] pbZero = new byte[pbHash.Length];
Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero));
#endif
MemUtil.ZeroByteArray(pbHash);
return pbRet;
}
}
}

View File

@ -0,0 +1,229 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// This implementation is based on the official reference C
// implementation by Samuel Neves (CC0 1.0 Universal).
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.Hash
{
public sealed class Blake2b : HashAlgorithm
{
private const int NbRounds = 12;
private const int NbBlockBytes = 128;
private const int NbMaxOutBytes = 64;
private static readonly ulong[] g_vIV = new ulong[8] {
0x6A09E667F3BCC908UL, 0xBB67AE8584CAA73BUL,
0x3C6EF372FE94F82BUL, 0xA54FF53A5F1D36F1UL,
0x510E527FADE682D1UL, 0x9B05688C2B3E6C1FUL,
0x1F83D9ABFB41BD6BUL, 0x5BE0CD19137E2179UL
};
private static readonly int[] g_vSigma = new int[NbRounds * 16] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3
};
private readonly int m_cbHashLength;
private ulong[] m_h = new ulong[8];
private ulong[] m_t = new ulong[2];
private ulong[] m_f = new ulong[2];
private byte[] m_buf = new byte[NbBlockBytes];
private int m_cbBuf = 0;
private ulong[] m_m = new ulong[16];
private ulong[] m_v = new ulong[16];
public Blake2b()
{
m_cbHashLength = NbMaxOutBytes;
this.HashSizeValue = NbMaxOutBytes * 8; // Bits
Initialize();
}
public Blake2b(int cbHashLength)
{
if((cbHashLength < 0) || (cbHashLength > NbMaxOutBytes))
throw new ArgumentOutOfRangeException("cbHashLength");
m_cbHashLength = cbHashLength;
this.HashSizeValue = cbHashLength * 8; // Bits
Initialize();
}
public override void Initialize()
{
Debug.Assert(m_h.Length == g_vIV.Length);
Array.Copy(g_vIV, m_h, m_h.Length);
// Fan-out = 1, depth = 1
m_h[0] ^= 0x0000000001010000UL ^ (ulong)m_cbHashLength;
Array.Clear(m_t, 0, m_t.Length);
Array.Clear(m_f, 0, m_f.Length);
Array.Clear(m_buf, 0, m_buf.Length);
m_cbBuf = 0;
Array.Clear(m_m, 0, m_m.Length);
Array.Clear(m_v, 0, m_v.Length);
}
private static void G(ulong[] v, ulong[] m, int r16, int i,
int a, int b, int c, int d)
{
int p = r16 + i;
v[a] += v[b] + m[g_vSigma[p]];
v[d] = MemUtil.RotateRight64(v[d] ^ v[a], 32);
v[c] += v[d];
v[b] = MemUtil.RotateRight64(v[b] ^ v[c], 24);
v[a] += v[b] + m[g_vSigma[p + 1]];
v[d] = MemUtil.RotateRight64(v[d] ^ v[a], 16);
v[c] += v[d];
v[b] = MemUtil.RotateRight64(v[b] ^ v[c], 63);
}
private void Compress(byte[] pb, int iOffset)
{
ulong[] v = m_v;
ulong[] m = m_m;
ulong[] h = m_h;
for(int i = 0; i < 16; ++i)
m[i] = MemUtil.BytesToUInt64(pb, iOffset + (i << 3));
Array.Copy(h, v, 8);
v[8] = g_vIV[0];
v[9] = g_vIV[1];
v[10] = g_vIV[2];
v[11] = g_vIV[3];
v[12] = g_vIV[4] ^ m_t[0];
v[13] = g_vIV[5] ^ m_t[1];
v[14] = g_vIV[6] ^ m_f[0];
v[15] = g_vIV[7] ^ m_f[1];
for(int r = 0; r < NbRounds; ++r)
{
int r16 = r << 4;
G(v, m, r16, 0, 0, 4, 8, 12);
G(v, m, r16, 2, 1, 5, 9, 13);
G(v, m, r16, 4, 2, 6, 10, 14);
G(v, m, r16, 6, 3, 7, 11, 15);
G(v, m, r16, 8, 0, 5, 10, 15);
G(v, m, r16, 10, 1, 6, 11, 12);
G(v, m, r16, 12, 2, 7, 8, 13);
G(v, m, r16, 14, 3, 4, 9, 14);
}
for(int i = 0; i < 8; ++i)
h[i] ^= v[i] ^ v[i + 8];
}
private void IncrementCounter(ulong cb)
{
m_t[0] += cb;
if(m_t[0] < cb) ++m_t[1];
}
protected override void HashCore(byte[] array, int ibStart, int cbSize)
{
Debug.Assert(m_f[0] == 0);
if((m_cbBuf + cbSize) > NbBlockBytes) // Not '>=' (buffer must not be empty)
{
int cbFill = NbBlockBytes - m_cbBuf;
if(cbFill > 0) Array.Copy(array, ibStart, m_buf, m_cbBuf, cbFill);
IncrementCounter((ulong)NbBlockBytes);
Compress(m_buf, 0);
m_cbBuf = 0;
cbSize -= cbFill;
ibStart += cbFill;
while(cbSize > NbBlockBytes) // Not '>=' (buffer must not be empty)
{
IncrementCounter((ulong)NbBlockBytes);
Compress(array, ibStart);
cbSize -= NbBlockBytes;
ibStart += NbBlockBytes;
}
}
if(cbSize > 0)
{
Debug.Assert((m_cbBuf + cbSize) <= NbBlockBytes);
Array.Copy(array, ibStart, m_buf, m_cbBuf, cbSize);
m_cbBuf += cbSize;
}
}
protected override byte[] HashFinal()
{
if(m_f[0] != 0) { Debug.Assert(false); throw new InvalidOperationException(); }
Debug.Assert(((m_t[1] == 0) && (m_t[0] == 0)) ||
(m_cbBuf > 0)); // Buffer must not be empty for last block processing
m_f[0] = ulong.MaxValue; // Indicate last block
int cbFill = NbBlockBytes - m_cbBuf;
if(cbFill > 0) Array.Clear(m_buf, m_cbBuf, cbFill);
IncrementCounter((ulong)m_cbBuf);
Compress(m_buf, 0);
byte[] pbHash = new byte[NbMaxOutBytes];
for(int i = 0; i < m_h.Length; ++i)
MemUtil.UInt64ToBytesEx(m_h[i], pbHash, i << 3);
if(m_cbHashLength == NbMaxOutBytes) return pbHash;
Debug.Assert(m_cbHashLength < NbMaxOutBytes);
byte[] pbShort = new byte[m_cbHashLength];
if(m_cbHashLength > 0)
Array.Copy(pbHash, pbShort, m_cbHashLength);
MemUtil.ZeroByteArray(pbHash);
return pbShort;
}
}
}

View File

@ -0,0 +1,270 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
#if KeePassUAP
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
#else
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Native;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.KeyDerivation
{
public sealed class AesKdf : KdfEngine
{
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
0xC9, 0xD9, 0xF3, 0x9A, 0x62, 0x8A, 0x44, 0x60,
0xBF, 0x74, 0x0D, 0x08, 0xC1, 0x8A, 0x4F, 0xEA });
public const string ParamRounds = "R"; // UInt64
public const string ParamSeed = "S"; // Byte[32]
public override PwUuid Uuid
{
get { return g_uuid; }
}
public override string Name
{
get { return "AES-KDF"; }
}
public AesKdf()
{
}
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
p.SetUInt64(ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
return p;
}
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(g_uuid.Equals(p.KdfUuid));
byte[] pbSeed = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSeed, pbSeed);
}
public override byte[] Transform(byte[] pbMsg, KdfParameters p)
{
if(pbMsg == null) throw new ArgumentNullException("pbMsg");
if(p == null) throw new ArgumentNullException("p");
Type tRounds = p.GetTypeOf(ParamRounds);
if(tRounds == null) throw new ArgumentNullException("p.Rounds");
if(tRounds != typeof(ulong)) throw new ArgumentOutOfRangeException("p.Rounds");
ulong uRounds = p.GetUInt64(ParamRounds, 0);
byte[] pbSeed = p.GetByteArray(ParamSeed);
if(pbSeed == null) throw new ArgumentNullException("p.Seed");
if(pbMsg.Length != 32)
{
Debug.Assert(false);
pbMsg = CryptoUtil.HashSha256(pbMsg);
}
if(pbSeed.Length != 32)
{
Debug.Assert(false);
pbSeed = CryptoUtil.HashSha256(pbSeed);
}
return TransformKey(pbMsg, pbSeed, uRounds);
}
private static byte[] TransformKey(byte[] pbOriginalKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
Debug.Assert((pbOriginalKey32 != null) && (pbOriginalKey32.Length == 32));
if(pbOriginalKey32 == null) throw new ArgumentNullException("pbOriginalKey32");
if(pbOriginalKey32.Length != 32) throw new ArgumentException();
Debug.Assert((pbKeySeed32 != null) && (pbKeySeed32.Length == 32));
if(pbKeySeed32 == null) throw new ArgumentNullException("pbKeySeed32");
if(pbKeySeed32.Length != 32) throw new ArgumentException();
byte[] pbNewKey = new byte[32];
Array.Copy(pbOriginalKey32, pbNewKey, pbNewKey.Length);
try
{
// Try to use the native library first
if(NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
if(TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds))
return CryptoUtil.HashSha256(pbNewKey);
}
finally { MemUtil.ZeroByteArray(pbNewKey); }
return null;
}
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
#if KeePassUAP
KeyParameter kp = new KeyParameter(pbKeySeed32);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
for(ulong i = 0; i < uNumRounds; ++i)
{
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
}
#else
byte[] pbIV = new byte[16];
Array.Clear(pbIV, 0, pbIV.Length);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKeySeed32;
ICryptoTransform iCrypt = r.CreateEncryptor();
// !iCrypt.CanReuseTransform -- doesn't work with Mono
if((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
(iCrypt.OutputBlockSize != 16))
{
Debug.Assert(false, "Invalid ICryptoTransform.");
Debug.Assert((iCrypt.InputBlockSize == 16), "Invalid input block size!");
Debug.Assert((iCrypt.OutputBlockSize == 16), "Invalid output block size!");
return false;
}
for(ulong i = 0; i < uNumRounds; ++i)
{
iCrypt.TransformBlock(pbNewKey32, 0, 16, pbNewKey32, 0);
iCrypt.TransformBlock(pbNewKey32, 16, 16, pbNewKey32, 16);
}
#endif
return true;
}
public override KdfParameters GetBestParameters(uint uMilliseconds)
{
const ulong uStep = 3001;
ulong uRounds;
KdfParameters p = GetDefaultParameters();
// Try native method
if(NativeLib.TransformKeyBenchmark256(uMilliseconds, out uRounds))
{
p.SetUInt64(ParamRounds, uRounds);
return p;
}
byte[] pbKey = new byte[32];
byte[] pbNewKey = new byte[32];
for(int i = 0; i < pbKey.Length; ++i)
{
pbKey[i] = (byte)i;
pbNewKey[i] = (byte)i;
}
#if KeePassUAP
KeyParameter kp = new KeyParameter(pbKey);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
#else
byte[] pbIV = new byte[16];
Array.Clear(pbIV, 0, pbIV.Length);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKey;
ICryptoTransform iCrypt = r.CreateEncryptor();
// !iCrypt.CanReuseTransform -- doesn't work with Mono
if((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
(iCrypt.OutputBlockSize != 16))
{
Debug.Assert(false, "Invalid ICryptoTransform.");
Debug.Assert(iCrypt.InputBlockSize == 16, "Invalid input block size!");
Debug.Assert(iCrypt.OutputBlockSize == 16, "Invalid output block size!");
p.SetUInt64(ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
return p;
}
#endif
uRounds = 0;
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < uStep; ++j)
{
#if KeePassUAP
aes.ProcessBlock(pbNewKey, 0, pbNewKey, 0);
aes.ProcessBlock(pbNewKey, 16, pbNewKey, 16);
#else
iCrypt.TransformBlock(pbNewKey, 0, 16, pbNewKey, 0);
iCrypt.TransformBlock(pbNewKey, 16, 16, pbNewKey, 16);
#endif
}
uRounds += uStep;
if(uRounds < uStep) // Overflow check
{
uRounds = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > uMilliseconds) break;
}
p.SetUInt64(ParamRounds, uRounds);
return p;
}
}
}

View File

@ -0,0 +1,610 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// This implementation is based on the official reference C
// implementation by Daniel Dinu and Dmitry Khovratovich (CC0 1.0).
// Relative iterations (* = B2ROUND_ARRAYS \\ G_INLINED):
// * | false true
// ------+-----------
// false | 8885 9618
// true | 9009 9636
#define ARGON2_B2ROUND_ARRAYS
#define ARGON2_G_INLINED
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using KeePassLib.Cryptography.Hash;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.KeyDerivation
{
public sealed partial class Argon2Kdf : KdfEngine
{
private const ulong NbBlockSize = 1024;
private const ulong NbBlockSizeInQW = NbBlockSize / 8UL;
private const ulong NbSyncPoints = 4;
private const int NbPreHashDigestLength = 64;
private const int NbPreHashSeedLength = NbPreHashDigestLength + 8;
#if ARGON2_B2ROUND_ARRAYS
private static int[][] g_vFBCols = null;
private static int[][] g_vFBRows = null;
#endif
private sealed class Argon2Ctx
{
public uint Version = 0;
public ulong Lanes = 0;
public ulong TCost = 0;
public ulong MCost = 0;
public ulong MemoryBlocks = 0;
public ulong SegmentLength = 0;
public ulong LaneLength = 0;
public ulong[] Mem = null;
}
private sealed class Argon2ThreadInfo
{
public Argon2Ctx Context = null;
public ManualResetEvent Finished = new ManualResetEvent(false);
public ulong Pass = 0;
public ulong Lane = 0;
public ulong Slice = 0;
public ulong Index = 0;
}
private static byte[] Argon2d(byte[] pbMsg, byte[] pbSalt, uint uParallel,
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
byte[] pbAssocData)
{
pbSecretKey = (pbSecretKey ?? MemUtil.EmptyByteArray);
pbAssocData = (pbAssocData ?? MemUtil.EmptyByteArray);
#if ARGON2_B2ROUND_ARRAYS
InitB2RoundIndexArrays();
#endif
Argon2Ctx ctx = new Argon2Ctx();
ctx.Version = uVersion;
ctx.Lanes = uParallel;
ctx.TCost = uIt;
ctx.MCost = uMem / NbBlockSize;
ctx.MemoryBlocks = Math.Max(ctx.MCost, 2UL * NbSyncPoints * ctx.Lanes);
ctx.SegmentLength = ctx.MemoryBlocks / (ctx.Lanes * NbSyncPoints);
ctx.MemoryBlocks = ctx.SegmentLength * ctx.Lanes * NbSyncPoints;
ctx.LaneLength = ctx.SegmentLength * NbSyncPoints;
Debug.Assert(NbBlockSize == (NbBlockSizeInQW *
(ulong)Marshal.SizeOf(typeof(ulong))));
ctx.Mem = new ulong[ctx.MemoryBlocks * NbBlockSizeInQW];
Blake2b h = new Blake2b();
// Initial hash
Debug.Assert(h.HashSize == (NbPreHashDigestLength * 8));
byte[] pbBuf = new byte[4];
MemUtil.UInt32ToBytesEx(uParallel, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)cbOut, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)ctx.MCost, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)uIt, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx(uVersion, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx(0, pbBuf, 0); // Argon2d type = 0
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)pbMsg.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0);
MemUtil.UInt32ToBytesEx((uint)pbSalt.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbSalt, 0, pbSalt.Length, pbSalt, 0);
MemUtil.UInt32ToBytesEx((uint)pbSecretKey.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbSecretKey, 0, pbSecretKey.Length, pbSecretKey, 0);
MemUtil.UInt32ToBytesEx((uint)pbAssocData.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
h.TransformBlock(pbAssocData, 0, pbAssocData.Length, pbAssocData, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
byte[] pbH0 = h.Hash;
Debug.Assert(pbH0.Length == 64);
byte[] pbBlockHash = new byte[NbPreHashSeedLength];
Array.Copy(pbH0, pbBlockHash, pbH0.Length);
MemUtil.ZeroByteArray(pbH0);
FillFirstBlocks(ctx, pbBlockHash, h);
MemUtil.ZeroByteArray(pbBlockHash);
FillMemoryBlocks(ctx);
byte[] pbOut = FinalHash(ctx, cbOut, h);
h.Clear();
MemUtil.ZeroArray<ulong>(ctx.Mem);
return pbOut;
}
private static void LoadBlock(ulong[] pqDst, ulong uDstOffset, byte[] pbIn)
{
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
// pqDst[uDstOffset + i] = MemUtil.BytesToUInt64(pbIn, (int)(i << 3));
Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
int iDstOffset = (int)uDstOffset;
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
pqDst[iDstOffset + i] = MemUtil.BytesToUInt64(pbIn, i << 3);
}
private static void StoreBlock(byte[] pbDst, ulong[] pqSrc)
{
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
MemUtil.UInt64ToBytesEx(pqSrc[i], pbDst, i << 3);
}
private static void CopyBlock(ulong[] vDst, ulong uDstOffset, ulong[] vSrc,
ulong uSrcOffset)
{
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
// vDst[uDstOffset + i] = vSrc[uSrcOffset + i];
// Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
// Debug.Assert((uSrcOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
// int iDstOffset = (int)uDstOffset;
// int iSrcOffset = (int)uSrcOffset;
// for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
// vDst[iDstOffset + i] = vSrc[iSrcOffset + i];
Array.Copy(vSrc, (long)uSrcOffset, vDst, (long)uDstOffset,
(long)NbBlockSizeInQW);
}
private static void XorBlock(ulong[] vDst, ulong uDstOffset, ulong[] vSrc,
ulong uSrcOffset)
{
// for(ulong i = 0; i < NbBlockSizeInQW; ++i)
// vDst[uDstOffset + i] ^= vSrc[uSrcOffset + i];
Debug.Assert((uDstOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
Debug.Assert((uSrcOffset + NbBlockSizeInQW - 1UL) <= (ulong)int.MaxValue);
int iDstOffset = (int)uDstOffset;
int iSrcOffset = (int)uSrcOffset;
for(int i = 0; i < (int)NbBlockSizeInQW; ++i)
vDst[iDstOffset + i] ^= vSrc[iSrcOffset + i];
}
private static void Blake2bLong(byte[] pbOut, int cbOut,
byte[] pbIn, int cbIn, Blake2b h)
{
Debug.Assert((h != null) && (h.HashSize == (64 * 8)));
byte[] pbOutLen = new byte[4];
MemUtil.UInt32ToBytesEx((uint)cbOut, pbOutLen, 0);
if(cbOut <= 64)
{
Blake2b hOut = ((cbOut == 64) ? h : new Blake2b(cbOut));
if(cbOut == 64) hOut.Initialize();
hOut.TransformBlock(pbOutLen, 0, pbOutLen.Length, pbOutLen, 0);
hOut.TransformBlock(pbIn, 0, cbIn, pbIn, 0);
hOut.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
Array.Copy(hOut.Hash, pbOut, cbOut);
if(cbOut < 64) hOut.Clear();
return;
}
h.Initialize();
h.TransformBlock(pbOutLen, 0, pbOutLen.Length, pbOutLen, 0);
h.TransformBlock(pbIn, 0, cbIn, pbIn, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
byte[] pbOutBuffer = new byte[64];
Array.Copy(h.Hash, pbOutBuffer, pbOutBuffer.Length);
int ibOut = 64 / 2;
Array.Copy(pbOutBuffer, pbOut, ibOut);
int cbToProduce = cbOut - ibOut;
h.Initialize();
while(cbToProduce > 64)
{
byte[] pbHash = h.ComputeHash(pbOutBuffer);
Array.Copy(pbHash, pbOutBuffer, 64);
Array.Copy(pbHash, 0, pbOut, ibOut, 64 / 2);
ibOut += 64 / 2;
cbToProduce -= 64 / 2;
MemUtil.ZeroByteArray(pbHash);
}
using(Blake2b hOut = new Blake2b(cbToProduce))
{
byte[] pbHash = hOut.ComputeHash(pbOutBuffer);
Array.Copy(pbHash, 0, pbOut, ibOut, cbToProduce);
MemUtil.ZeroByteArray(pbHash);
}
MemUtil.ZeroByteArray(pbOutBuffer);
}
#if !ARGON2_G_INLINED
private static ulong BlaMka(ulong x, ulong y)
{
ulong xy = (x & 0xFFFFFFFFUL) * (y & 0xFFFFFFFFUL);
return (x + y + (xy << 1));
}
private static void G(ulong[] v, int a, int b, int c, int d)
{
ulong va = v[a], vb = v[b], vc = v[c], vd = v[d];
va = BlaMka(va, vb);
vd = MemUtil.RotateRight64(vd ^ va, 32);
vc = BlaMka(vc, vd);
vb = MemUtil.RotateRight64(vb ^ vc, 24);
va = BlaMka(va, vb);
vd = MemUtil.RotateRight64(vd ^ va, 16);
vc = BlaMka(vc, vd);
vb = MemUtil.RotateRight64(vb ^ vc, 63);
v[a] = va;
v[b] = vb;
v[c] = vc;
v[d] = vd;
}
#else
private static void G(ulong[] v, int a, int b, int c, int d)
{
ulong va = v[a], vb = v[b], vc = v[c], vd = v[d];
ulong xy = (va & 0xFFFFFFFFUL) * (vb & 0xFFFFFFFFUL);
va += vb + (xy << 1);
vd = MemUtil.RotateRight64(vd ^ va, 32);
xy = (vc & 0xFFFFFFFFUL) * (vd & 0xFFFFFFFFUL);
vc += vd + (xy << 1);
vb = MemUtil.RotateRight64(vb ^ vc, 24);
xy = (va & 0xFFFFFFFFUL) * (vb & 0xFFFFFFFFUL);
va += vb + (xy << 1);
vd = MemUtil.RotateRight64(vd ^ va, 16);
xy = (vc & 0xFFFFFFFFUL) * (vd & 0xFFFFFFFFUL);
vc += vd + (xy << 1);
vb = MemUtil.RotateRight64(vb ^ vc, 63);
v[a] = va;
v[b] = vb;
v[c] = vc;
v[d] = vd;
}
#endif
#if ARGON2_B2ROUND_ARRAYS
private static void Blake2RoundNoMsg(ulong[] pbR, int[] v)
{
G(pbR, v[0], v[4], v[8], v[12]);
G(pbR, v[1], v[5], v[9], v[13]);
G(pbR, v[2], v[6], v[10], v[14]);
G(pbR, v[3], v[7], v[11], v[15]);
G(pbR, v[0], v[5], v[10], v[15]);
G(pbR, v[1], v[6], v[11], v[12]);
G(pbR, v[2], v[7], v[8], v[13]);
G(pbR, v[3], v[4], v[9], v[14]);
}
#else
private static void Blake2RoundNoMsgCols16i(ulong[] pbR, int i)
{
G(pbR, i, i + 4, i + 8, i + 12);
G(pbR, i + 1, i + 5, i + 9, i + 13);
G(pbR, i + 2, i + 6, i + 10, i + 14);
G(pbR, i + 3, i + 7, i + 11, i + 15);
G(pbR, i, i + 5, i + 10, i + 15);
G(pbR, i + 1, i + 6, i + 11, i + 12);
G(pbR, i + 2, i + 7, i + 8, i + 13);
G(pbR, i + 3, i + 4, i + 9, i + 14);
}
private static void Blake2RoundNoMsgRows2i(ulong[] pbR, int i)
{
G(pbR, i, i + 32, i + 64, i + 96);
G(pbR, i + 1, i + 33, i + 65, i + 97);
G(pbR, i + 16, i + 48, i + 80, i + 112);
G(pbR, i + 17, i + 49, i + 81, i + 113);
G(pbR, i, i + 33, i + 80, i + 113);
G(pbR, i + 1, i + 48, i + 81, i + 96);
G(pbR, i + 16, i + 49, i + 64, i + 97);
G(pbR, i + 17, i + 32, i + 65, i + 112);
}
#endif
private static void FillFirstBlocks(Argon2Ctx ctx, byte[] pbBlockHash,
Blake2b h)
{
byte[] pbBlock = new byte[NbBlockSize];
for(ulong l = 0; l < ctx.Lanes; ++l)
{
MemUtil.UInt32ToBytesEx(0, pbBlockHash, NbPreHashDigestLength);
MemUtil.UInt32ToBytesEx((uint)l, pbBlockHash, NbPreHashDigestLength + 4);
Blake2bLong(pbBlock, (int)NbBlockSize, pbBlockHash,
NbPreHashSeedLength, h);
LoadBlock(ctx.Mem, l * ctx.LaneLength * NbBlockSizeInQW, pbBlock);
MemUtil.UInt32ToBytesEx(1, pbBlockHash, NbPreHashDigestLength);
Blake2bLong(pbBlock, (int)NbBlockSize, pbBlockHash,
NbPreHashSeedLength, h);
LoadBlock(ctx.Mem, (l * ctx.LaneLength + 1UL) * NbBlockSizeInQW, pbBlock);
}
MemUtil.ZeroByteArray(pbBlock);
}
private static ulong IndexAlpha(Argon2Ctx ctx, Argon2ThreadInfo ti,
uint uPseudoRand, bool bSameLane)
{
ulong uRefAreaSize;
if(ti.Pass == 0)
{
if(ti.Slice == 0)
{
Debug.Assert(ti.Index > 0);
uRefAreaSize = ti.Index - 1UL;
}
else
{
if(bSameLane)
uRefAreaSize = ti.Slice * ctx.SegmentLength +
ti.Index - 1UL;
else
uRefAreaSize = ti.Slice * ctx.SegmentLength -
((ti.Index == 0UL) ? 1UL : 0UL);
}
}
else
{
if(bSameLane)
uRefAreaSize = ctx.LaneLength - ctx.SegmentLength +
ti.Index - 1UL;
else
uRefAreaSize = ctx.LaneLength - ctx.SegmentLength -
((ti.Index == 0) ? 1UL : 0UL);
}
Debug.Assert(uRefAreaSize <= (ulong)uint.MaxValue);
ulong uRelPos = uPseudoRand;
uRelPos = (uRelPos * uRelPos) >> 32;
uRelPos = uRefAreaSize - 1UL - ((uRefAreaSize * uRelPos) >> 32);
ulong uStart = 0;
if(ti.Pass != 0)
uStart = (((ti.Slice + 1UL) == NbSyncPoints) ? 0UL :
((ti.Slice + 1UL) * ctx.SegmentLength));
Debug.Assert(uStart <= (ulong)uint.MaxValue);
Debug.Assert(ctx.LaneLength <= (ulong)uint.MaxValue);
return ((uStart + uRelPos) % ctx.LaneLength);
}
private static void FillMemoryBlocks(Argon2Ctx ctx)
{
int np = (int)ctx.Lanes;
Argon2ThreadInfo[] v = new Argon2ThreadInfo[np];
for(ulong r = 0; r < ctx.TCost; ++r)
{
for(ulong s = 0; s < NbSyncPoints; ++s)
{
for(int l = 0; l < np; ++l)
{
Argon2ThreadInfo ti = new Argon2ThreadInfo();
ti.Context = ctx;
ti.Pass = r;
ti.Lane = (ulong)l;
ti.Slice = s;
if(!ThreadPool.QueueUserWorkItem(FillSegmentThr, ti))
{
Debug.Assert(false);
throw new OutOfMemoryException();
}
v[l] = ti;
}
for(int l = 0; l < np; ++l)
v[l].Finished.WaitOne();
}
}
}
private static void FillSegmentThr(object o)
{
Argon2ThreadInfo ti = (o as Argon2ThreadInfo);
if(ti == null) { Debug.Assert(false); return; }
try
{
Argon2Ctx ctx = ti.Context;
if(ctx == null) { Debug.Assert(false); return; }
Debug.Assert(ctx.Version >= MinVersion);
bool bCanXor = (ctx.Version >= 0x13U);
ulong uStart = 0;
if((ti.Pass == 0) && (ti.Slice == 0)) uStart = 2;
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
ctx.SegmentLength) + uStart;
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
for(ulong i = uStart; i < ctx.SegmentLength; ++i)
{
if((uCur % ctx.LaneLength) == 1)
uPrev = uCur - 1UL;
ulong uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
ulong uRefLane = (uPseudoRand >> 32) % ctx.Lanes;
if((ti.Pass == 0) && (ti.Slice == 0))
uRefLane = ti.Lane;
ti.Index = i;
ulong uRefIndex = IndexAlpha(ctx, ti, (uint)uPseudoRand,
(uRefLane == ti.Lane));
ulong uRefBlockIndex = (ctx.LaneLength * uRefLane +
uRefIndex) * NbBlockSizeInQW;
ulong uCurBlockIndex = uCur * NbBlockSizeInQW;
FillBlock(ctx.Mem, uPrev * NbBlockSizeInQW, uRefBlockIndex,
uCurBlockIndex, ((ti.Pass != 0) && bCanXor), pbR, pbTmp);
++uCur;
++uPrev;
}
MemUtil.ZeroArray<ulong>(pbR);
MemUtil.ZeroArray<ulong>(pbTmp);
}
catch(Exception) { Debug.Assert(false); }
try { ti.Finished.Set(); }
catch(Exception) { Debug.Assert(false); }
}
#if ARGON2_B2ROUND_ARRAYS
private static void InitB2RoundIndexArrays()
{
int[][] vCols = g_vFBCols;
if(vCols == null)
{
vCols = new int[8][];
Debug.Assert(vCols.Length == 8);
int e = 0;
for(int i = 0; i < 8; ++i)
{
vCols[i] = new int[16];
for(int j = 0; j < 16; ++j)
{
vCols[i][j] = e;
++e;
}
}
g_vFBCols = vCols;
}
int[][] vRows = g_vFBRows;
if(vRows == null)
{
vRows = new int[8][];
for(int i = 0; i < 8; ++i)
{
vRows[i] = new int[16];
for(int j = 0; j < 16; ++j)
{
int jh = j / 2;
vRows[i][j] = (2 * i) + (16 * jh) + (j & 1);
}
}
g_vFBRows = vRows;
}
}
#endif
private static void FillBlock(ulong[] pMem, ulong uPrev, ulong uRef,
ulong uNext, bool bXor, ulong[] pbR, ulong[] pbTmp)
{
CopyBlock(pbR, 0, pMem, uRef);
XorBlock(pbR, 0, pMem, uPrev);
CopyBlock(pbTmp, 0, pbR, 0);
if(bXor) XorBlock(pbTmp, 0, pMem, uNext);
#if ARGON2_B2ROUND_ARRAYS
int[][] vCols = g_vFBCols;
int[][] vRows = g_vFBRows;
for(int i = 0; i < 8; ++i)
Blake2RoundNoMsg(pbR, vCols[i]);
for(int i = 0; i < 8; ++i)
Blake2RoundNoMsg(pbR, vRows[i]);
#else
for(int i = 0; i < (8 * 16); i += 16)
Blake2RoundNoMsgCols16i(pbR, i);
for(int i = 0; i < (8 * 2); i += 2)
Blake2RoundNoMsgRows2i(pbR, i);
#endif
CopyBlock(pMem, uNext, pbTmp, 0);
XorBlock(pMem, uNext, pbR, 0);
}
private static byte[] FinalHash(Argon2Ctx ctx, int cbOut, Blake2b h)
{
ulong[] pqBlockHash = new ulong[NbBlockSizeInQW];
CopyBlock(pqBlockHash, 0, ctx.Mem, (ctx.LaneLength - 1UL) *
NbBlockSizeInQW);
for(ulong l = 1; l < ctx.Lanes; ++l)
XorBlock(pqBlockHash, 0, ctx.Mem, (l * ctx.LaneLength +
ctx.LaneLength - 1UL) * NbBlockSizeInQW);
byte[] pbBlockHashBytes = new byte[NbBlockSize];
StoreBlock(pbBlockHashBytes, pqBlockHash);
byte[] pbOut = new byte[cbOut];
Blake2bLong(pbOut, cbOut, pbBlockHashBytes, (int)NbBlockSize, h);
MemUtil.ZeroArray<ulong>(pqBlockHash);
MemUtil.ZeroByteArray(pbBlockHashBytes);
return pbOut;
}
}
}

View File

@ -0,0 +1,144 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace KeePassLib.Cryptography.KeyDerivation
{
public sealed partial class Argon2Kdf : KdfEngine
{
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
public const string ParamSalt = "S"; // Byte[]
public const string ParamParallelism = "P"; // UInt32
public const string ParamMemory = "M"; // UInt64
public const string ParamIterations = "I"; // UInt64
public const string ParamVersion = "V"; // UInt32
public const string ParamSecretKey = "K"; // Byte[]
public const string ParamAssocData = "A"; // Byte[]
private const uint MinVersion = 0x10;
private const uint MaxVersion = 0x13;
private const int MinSalt = 8;
private const int MaxSalt = int.MaxValue; // .NET limit; 2^32 - 1 in spec
internal const ulong MinIterations = 1;
internal const ulong MaxIterations = uint.MaxValue;
internal const ulong MinMemory = 1024 * 8; // For parallelism = 1
// internal const ulong MaxMemory = (ulong)uint.MaxValue * 1024UL; // Spec
internal const ulong MaxMemory = int.MaxValue; // .NET limit
internal const uint MinParallelism = 1;
internal const uint MaxParallelism = (1 << 24) - 1;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 1024 * 1024; // 1 MB
internal const uint DefaultParallelism = 2;
public override PwUuid Uuid
{
get { return g_uuid; }
}
public override string Name
{
get { return "Argon2"; }
}
public Argon2Kdf()
{
}
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
p.SetUInt32(ParamVersion, MaxVersion);
p.SetUInt64(ParamIterations, DefaultIterations);
p.SetUInt64(ParamMemory, DefaultMemory);
p.SetUInt32(ParamParallelism, DefaultParallelism);
return p;
}
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(g_uuid.Equals(p.KdfUuid));
byte[] pb = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSalt, pb);
}
public override byte[] Transform(byte[] pbMsg, KdfParameters p)
{
if(pbMsg == null) throw new ArgumentNullException("pbMsg");
if(p == null) throw new ArgumentNullException("p");
byte[] pbSalt = p.GetByteArray(ParamSalt);
if(pbSalt == null)
throw new ArgumentNullException("p.Salt");
if((pbSalt.Length < MinSalt) || (pbSalt.Length > MaxSalt))
throw new ArgumentOutOfRangeException("p.Salt");
uint uPar = p.GetUInt32(ParamParallelism, 0);
if((uPar < MinParallelism) || (uPar > MaxParallelism))
throw new ArgumentOutOfRangeException("p.Parallelism");
ulong uMem = p.GetUInt64(ParamMemory, 0);
if((uMem < MinMemory) || (uMem > MaxMemory))
throw new ArgumentOutOfRangeException("p.Memory");
ulong uIt = p.GetUInt64(ParamIterations, 0);
if((uIt < MinIterations) || (uIt > MaxIterations))
throw new ArgumentOutOfRangeException("p.Iterations");
uint v = p.GetUInt32(ParamVersion, 0);
if((v < MinVersion) || (v > MaxVersion))
throw new ArgumentOutOfRangeException("p.Version");
byte[] pbSecretKey = p.GetByteArray(ParamSecretKey);
byte[] pbAssocData = p.GetByteArray(ParamAssocData);
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
return pbRet;
}
public override KdfParameters GetBestParameters(uint uMilliseconds)
{
KdfParameters p = GetDefaultParameters();
Randomize(p);
MaximizeParamUInt64(p, ParamIterations, MinIterations,
MaxIterations, uMilliseconds, true);
return p;
}
}
}

View File

@ -0,0 +1,142 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace KeePassLib.Cryptography.KeyDerivation
{
public abstract class KdfEngine
{
public abstract PwUuid Uuid
{
get;
}
public abstract string Name
{
get;
}
public virtual KdfParameters GetDefaultParameters()
{
return new KdfParameters(this.Uuid);
}
/// <summary>
/// Generate random seeds and store them in <paramref name="p" />.
/// </summary>
public virtual void Randomize(KdfParameters p)
{
Debug.Assert(p != null);
Debug.Assert(p.KdfUuid.Equals(this.Uuid));
}
public abstract byte[] Transform(byte[] pbMsg, KdfParameters p);
public virtual KdfParameters GetBestParameters(uint uMilliseconds)
{
throw new NotImplementedException();
}
protected void MaximizeParamUInt64(KdfParameters p, string strName,
ulong uMin, ulong uMax, uint uMilliseconds, bool bInterpSearch)
{
if(p == null) { Debug.Assert(false); return; }
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; }
if(uMin > uMax) { Debug.Assert(false); return; }
if(uMax > (ulong.MaxValue >> 1))
{
Debug.Assert(false);
uMax = ulong.MaxValue >> 1;
if(uMin > uMax) { p.SetUInt64(strName, uMin); return; }
}
byte[] pbMsg = new byte[32];
for(int i = 0; i < pbMsg.Length; ++i) pbMsg[i] = (byte)i;
ulong uLow = uMin;
ulong uHigh = uMin + 1UL;
long tLow = 0;
long tHigh = 0;
long tTarget = (long)uMilliseconds;
// Determine range
while(uHigh <= uMax)
{
p.SetUInt64(strName, uHigh);
// GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
Transform(pbMsg, p);
sw.Stop();
tHigh = sw.ElapsedMilliseconds;
if(tHigh > tTarget) break;
uLow = uHigh;
tLow = tHigh;
uHigh <<= 1;
}
if(uHigh > uMax) { uHigh = uMax; tHigh = 0; }
if(uLow > uHigh) uLow = uHigh; // Skips to end
// Find optimal number of iterations
while((uHigh - uLow) >= 2UL)
{
ulong u = (uHigh + uLow) >> 1; // Binary search
// Interpolation search, if possible
if(bInterpSearch && (tLow > 0) && (tHigh > tTarget) &&
(tLow <= tTarget))
{
u = uLow + (((uHigh - uLow) * (ulong)(tTarget - tLow)) /
(ulong)(tHigh - tLow));
if((u >= uLow) && (u <= uHigh))
{
u = Math.Max(u, uLow + 1UL);
u = Math.Min(u, uHigh - 1UL);
}
else
{
Debug.Assert(false);
u = (uHigh + uLow) >> 1;
}
}
p.SetUInt64(strName, u);
// GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
Transform(pbMsg, p);
sw.Stop();
long t = sw.ElapsedMilliseconds;
if(t == tTarget) { uLow = u; break; }
else if(t > tTarget) { uHigh = u; tHigh = t; }
else { uLow = u; tLow = t; }
}
p.SetUInt64(strName, uLow);
}
}
}

View File

@ -0,0 +1,80 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using KeePassLib.Collections;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.KeyDerivation
{
public sealed class KdfParameters : VariantDictionary
{
private const string ParamUuid = @"$UUID";
private readonly PwUuid m_puKdf;
public PwUuid KdfUuid
{
get { return m_puKdf; }
}
public KdfParameters(PwUuid puKdf)
{
if(puKdf == null) throw new ArgumentNullException("puKdf");
m_puKdf = puKdf;
SetByteArray(ParamUuid, puKdf.UuidBytes);
}
/// <summary>
/// Unsupported.
/// </summary>
public override object Clone()
{
throw new NotSupportedException();
}
public static byte[] SerializeExt(KdfParameters p)
{
return VariantDictionary.Serialize(p);
}
public static KdfParameters DeserializeExt(byte[] pb)
{
VariantDictionary d = VariantDictionary.Deserialize(pb);
if(d == null) { Debug.Assert(false); return null; }
byte[] pbUuid = d.GetByteArray(ParamUuid);
if((pbUuid == null) || (pbUuid.Length != (int)PwUuid.UuidSize))
{
Debug.Assert(false);
return null;
}
PwUuid pu = new PwUuid(pbUuid);
KdfParameters p = new KdfParameters(pu);
d.CopyTo(p);
return p;
}
}
}

View File

@ -0,0 +1,96 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.KeyDerivation
{
public static class KdfPool
{
private static List<KdfEngine> g_l = new List<KdfEngine>();
public static IEnumerable<KdfEngine> Engines
{
get
{
EnsureInitialized();
return g_l;
}
}
private static void EnsureInitialized()
{
if(g_l.Count > 0) return;
g_l.Add(new AesKdf());
g_l.Add(new Argon2Kdf());
}
internal static KdfParameters GetDefaultParameters()
{
EnsureInitialized();
return g_l[0].GetDefaultParameters();
}
public static KdfEngine Get(PwUuid pu)
{
if(pu == null) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(pu.Equals(kdf.Uuid)) return kdf;
}
return null;
}
public static KdfEngine Get(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(KdfEngine kdf in g_l)
{
if(strName.Equals(kdf.Name, StrUtil.CaseIgnoreCmp)) return kdf;
}
return null;
}
public static void Add(KdfEngine kdf)
{
if(kdf == null) { Debug.Assert(false); return; }
EnsureInitialized();
if(Get(kdf.Uuid) != null) { Debug.Assert(false); return; }
if(Get(kdf.Name) != null) { Debug.Assert(false); return; }
g_l.Add(kdf);
}
}
}

View File

@ -60,6 +60,18 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Collections\VariantDictionary.cs" />
<Compile Include="Cryptography\Cipher\ChaCha20Cipher.cs" />
<Compile Include="Cryptography\Cipher\ChaCha20Engine.cs" />
<Compile Include="Cryptography\Cipher\CtrBlockCipher.cs" />
<Compile Include="Cryptography\CryptoUtil.cs" />
<Compile Include="Cryptography\Hash\Blake2b.cs" />
<Compile Include="Cryptography\KeyDerivation\AesKdf.cs" />
<Compile Include="Cryptography\KeyDerivation\Argon2Kdf.Core.cs" />
<Compile Include="Cryptography\KeyDerivation\Argon2Kdf.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfEngine.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfParameters.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfPool.cs" />
<Compile Include="IDatabaseFormat.cs" />
<Compile Include="Kp2aLog.cs" />
<Compile Include="Resources\Resource.designer.cs" />
@ -87,7 +99,6 @@
<Compile Include="Cryptography\PasswordGenerator\PwProfile.cs" />
<Compile Include="Cryptography\PopularPasswords.cs" />
<Compile Include="Cryptography\QualityEstimation.cs" />
<Compile Include="Cryptography\SelfTest.cs" />
<Compile Include="Cryptography\PasswordGenerator\PwGenerator.cs" />
<Compile Include="PwCustomIcon.cs" />
<Compile Include="PwDatabase.cs" />
@ -124,9 +135,13 @@
<Compile Include="Serialization\FileLock.cs" />
<Compile Include="Serialization\FileTransactionEx.cs" />
<Compile Include="Serialization\HashedBlockStream.cs" />
<Compile Include="Serialization\HmacBlockStream.cs" />
<Compile Include="Serialization\IOConnection.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Serialization\IocProperties.cs" />
<Compile Include="Serialization\IocPropertyInfo.cs" />
<Compile Include="Serialization\IocPropertyInfoPool.cs" />
<Compile Include="Serialization\KdbxFile.cs" />
<Compile Include="Serialization\KdbxFile.Read.cs" />
<Compile Include="Serialization\KdbxFile.Read.Streamed.cs" />
@ -134,8 +149,6 @@
<Compile Include="Serialization\IOConnectionInfo.cs" />
<Compile Include="Serialization\OldFormatException.cs" />
<Compile Include="Serialization\ProtoBuf\KdbpFile.cs" />
<Compile Include="Translation\KPControlCustomization.cs" />
<Compile Include="Translation\KPFormCustomization.cs" />
<Compile Include="Translation\KPStringTable.cs" />
<Compile Include="Translation\KPStringTableItem.cs" />
<Compile Include="Translation\KPTranslation.cs" />
@ -144,6 +157,7 @@
<Compile Include="Utility\GfxUtil.cs" />
<Compile Include="Utility\MemUtil.cs" />
<Compile Include="Utility\MessageService.cs" />
<Compile Include="Utility\MonoWorkarounds.cs" />
<Compile Include="Utility\StrUtil.cs" />
<Compile Include="Utility\UrlUtil.cs" />
<Compile Include="Utility\TimeUtil.cs" />

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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,22 +18,16 @@
*/
using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
#if KeePassRT
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
#endif
using System.Text;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Native;
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
using keepass2android;
namespace KeePassLib.Keys
{
@ -109,7 +103,6 @@ namespace KeePassLib.Keys
return m_vUserKeys.Remove(pKey);
}
#if !KeePassRT
/// <summary>
/// Test whether the composite key contains a specific type of
/// user keys (password, key file, ...). If at least one user
@ -125,8 +118,15 @@ namespace KeePassLib.Keys
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return true;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return true;
#endif
}
return false;
@ -145,8 +145,15 @@ namespace KeePassLib.Keys
foreach(IUserKey pKey in m_vUserKeys)
{
if(pKey == null) { Debug.Assert(false); continue; }
#if KeePassUAP
if(pKey.GetType() == tUserKeyType)
return pKey;
#else
if(tUserKeyType.IsInstanceOfType(pKey))
return pKey;
#endif
}
return null;
@ -156,8 +163,6 @@ namespace KeePassLib.Keys
{
return (T) GetUserKey(typeof (T));
}
#endif
/// <summary>
/// Creates the composite key from the supplied user key sources (password,
/// key file, user account, computer ID, etc.).
@ -167,21 +172,31 @@ namespace KeePassLib.Keys
ValidateUserKeys();
// Concatenate user key data
MemoryStream ms = new MemoryStream();
List<byte[]> lData = new List<byte[]>();
int cbData = 0;
foreach(IUserKey pKey in m_vUserKeys)
{
ProtectedBinary b = pKey.KeyData;
if(b != null)
{
byte[] pbKeyData = b.ReadData();
ms.Write(pbKeyData, 0, pbKeyData.Length);
MemUtil.ZeroByteArray(pbKeyData);
lData.Add(pbKeyData);
cbData += pbKeyData.Length;
}
}
SHA256Managed sha256 = new SHA256Managed();
byte[] pbHash = sha256.ComputeHash(ms.ToArray());
ms.Close();
byte[] pbAllData = new byte[cbData];
int p = 0;
foreach(byte[] pbData in lData)
{
Array.Copy(pbData, 0, pbAllData, p, pbData.Length);
p += pbData.Length;
MemUtil.ZeroByteArray(pbData);
}
Debug.Assert(p == cbData);
byte[] pbHash = CryptoUtil.HashSha256(pbAllData);
MemUtil.ZeroByteArray(pbAllData);
return pbHash;
}
@ -192,21 +207,13 @@ namespace KeePassLib.Keys
byte[] pbThis = CreateRawCompositeKey32();
byte[] pbOther = ckOther.CreateRawCompositeKey32();
bool bResult = MemUtil.ArraysEqual(pbThis, pbOther);
Array.Clear(pbOther, 0, pbOther.Length);
Array.Clear(pbThis, 0, pbThis.Length);
MemUtil.ZeroByteArray(pbOther);
MemUtil.ZeroByteArray(pbThis);
return bResult;
}
/// <summary>
/// Generate a 32-bit wide key out of the composite key.
/// </summary>
/// <param name="pbKeySeed32">Seed used in the key transformation
/// rounds. Must be a byte array containing exactly 32 bytes; must
/// not be null.</param>
/// <param name="uNumRounds">Number of key transformation rounds.</param>
/// <returns>Returns a protected binary object that contains the
/// resulting 32-bit wide key.</returns>
[Obsolete]
public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds)
{
Debug.Assert(pbKeySeed32 != null);
@ -214,18 +221,43 @@ namespace KeePassLib.Keys
Debug.Assert(pbKeySeed32.Length == 32);
if(pbKeySeed32.Length != 32) throw new ArgumentException("pbKeySeed32");
AesKdf kdf = new AesKdf();
KdfParameters p = kdf.GetDefaultParameters();
p.SetUInt64(AesKdf.ParamRounds, uNumRounds);
p.SetByteArray(AesKdf.ParamSeed, pbKeySeed32);
return GenerateKey32(p);
}
/// <summary>
/// Generate a 32-byte (256-bit) key from the composite key.
/// </summary>
public ProtectedBinary GenerateKey32(KdfParameters p)
{
if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
byte[] pbRaw32 = CreateRawCompositeKey32();
if((pbRaw32 == null) || (pbRaw32.Length != 32))
{ Debug.Assert(false); return null; }
byte[] pbTrf32 = TransformKey(pbRaw32, pbKeySeed32, uNumRounds);
if((pbTrf32 == null) || (pbTrf32.Length != 32))
{ Debug.Assert(false); return null; }
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
"UUID: " + p.KdfUuid.ToHexString() + ".");
byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
if(pbTrf32 == null) { Debug.Assert(false); return null; }
if(pbTrf32.Length != 32)
{
Debug.Assert(false);
pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
}
ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
MemUtil.ZeroByteArray(pbTrf32);
MemUtil.ZeroByteArray(pbRaw32);
return pbRet;
}
@ -245,192 +277,6 @@ namespace KeePassLib.Keys
throw new InvalidOperationException();
}
}
/// <summary>
/// Transform the current key <c>uNumRounds</c> times.
/// </summary>
/// <param name="pbOriginalKey32">The original key which will be transformed.
/// This parameter won't be modified.</param>
/// <param name="pbKeySeed32">Seed used for key transformations. Must not
/// be <c>null</c>. This parameter won't be modified.</param>
/// <param name="uNumRounds">Transformation count.</param>
/// <returns>256-bit transformed key.</returns>
private static byte[] TransformKey(byte[] pbOriginalKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
Debug.Assert((pbOriginalKey32 != null) && (pbOriginalKey32.Length == 32));
if (pbOriginalKey32 == null)
throw new ArgumentNullException("pbOriginalKey32");
if (pbOriginalKey32.Length != 32)
throw new ArgumentException();
Debug.Assert((pbKeySeed32 != null) && (pbKeySeed32.Length == 32));
if (pbKeySeed32 == null)
throw new ArgumentNullException("pbKeySeed32");
if (pbKeySeed32.Length != 32)
throw new ArgumentException();
byte[] pbNewKey = new byte[32];
Array.Copy(pbOriginalKey32, pbNewKey, pbNewKey.Length);
// Try to use the native library first
Stopwatch sw = new Stopwatch();
sw.Start();
if (NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
{
sw.Stop();
Kp2aLog.Log("Native transform:" +sw.ElapsedMilliseconds+"ms");
return pbNewKey;
}
sw.Restart();
if(TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds) == false)
return null;
sw.Stop();
Kp2aLog.Log("Managed transform:" +sw.ElapsedMilliseconds+"ms");
SHA256Managed sha256 = new SHA256Managed();
return sha256.ComputeHash(pbNewKey);
}
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
ulong uNumRounds)
{
#if KeePassRT
KeyParameter kp = new KeyParameter(pbKeySeed32);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
for(ulong i = 0; i < uNumRounds; ++i)
{
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
}
#else
byte[] pbIV = new byte[16];
Array.Clear(pbIV, 0, pbIV.Length);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKeySeed32;
ICryptoTransform iCrypt = r.CreateEncryptor();
// !iCrypt.CanReuseTransform -- doesn't work with Mono
if((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
(iCrypt.OutputBlockSize != 16))
{
Debug.Assert(false, "Invalid ICryptoTransform.");
Debug.Assert((iCrypt.InputBlockSize == 16), "Invalid input block size!");
Debug.Assert((iCrypt.OutputBlockSize == 16), "Invalid output block size!");
return false;
}
for(ulong i = 0; i < uNumRounds; ++i)
{
iCrypt.TransformBlock(pbNewKey32, 0, 16, pbNewKey32, 0);
iCrypt.TransformBlock(pbNewKey32, 16, 16, pbNewKey32, 16);
}
#endif
return true;
}
/// <summary>
/// Benchmark the <c>TransformKey</c> method. Within
/// <paramref name="uMilliseconds"/> ms, random keys will be transformed
/// and the number of performed transformations are returned.
/// </summary>
/// <param name="uMilliseconds">Test duration in ms.</param>
/// <param name="uStep">Stepping.
/// <paramref name="uStep" /> should be a prime number. For fast processors
/// (PCs) a value of <c>3001</c> is recommended, for slower processors (PocketPC)
/// a value of <c>401</c> is recommended.</param>
/// <returns>Number of transformations performed in the specified
/// amount of time. Maximum value is <c>uint.MaxValue</c>.</returns>
public static ulong TransformKeyBenchmark(uint uMilliseconds, ulong uStep)
{
ulong uRounds;
// Try native method
if(NativeLib.TransformKeyBenchmark256(uMilliseconds, out uRounds))
return uRounds;
byte[] pbKey = new byte[32];
byte[] pbNewKey = new byte[32];
for(int i = 0; i < pbKey.Length; ++i)
{
pbKey[i] = (byte)i;
pbNewKey[i] = (byte)i;
}
#if KeePassRT
KeyParameter kp = new KeyParameter(pbKey);
AesEngine aes = new AesEngine();
aes.Init(true, kp);
#else
byte[] pbIV = new byte[16];
Array.Clear(pbIV, 0, pbIV.Length);
RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size
{
Debug.Assert(false);
r.BlockSize = 128;
}
r.IV = pbIV;
r.Mode = CipherMode.ECB;
r.KeySize = 256;
r.Key = pbKey;
ICryptoTransform iCrypt = r.CreateEncryptor();
// !iCrypt.CanReuseTransform -- doesn't work with Mono
if((iCrypt == null) || (iCrypt.InputBlockSize != 16) ||
(iCrypt.OutputBlockSize != 16))
{
Debug.Assert(false, "Invalid ICryptoTransform.");
Debug.Assert(iCrypt.InputBlockSize == 16, "Invalid input block size!");
Debug.Assert(iCrypt.OutputBlockSize == 16, "Invalid output block size!");
return PwDefs.DefaultKeyEncryptionRounds;
}
#endif
uRounds = 0;
int tStart = Environment.TickCount;
while(true)
{
for(ulong j = 0; j < uStep; ++j)
{
#if KeePassRT
aes.ProcessBlock(pbNewKey, 0, pbNewKey, 0);
aes.ProcessBlock(pbNewKey, 16, pbNewKey, 16);
#else
iCrypt.TransformBlock(pbNewKey, 0, 16, pbNewKey, 0);
iCrypt.TransformBlock(pbNewKey, 16, 16, pbNewKey, 16);
#endif
}
uRounds += uStep;
if(uRounds < uStep) // Overflow check
{
uRounds = ulong.MaxValue;
break;
}
uint tElapsed = (uint)(Environment.TickCount - tStart);
if(tElapsed > uMilliseconds) break;
}
return uRounds;
}
}
public sealed class InvalidCompositeKeyException : Exception

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -20,12 +18,15 @@
*/
using System;
using System.Text;
using System.IO;
using System.Xml;
using System.Security;
using System.Security.Cryptography;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Text;
using System.Xml;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Resources;
@ -103,12 +104,12 @@ namespace KeePassLib.Keys
if (pbFileData == null) throw new Java.IO.FileNotFoundException();
m_pbFileData = new ProtectedBinary(true, pbFileData);
if (bThrowIfDbFile && (pbFileData.Length >= 8))
if(bThrowIfDbFile && (pbFileData.Length >= 8))
{
uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
if (((uSig1 == KdbxFile.FileSignature1) &&
if(((uSig1 == KdbxFile.FileSignature1) &&
(uSig2 == KdbxFile.FileSignature2)) ||
((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
@ -122,9 +123,9 @@ namespace KeePassLib.Keys
}
byte[] pbKey = LoadXmlKeyFile(pbFileData);
if (pbKey == null) pbKey = LoadKeyFile(pbFileData);
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
if (pbKey == null) throw new InvalidOperationException();
if(pbKey == null) throw new InvalidOperationException();
m_ioc = iocKeyFile;
m_pbKeyData = new ProtectedBinary(true, pbKey);
@ -137,13 +138,13 @@ namespace KeePassLib.Keys
byte[] pbFileData = IOConnection.ReadFile(iocFile);
Construct(pbFileData, iocFile, bThrowIfDbFile);
}
// public void Clear()
// {
// m_strPath = string.Empty;
// m_pbKeyData = null;
// }
private static byte[] LoadKeyFile(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
@ -155,10 +156,7 @@ namespace KeePassLib.Keys
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
if(pbKey == null)
{
SHA256Managed sha256 = new SHA256Managed();
pbKey = sha256.ComputeHash(pbFileData);
}
pbKey = CryptoUtil.HashSha256(pbFileData);
return pbKey;
}
@ -178,12 +176,15 @@ namespace KeePassLib.Keys
try
{
string strHex = StrUtil.Utf8.GetString(pbFileData, 0, 64);
if(!StrUtil.IsHexString(strHex, true)) return null;
if(!StrUtil.IsHexString(pbFileData, true)) return null;
string strHex = StrUtil.Utf8.GetString(pbFileData);
byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
if((pbKey == null) || (pbKey.Length != 32))
{
Debug.Assert(false);
return null;
}
return pbKey;
}
@ -211,13 +212,13 @@ namespace KeePassLib.Keys
pbFinalKey32 = pbKey32;
else
{
MemoryStream ms = new MemoryStream();
ms.Write(pbAdditionalEntropy, 0, pbAdditionalEntropy.Length);
ms.Write(pbKey32, 0, 32);
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, pbAdditionalEntropy);
MemUtil.Write(ms, pbKey32);
SHA256Managed sha256 = new SHA256Managed();
pbFinalKey32 = sha256.ComputeHash(ms.ToArray());
ms.Close();
pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
}
}
CreateXmlKeyFile(strFilePath, pbFinalKey32);
@ -292,7 +293,15 @@ namespace KeePassLib.Keys
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
Stream sOut = IOConnection.OpenWrite(ioc);
#if KeePassUAP
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = StrUtil.Utf8;
xws.Indent = false;
XmlWriter xtw = XmlWriter.Create(sOut, xws);
#else
XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8);
#endif
xtw.WriteStartDocument();
xtw.WriteWhitespace("\r\n");
@ -334,6 +343,6 @@ namespace KeePassLib.Keys
public void ResetIoc(IOConnectionInfo newIoc)
{
m_ioc = newIoc;
}
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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,10 @@
*/
using System;
using System.Text;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using KeePassLib.Cryptography;
using KeePassLib.Security;
using KeePassLib.Utility;
@ -68,8 +68,11 @@ namespace KeePassLib.Keys
Debug.Assert(pbPasswordUtf8 != null);
if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8");
SHA256Managed sha256 = new SHA256Managed();
byte[] pbRaw = sha256.ComputeHash(pbPasswordUtf8);
#if (DEBUG && !KeePassLibSD)
Debug.Assert(ValidatePassword(pbPasswordUtf8));
#endif
byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
m_psPassword = new ProtectedString(true, pbPasswordUtf8);
m_pbKeyData = new ProtectedBinary(true, pbRaw);
@ -80,5 +83,19 @@ namespace KeePassLib.Keys
// m_psPassword = null;
// m_pbKeyData = null;
// }
#if (DEBUG && !KeePassLibSD)
private static bool ValidatePassword(byte[] pb)
{
try
{
string str = StrUtil.Utf8.GetString(pb);
return str.IsNormalized(NormalizationForm.FormC);
}
catch(Exception) { Debug.Assert(false); }
return false;
}
#endif
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -20,9 +20,13 @@
*/
using System;
using System.Security;
using System.Security.Cryptography;
using System.Diagnostics;
using System.IO;
using System.Security;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Resources;

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -21,12 +19,19 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;
using Android.Util;
using KeePassLib.Utility;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using Java.Text;
using keepass2android;
#if !KeePassUAP
using System.IO;
using System.Threading;
#endif
using KeePassLib.Utility;
namespace KeePassLib.Native
{
@ -48,6 +53,61 @@ namespace KeePassLib.Native
set { m_bAllowNative = value; }
}
private static int? g_oiPointerSize = null;
/// <summary>
/// Size of a native pointer (in bytes).
/// </summary>
public static int PointerSize
{
get
{
if(!g_oiPointerSize.HasValue)
#if KeePassUAP
g_oiPointerSize = Marshal.SizeOf<IntPtr>();
#else
g_oiPointerSize = Marshal.SizeOf(typeof(IntPtr));
#endif
return g_oiPointerSize.Value;
}
}
private static ulong? m_ouMonoVersion = null;
public static ulong MonoVersion
{
get
{
if(m_ouMonoVersion.HasValue) return m_ouMonoVersion.Value;
ulong uVersion = 0;
try
{
Type t = Type.GetType("Mono.Runtime");
if(t != null)
{
MethodInfo mi = t.GetMethod("GetDisplayName",
BindingFlags.NonPublic | BindingFlags.Static);
if(mi != null)
{
string strName = (mi.Invoke(null, null) as string);
if(!string.IsNullOrEmpty(strName))
{
Match m = Regex.Match(strName, "\\d+(\\.\\d+)+");
if(m.Success)
uVersion = StrUtil.ParseVersion(m.Value);
else { Debug.Assert(false); }
}
else { Debug.Assert(false); }
}
else { Debug.Assert(false); }
}
}
catch(Exception) { Debug.Assert(false); }
m_ouMonoVersion = uVersion;
return uVersion;
}
}
/// <summary>
/// Determine if the native library is installed.
/// </summary>
@ -91,15 +151,15 @@ namespace KeePassLib.Native
{
if(m_platID.HasValue) return m_platID.Value;
#if KeePassRT
m_platID = PlatformID.Win32NT;
#if KeePassUAP
m_platID = EnvironmentExt.OSVersion.Platform;
#else
m_platID = Environment.OSVersion.Platform;
#endif
#if (!KeePassLibSD && !KeePassRT)
#if (!KeePassLibSD && !KeePassUAP)
/*// Mono returns PlatformID.Unix on Mac OS X, workaround this
//fails on Anroid
//not supported on Mono
if(m_platID.Value == PlatformID.Unix)
{
if((RunConsoleApp("uname", null) ?? string.Empty).Trim().Equals(
@ -111,7 +171,111 @@ namespace KeePassLib.Native
return m_platID.Value;
}
private static DesktopType? m_tDesktop = null;
public static DesktopType GetDesktopType()
{
if(!m_tDesktop.HasValue)
{
DesktopType t = DesktopType.None;
if(!IsUnix()) t = DesktopType.Windows;
else
{
try
{
string strXdg = (Environment.GetEnvironmentVariable(
"XDG_CURRENT_DESKTOP") ?? string.Empty).Trim();
string strGdm = (Environment.GetEnvironmentVariable(
"GDMSESSION") ?? string.Empty).Trim();
StringComparison sc = StrUtil.CaseIgnoreCmp;
if(strXdg.Equals("Unity", sc))
t = DesktopType.Unity;
else if(strXdg.Equals("LXDE", sc))
t = DesktopType.Lxde;
else if(strXdg.Equals("XFCE", sc))
t = DesktopType.Xfce;
else if(strXdg.Equals("MATE", sc))
t = DesktopType.Mate;
else if(strXdg.Equals("X-Cinnamon", sc))
t = DesktopType.Cinnamon;
else if(strXdg.Equals("Pantheon", sc)) // Elementary OS
t = DesktopType.Pantheon;
else if(strXdg.Equals("KDE", sc) || // Mint 16
strGdm.Equals("kde-plasma", sc)) // Ubuntu 12.04
t = DesktopType.Kde;
else if(strXdg.Equals("GNOME", sc))
{
if(strGdm.Equals("cinnamon", sc)) // Mint 13
t = DesktopType.Cinnamon;
else t = DesktopType.Gnome;
}
}
catch(Exception) { Debug.Assert(false); }
}
m_tDesktop = t;
}
return m_tDesktop.Value;
}
#if (!KeePassLibSD && !KeePassUAP)
/* Not supported on Android
public static string RunConsoleApp(string strAppPath, string strParams)
{
return RunConsoleApp(strAppPath, strParams, null);
}
public static string RunConsoleApp(string strAppPath, string strParams,
string strStdInput)
{
return RunConsoleApp(strAppPath, strParams, strStdInput,
(AppRunFlags.GetStdOutput | AppRunFlags.WaitForExit));
}
*/
private delegate string RunProcessDelegate();
private static void EnsureNoBom(StreamWriter sw)
{
if(sw == null) { Debug.Assert(false); return; }
if(!MonoWorkarounds.IsRequired(1219)) return;
try
{
Encoding enc = sw.Encoding;
if(enc == null) { Debug.Assert(false); return; }
byte[] pbBom = enc.GetPreamble();
if((pbBom == null) || (pbBom.Length == 0)) return;
// For Mono >= 4.0 (using Microsoft's reference source)
try
{
FieldInfo fi = typeof(StreamWriter).GetField("haveWrittenPreamble",
BindingFlags.Instance | BindingFlags.NonPublic);
if(fi != null)
{
fi.SetValue(sw, true);
return;
}
}
catch(Exception) { Debug.Assert(false); }
// For Mono < 4.0
FieldInfo fiPD = typeof(StreamWriter).GetField("preamble_done",
BindingFlags.Instance | BindingFlags.NonPublic);
if(fiPD != null) fiPD.SetValue(sw, true);
else { Debug.Assert(false); }
}
catch(Exception) { Debug.Assert(false); }
}
#endif
/// <summary>
/// Transform a key.
/// </summary>
/// <param name="pBuf256">Source and destination buffer.</param>
/// <param name="pKey256">Key to use in the transformation.</param>
/// <param name="uRounds">Number of transformation rounds.</param>
/// <returns>Returns <c>true</c>, if the key was transformed successfully.</returns>
/// <summary>
/// Transform a key.
/// </summary>
@ -122,7 +286,7 @@ namespace KeePassLib.Native
public static bool TransformKey256(byte[] pBuf256, byte[] pKey256,
ulong uRounds)
{
if(m_bAllowNative == false) return false;
if (m_bAllowNative == false) return false;
try
{
@ -136,10 +300,10 @@ namespace KeePassLib.Native
return false;
#endif
}
catch(Exception e)
catch (Exception e)
{
Kp2aLog.Log(e.Message);
return false;
return false;
}
return true;
@ -148,19 +312,23 @@ namespace KeePassLib.Native
/// <summary>
/// Benchmark key transformation.
/// </summary>
/// <param name="uTimeMs">Number of seconds to perform the benchmark.</param>
/// <param name="uTimeMs">Number of milliseconds to perform the benchmark.</param>
/// <param name="puRounds">Number of transformations done.</param>
/// <returns>Returns <c>true</c>, if the benchmark was successful.</returns>
public static bool TransformKeyBenchmark256(uint uTimeMs, out ulong puRounds)
{
puRounds = 0;
if(m_bAllowNative == false) return false;
#if KeePassUAP
return false;
#else
if(!m_bAllowNative) return false;
try { puRounds = NativeMethods.TransformKeyBenchmark(uTimeMs); }
catch(Exception) { return false; }
return true;
#endif
}
private static KeyValuePair<IntPtr, IntPtr> PrepareArrays256(byte[] pBuf256,

View File

@ -0,0 +1,115 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
#if !KeePassUAP
using System.Windows.Forms;
#endif
namespace KeePassLib.Native
{
internal static partial class NativeMethods
{
#if (!KeePassLibSD && !KeePassUAP)
[StructLayout(LayoutKind.Sequential)]
private struct XClassHint
{
public IntPtr res_name;
public IntPtr res_class;
}
[DllImport("libX11")]
private static extern int XSetClassHint(IntPtr display, IntPtr window, IntPtr class_hints);
private static Type m_tXplatUIX11 = null;
private static Type GetXplatUIX11Type(bool bThrowOnError)
{
if(m_tXplatUIX11 == null)
{
// CheckState is in System.Windows.Forms
string strTypeCS = typeof(CheckState).AssemblyQualifiedName;
string strTypeX11 = strTypeCS.Replace("CheckState", "XplatUIX11");
m_tXplatUIX11 = Type.GetType(strTypeX11, bThrowOnError, true);
}
return m_tXplatUIX11;
}
private static Type m_tHwnd = null;
private static Type GetHwndType(bool bThrowOnError)
{
if(m_tHwnd == null)
{
// CheckState is in System.Windows.Forms
string strTypeCS = typeof(CheckState).AssemblyQualifiedName;
string strTypeHwnd = strTypeCS.Replace("CheckState", "Hwnd");
m_tHwnd = Type.GetType(strTypeHwnd, bThrowOnError, true);
}
return m_tHwnd;
}
internal static void SetWmClass(Form f, string strName, string strClass)
{
if(f == null) { Debug.Assert(false); return; }
// The following crashes under Mac OS X (SIGSEGV in native code,
// not just an exception), thus skip it when we're on Mac OS X;
// https://sourceforge.net/projects/keepass/forums/forum/329221/topic/5860588
if(NativeLib.GetPlatformID() == PlatformID.MacOSX) return;
try
{
Type tXplatUIX11 = GetXplatUIX11Type(true);
FieldInfo fiDisplayHandle = tXplatUIX11.GetField("DisplayHandle",
BindingFlags.NonPublic | BindingFlags.Static);
IntPtr hDisplay = (IntPtr)fiDisplayHandle.GetValue(null);
Type tHwnd = GetHwndType(true);
MethodInfo miObjectFromHandle = tHwnd.GetMethod("ObjectFromHandle",
BindingFlags.Public | BindingFlags.Static);
object oHwnd = miObjectFromHandle.Invoke(null, new object[] { f.Handle });
FieldInfo fiWholeWindow = tHwnd.GetField("whole_window",
BindingFlags.NonPublic | BindingFlags.Instance);
IntPtr hWindow = (IntPtr)fiWholeWindow.GetValue(oHwnd);
XClassHint xch = new XClassHint();
xch.res_name = Marshal.StringToCoTaskMemAnsi(strName ?? string.Empty);
xch.res_class = Marshal.StringToCoTaskMemAnsi(strClass ?? string.Empty);
IntPtr pXch = Marshal.AllocCoTaskMem(Marshal.SizeOf(xch));
Marshal.StructureToPtr(xch, pXch, false);
XSetClassHint(hDisplay, hWindow, pXch);
Marshal.FreeCoTaskMem(pXch);
Marshal.FreeCoTaskMem(xch.res_name);
Marshal.FreeCoTaskMem(xch.res_class);
}
catch(Exception) { Debug.Assert(false); }
}
#endif
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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,20 +18,22 @@
*/
using System;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Native
{
internal static class NativeMethods
internal static partial class NativeMethods
{
internal const int MAX_PATH = 260;
// internal const uint TF_SFT_SHOWNORMAL = 0x00000001;
// internal const uint TF_SFT_HIDDEN = 0x00000008;
/* [DllImport("KeePassNtv32.dll", EntryPoint = "TransformKey")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey32(IntPtr pBuf256,
@ -70,6 +72,7 @@ namespace KeePassLib.Native
return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds);
} */
#if !KeePassUAP
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKey256")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey32(IntPtr pBuf256,
@ -83,7 +86,7 @@ namespace KeePassLib.Native
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds)
{
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
if(NativeLib.PointerSize == 8)
return TransformKey64(pBuf256, pKey256, uRounds);
else
return TransformKey32(pBuf256, pKey256, uRounds);
@ -97,69 +100,87 @@ namespace KeePassLib.Native
internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs)
{
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
if(NativeLib.PointerSize == 8)
return TransformKeyBenchmark64(uTimeMs);
else
return TransformKeyBenchmark32(uTimeMs);
return TransformKeyBenchmark32(uTimeMs);
}
#endif
#if (!KeePassLibSD && !KeePassRT)
[DllImport("ShlWApi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
internal static extern int StrCmpLogicalW(string x, string y);
/* [DllImport("KeePassLibC32.dll", EntryPoint = "TF_ShowLangBar")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TF_ShowLangBar32(UInt32 dwFlags);
[DllImport("KeePassLibC64.dll", EntryPoint = "TF_ShowLangBar")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TF_ShowLangBar64(UInt32 dwFlags);
internal static bool TfShowLangBar(uint dwFlags)
{
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
return TF_ShowLangBar64(dwFlags);
return TF_ShowLangBar32(dwFlags);
} */
#if (!KeePassLibSD && !KeePassUAP)
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool PathRelativePathTo([Out] StringBuilder pszPath,
[In] string pszFrom, [In] uint dwAttrFrom, [In] string pszTo,
[In] uint dwAttrTo);
#endif
[In] string pszFrom, uint dwAttrFrom, [In] string pszTo, uint dwAttrTo);
private static bool? m_bSupportsLogicalCmp = null;
[DllImport("ShlWApi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern int StrCmpLogicalW(string x, string y);
private static bool? m_obSupportsLogicalCmp = null;
private static void TestNaturalComparisonsSupport()
{
#if (KeePassLibSD || KeePassRT)
#warning No native natural comparisons supported.
m_bSupportsLogicalCmp = false;
#else
try
{
StrCmpLogicalW("0", "0"); // Throws exception if unsupported
m_bSupportsLogicalCmp = true;
m_obSupportsLogicalCmp = true;
}
catch(Exception) { m_bSupportsLogicalCmp = false; }
#endif
catch(Exception) { m_obSupportsLogicalCmp = false; }
}
#endif
internal static bool SupportsStrCmpNaturally
{
get
{
if(m_bSupportsLogicalCmp.HasValue == false)
#if (!KeePassLibSD && !KeePassUAP)
if(!m_obSupportsLogicalCmp.HasValue)
TestNaturalComparisonsSupport();
return m_bSupportsLogicalCmp.Value;
return m_obSupportsLogicalCmp.Value;
#else
return false;
#endif
}
}
internal static int StrCmpNaturally(string x, string y)
{
if(m_bSupportsLogicalCmp.HasValue == false) TestNaturalComparisonsSupport();
if(m_bSupportsLogicalCmp.Value == false) return 0;
#if (!KeePassLibSD && !KeePassUAP)
if(!NativeMethods.SupportsStrCmpNaturally)
{
Debug.Assert(false);
return string.Compare(x, y, true);
}
#if (KeePassLibSD || KeePassRT)
#warning No native natural comparisons supported.
return x.CompareTo(y);
#else
return StrCmpLogicalW(x, y);
#else
Debug.Assert(false);
return string.Compare(x, y, true);
#endif
}
internal static string GetUserRuntimeDir()
{
#if !KeePassLibSD
#if KeePassRT
string strRtDir = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
#if KeePassLibSD
return Path.GetTempPath();
#else
#if KeePassUAP
string strRtDir = EnvironmentExt.AppDataLocalFolderPath;
#else
string strRtDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
if(string.IsNullOrEmpty(strRtDir))
@ -175,8 +196,6 @@ namespace KeePassLib.Native
strRtDir += PwDefs.ShortProductName;
return strRtDir;
#else
return Path.GetTempPath();
#endif
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -27,7 +27,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Dominik Reichl")]
[assembly: AssemblyProduct("KeePassLib")]
[assembly: AssemblyCopyright("Copyright © 2003-2013 Dominik Reichl")]
[assembly: AssemblyCopyright("Copyright © 2003-2016 Dominik Reichl")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -38,5 +38,5 @@ using System.Runtime.InteropServices;
[assembly: Guid("395f6eec-a1e0-4438-aa82-b75099348134")]
// Assembly version information
[assembly: AssemblyVersion("2.24.0.*")]
[assembly: AssemblyFileVersion("2.24.0.0")]
[assembly: AssemblyVersion("2.34.0.*")]
[assembly: AssemblyFileVersion("2.34.0.0")]

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,9 +19,9 @@
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.Serialization;
using KeePassLib.Delegates;
using KeePassLib.Interfaces;
@ -55,20 +55,20 @@ namespace KeePassLib
/// e.g. 2.19 = 0x02130000.
/// It is highly recommended to use <c>FileVersion64</c> instead.
/// </summary>
public const uint Version32 = 0x02180000;
public const uint Version32 = 0x02220000;
/// <summary>
/// Version, encoded as 64-bit unsigned integer
/// (component-wise, 16 bits per component).
/// </summary>
public const ulong FileVersion64 = 0x0002001800000000UL;
public const ulong FileVersion64 = 0x0002002200000000UL;
/// <summary>
/// Version, encoded as string.
/// </summary>
public const string VersionString = "2.24";
public const string VersionString = "2.34";
public const string Copyright = @"Copyright © 2003-2013 Dominik Reichl";
public const string Copyright = @"Copyright © 2003-2016 Dominik Reichl";
/// <summary>
/// Product website URL. Terminated by a forward slash.
@ -94,7 +94,8 @@ namespace KeePassLib
/// URL to a TXT file (eventually compressed) that contains information
/// about the latest KeePass version available on the website.
/// </summary>
public const string VersionUrl = "http://keepass.info/update/version2x.txt.gz";
public const string VersionUrl = "https://sslsites.de/keepass.info/update/version2x.txt.gz";
// public const string VersionUrl = "http://keepass.info/update/version2x.txt.gz";
/// <summary>
/// URL to the root path of the online KeePass help. Terminated by
@ -220,7 +221,7 @@ namespace KeePassLib
}
}
#pragma warning disable 1591 // Missing XML comments warning
// #pragma warning disable 1591 // Missing XML comments warning
/// <summary>
/// Search parameters for group and entry searches.
/// </summary>
@ -318,7 +319,7 @@ namespace KeePassLib
set { m_bSearchInTags = value; }
}
#if KeePassRT
#if KeePassUAP
private StringComparison m_scType = StringComparison.OrdinalIgnoreCase;
#else
private StringComparison m_scType = StringComparison.InvariantCultureIgnoreCase;
@ -410,9 +411,9 @@ namespace KeePassLib
return (SearchParameters)this.MemberwiseClone();
}
}
#pragma warning restore 1591 // Missing XML comments warning
// #pragma warning restore 1591 // Missing XML comments warning
#pragma warning disable 1591 // Missing XML comments warning
// #pragma warning disable 1591 // Missing XML comments warning
/// <summary>
/// Memory protection configuration structure (for default fields).
/// </summary>
@ -442,7 +443,7 @@ namespace KeePassLib
return false;
}
}
#pragma warning restore 1591 // Missing XML comments warning
// #pragma warning restore 1591 // Missing XML comments warning
public sealed class ObjectTouchedEventArgs : EventArgs
{

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -20,8 +20,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
#if !KeePassUAP
using System.Drawing;
#endif
using KeePassLib.Collections;
using KeePassLib.Interfaces;
@ -70,9 +72,11 @@ namespace KeePassLib
private List<string> m_vTags = new List<string>();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary>
/// UUID of this entry.
/// </summary>
/// </summary>
public PwUuid Uuid
{
get { return m_uuid; }
@ -304,6 +308,23 @@ namespace KeePassLib
}
}
/// <summary>
/// Custom data container that can be used by plugins to store
/// own data in KeePass entries.
/// The data is stored in the encrypted part of encrypted
/// database files.
/// Use unique names for your items, e.g. "PluginName_ItemName".
/// </summary>
public StringDictionaryEx CustomData
{
get { return m_dCustomData; }
internal set
{
if(value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dCustomData = value;
}
}
public static EventHandler<ObjectTouchedEventArgs> EntryTouched;
public EventHandler<ObjectTouchedEventArgs> Touched;
@ -352,12 +373,20 @@ namespace KeePassLib
}
}
#if DEBUG
/// <summary>
public override string ToString()
{
return (@"PwEntry '" + m_listStrings.ReadSafe(PwDefs.TitleField) + @"'");
}
#endif
/// Clone the current entry. The returned entry is an exact value copy
/// of the current entry (including UUID and parent group reference).
/// All mutable members are cloned.
/// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns>
/// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns>
public PwEntry CloneDeep()
{
PwEntry peNew = new PwEntry(false, false);
@ -394,6 +423,8 @@ namespace KeePassLib
peNew.m_vTags = new List<string>(m_vTags);
peNew.m_dCustomData = m_dCustomData.CloneDeep();
return peNew;
}
@ -515,6 +546,8 @@ namespace KeePassLib
if(m_vTags[iTag] != pe.m_vTags[iTag]) return false;
}
if(!m_dCustomData.Equals(pe.m_dCustomData)) return false;
return true;
}
@ -531,7 +564,7 @@ namespace KeePassLib
public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer,
bool bIncludeHistory, bool bAssignLocationChanged)
{
Debug.Assert(peTemplate != null); if(peTemplate == null) throw new ArgumentNullException("peTemplate");
if(peTemplate == null) { Debug.Assert(false); throw new ArgumentNullException("peTemplate"); }
if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.LastModificationTime,LastModificationTime,true) < 0)) return;
@ -542,10 +575,11 @@ namespace KeePassLib
if(bAssignLocationChanged)
m_tParentGroupLastMod = peTemplate.LocationChanged;
m_listStrings = peTemplate.m_listStrings;
m_listBinaries = peTemplate.m_listBinaries;
m_listAutoType = peTemplate.m_listAutoType;
if(bIncludeHistory) m_listHistory = peTemplate.m_listHistory;
m_listStrings = peTemplate.m_listStrings.CloneDeep();
m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
if(bIncludeHistory)
m_listHistory = peTemplate.m_listHistory.CloneDeep();
m_pwIcon = peTemplate.m_pwIcon;
m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
@ -563,6 +597,8 @@ namespace KeePassLib
m_strOverrideUrl = peTemplate.m_strOverrideUrl;
m_vTags = new List<string>(peTemplate.m_vTags);
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
}
/// <summary>
@ -814,6 +850,9 @@ namespace KeePassLib
foreach(string strTag in m_vTags)
uSize += (ulong)strTag.Length;
foreach(KeyValuePair<string, string> kvp in m_dCustomData)
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return uSize;
}
@ -893,6 +932,25 @@ namespace KeePassLib
}
return dateTime;
}
public void SetCreatedNow()
{
DateTime dt = DateTime.Now;
m_tCreation = dt;
m_tLastAccess = dt;
}
public PwEntry Duplicate()
{
PwEntry pe = CloneDeep();
pe.SetUuid(new PwUuid(true), true);
pe.SetCreatedNow();
return pe;
}
}
public sealed class PwEntryComparer : IComparer<PwEntry>
@ -918,12 +976,7 @@ namespace KeePassLib
if(m_bCompareNaturally) return StrUtil.CompareNaturally(strA, strB);
#if KeePassRT
return string.Compare(strA, strB, m_bCaseInsensitive ?
StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture);
#else
return string.Compare(strA, strB, m_bCaseInsensitive);
#endif
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -163,6 +163,26 @@ namespace KeePassLib
Manual = 2
}
public enum ProxyAuthType
{
None = 0,
/// <summary>
/// Use default user credentials (provided by the system).
/// </summary>
Default = 1,
Manual = 2,
/// <summary>
/// <c>Default</c> or <c>Manual</c>, depending on whether
/// manual credentials are available.
/// This type exists for supporting upgrading from KeePass
/// 2.28 to 2.29; the user cannot select this type.
/// </summary>
Auto = 3
}
/// <summary>
/// Comparison modes for in-memory protected objects.
/// </summary>
@ -204,6 +224,9 @@ namespace KeePassLib
IgnoreHistory = 0x10,
IgnoreLastBackup = 0x20,
// For groups:
PropertiesOnly = 0x40,
IgnoreTimes = (IgnoreLastAccess | IgnoreLastMod)
}
@ -254,14 +277,43 @@ namespace KeePassLib
GetStdOutput = 1,
WaitForExit = 2,
// This flag prevents any handles being garbage-collected
// before the started process has terminated, without
// blocking the current thread;
// https://sourceforge.net/p/keepass/patches/84/
/// <summary>
/// This flag prevents any handles being garbage-collected
/// before the started process has terminated, without
/// blocking the current thread.
/// </summary>
GCKeepAlive = 4,
// https://sourceforge.net/p/keepass/patches/85/
DoEvents = 8,
DisableForms = 16
}
[Flags]
public enum ScaleTransformFlags
{
None = 0,
/// <summary>
/// <c>UIIcon</c> indicates that the returned image is going
/// to be displayed as icon in the UI and that it is not
/// subject to future changes in size.
/// </summary>
UIIcon = 1
}
public enum DesktopType
{
None = 0,
Windows,
Gnome,
Kde,
Unity,
Lxde,
Xfce,
Mate,
Cinnamon,
Pantheon
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -137,12 +137,6 @@ namespace KeePassLib
{
if(other == null) { Debug.Assert(false); return false; }
// Shortcut
if (Object.ReferenceEquals(this, other))
{
return true;
}
for(int i = 0; i < (int)UuidSize; ++i)
{
if(m_pbUuid[i] != other.m_pbUuid[i]) return false;

View File

@ -27,14 +27,16 @@ namespace KeePassLib.Resources
if(dictNew == null) throw new ArgumentNullException("dictNew");
m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed);
m_strEncAlgorithmAes = TryGetEx(dictNew, "EncAlgorithmAes", m_strEncAlgorithmAes);
m_strEncDataTooLarge = TryGetEx(dictNew, "EncDataTooLarge", m_strEncDataTooLarge);
m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
m_strExpect100Continue = TryGetEx(dictNew, "Expect100Continue", m_strExpect100Continue);
m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError);
m_strFatalErrorText = TryGetEx(dictNew, "FatalErrorText", m_strFatalErrorText);
m_strFileCorrupted = TryGetEx(dictNew, "FileCorrupted", m_strFileCorrupted);
m_strFileHeaderEndEarly = TryGetEx(dictNew, "FileHeaderEndEarly", m_strFileHeaderEndEarly);
m_strFileLoadFailed = TryGetEx(dictNew, "FileLoadFailed", m_strFileLoadFailed);
m_strFileLockedWrite = TryGetEx(dictNew, "FileLockedWrite", m_strFileLockedWrite);
m_strFileNewVerOrPlgReq = TryGetEx(dictNew, "FileNewVerOrPlgReq", m_strFileNewVerOrPlgReq);
m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq);
m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning);
m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
@ -44,28 +46,37 @@ namespace KeePassLib.Resources
m_strFileVersionUnsupported = TryGetEx(dictNew, "FileVersionUnsupported", m_strFileVersionUnsupported);
m_strFinalKeyCreationFailed = TryGetEx(dictNew, "FinalKeyCreationFailed", m_strFinalKeyCreationFailed);
m_strFrameworkNotImplExcp = TryGetEx(dictNew, "FrameworkNotImplExcp", m_strFrameworkNotImplExcp);
m_strGeneral = TryGetEx(dictNew, "General", m_strGeneral);
m_strInvalidCompositeKey = TryGetEx(dictNew, "InvalidCompositeKey", m_strInvalidCompositeKey);
m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint);
m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding);
m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
m_strUserAgent = TryGetEx(dictNew, "UserAgent", m_strUserAgent);
}
private static readonly string[] m_vKeyNames = {
"CryptoStreamFailed",
"EncAlgorithmAes",
"EncDataTooLarge",
"ErrorInClipboard",
"Expect100Continue",
"FatalError",
"FatalErrorText",
"FileCorrupted",
"FileHeaderEndEarly",
"FileLoadFailed",
"FileLockedWrite",
"FileNewVerOrPlgReq",
"FileNewVerReq",
"FileSaveCorruptionWarning",
"FileSaveFailed",
@ -75,16 +86,23 @@ namespace KeePassLib.Resources
"FileVersionUnsupported",
"FinalKeyCreationFailed",
"FrameworkNotImplExcp",
"General",
"InvalidCompositeKey",
"InvalidCompositeKeyHint",
"InvalidDataWhileDecoding",
"KeePass1xHint",
"KeyBits",
"KeyFileDbSel",
"MasterSeedLengthInvalid",
"OldFormat",
"Passive",
"PreAuth",
"Timeout",
"TryAgainSecs",
"UnknownHeaderId",
"UserAccountKeyError"
"UnknownKdf",
"UserAccountKeyError",
"UserAgent"
};
public static string[] GetKeyNames()
@ -103,15 +121,15 @@ namespace KeePassLib.Resources
get { return m_strCryptoStreamFailed; }
}
private static string m_strEncAlgorithmAes =
@"AES/Rijndael (256-Bit Key)";
private static string m_strEncDataTooLarge =
@"The data is too large to be encrypted/decrypted securely using {PARAM}.";
/// <summary>
/// Look up a localized string similar to
/// 'AES/Rijndael (256-Bit Key)'.
/// 'The data is too large to be encrypted/decrypted securely using {PARAM}.'.
/// </summary>
public static string EncAlgorithmAes
public static string EncDataTooLarge
{
get { return m_strEncAlgorithmAes; }
get { return m_strEncDataTooLarge; }
}
private static string m_strErrorInClipboard =
@ -125,6 +143,17 @@ namespace KeePassLib.Resources
get { return m_strErrorInClipboard; }
}
private static string m_strExpect100Continue =
@"Expect 100-Continue responses";
/// <summary>
/// Look up a localized string similar to
/// 'Expect 100-Continue responses'.
/// </summary>
public static string Expect100Continue
{
get { return m_strExpect100Continue; }
}
private static string m_strFatalError =
@"Fatal Error";
/// <summary>
@ -191,6 +220,17 @@ namespace KeePassLib.Resources
get { return m_strFileLockedWrite; }
}
private static string m_strFileNewVerOrPlgReq =
@"A newer KeePass version or a plugin is required to open this file.";
/// <summary>
/// Look up a localized string similar to
/// 'A newer KeePass version or a plugin is required to open this file.'.
/// </summary>
public static string FileNewVerOrPlgReq
{
get { return m_strFileNewVerOrPlgReq; }
}
private static string m_strFileNewVerReq =
@"A newer KeePass version is required to open this file.";
/// <summary>
@ -290,6 +330,17 @@ namespace KeePassLib.Resources
get { return m_strFrameworkNotImplExcp; }
}
private static string m_strGeneral =
@"General";
/// <summary>
/// Look up a localized string similar to
/// 'General'.
/// </summary>
public static string General
{
get { return m_strGeneral; }
}
private static string m_strInvalidCompositeKey =
@"The composite key is invalid!";
/// <summary>
@ -334,6 +385,17 @@ namespace KeePassLib.Resources
get { return m_strKeePass1xHint; }
}
private static string m_strKeyBits =
@"{PARAM}-bit key";
/// <summary>
/// Look up a localized string similar to
/// '{PARAM}-bit key'.
/// </summary>
public static string KeyBits
{
get { return m_strKeyBits; }
}
private static string m_strKeyFileDbSel =
@"Database files cannot be used as key files.";
/// <summary>
@ -367,6 +429,39 @@ namespace KeePassLib.Resources
get { return m_strOldFormat; }
}
private static string m_strPassive =
@"Passive";
/// <summary>
/// Look up a localized string similar to
/// 'Passive'.
/// </summary>
public static string Passive
{
get { return m_strPassive; }
}
private static string m_strPreAuth =
@"Pre-authenticate";
/// <summary>
/// Look up a localized string similar to
/// 'Pre-authenticate'.
/// </summary>
public static string PreAuth
{
get { return m_strPreAuth; }
}
private static string m_strTimeout =
@"Timeout";
/// <summary>
/// Look up a localized string similar to
/// 'Timeout'.
/// </summary>
public static string Timeout
{
get { return m_strTimeout; }
}
private static string m_strTryAgainSecs =
@"Please try it again in a few seconds.";
/// <summary>
@ -389,6 +484,17 @@ namespace KeePassLib.Resources
get { return m_strUnknownHeaderId; }
}
private static string m_strUnknownKdf =
@"Unknown key derivation function!";
/// <summary>
/// Look up a localized string similar to
/// 'Unknown key derivation function!'.
/// </summary>
public static string UnknownKdf
{
get { return m_strUnknownKdf; }
}
private static string m_strUserAccountKeyError =
@"The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored.";
/// <summary>
@ -399,5 +505,16 @@ namespace KeePassLib.Resources
{
get { return m_strUserAccountKeyError; }
}
private static string m_strUserAgent =
@"User agent";
/// <summary>
/// Look up a localized string similar to
/// 'User agent'.
/// </summary>
public static string UserAgent
{
get { return m_strUserAgent; }
}
}
}

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -20,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
@ -32,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
@ -39,26 +54,97 @@ 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,
ChaCha20,
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;
throw new NotSupportedException();
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
{
@ -73,19 +159,13 @@ namespace KeePassLib.Security
get { return m_uDataLen; }
}
static ProtectedBinary()
{
//protection not supported on Android currently
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()
{
Init(false, new byte[0]);
Init(false, MemUtil.EmptyByteArray);
}
/// <summary>
@ -110,34 +190,99 @@ 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))
throw new NotSupportedException();
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;
}
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;
}
byte[] pbIV = new byte[12];
MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey32, pbIV, true))
{
c.Encrypt(m_pbData, 0, m_pbData.Length);
}
m_mp = PbMemProt.ChaCha20;
}
private void Decrypt()
{
if(m_pbData.Length == 0) return;
else if(m_mp == PbMemProt.ChaCha20)
{
byte[] pbIV = new byte[12];
MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
using(ChaCha20Cipher c = new ChaCha20Cipher(g_pbKey32, pbIV, true))
{
c.Decrypt(m_pbData, 0, m_pbData.Length);
}
}
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>
@ -150,18 +295,16 @@ namespace KeePassLib.Security
/// protected data and can therefore be cleared safely.</returns>
public byte[] ReadData()
{
if(m_uDataLen == 0) return new byte[0];
if(m_uDataLen == 0) return MemUtil.EmptyByteArray;
byte[] pbReturn = new byte[m_uDataLen];
if(m_bProtected && m_bProtectionSupported)
lock(m_objSync)
{
lock(m_objSync)
{
throw new NotSupportedException();
}
Decrypt();
Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
Encrypt();
}
else Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
return pbReturn;
}
@ -171,9 +314,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);
@ -183,7 +323,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];
@ -191,8 +331,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();
@ -203,6 +346,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-2013 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-2013 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

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,8 +19,8 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text;
using KeePassLib.Utility;

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -21,10 +19,10 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using KeePassLib.Cryptography;
using KeePassLib.Resources;
@ -139,7 +137,7 @@ namespace KeePassLib.Serialization
if(!v[0].StartsWith(LockFileHeader)) { Debug.Assert(false); return null; }
return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
}
catch(Java.IO.FileNotFoundException) { }
catch(FileNotFoundException) { }
catch(Exception) { Debug.Assert(false); }
finally { if(s != null) s.Close(); }
@ -154,15 +152,17 @@ namespace KeePassLib.Serialization
try
{
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16);
string strTime = TimeUtil.SerializeUtc(DateTime.Now);
string strTime = TimeUtil.SerializeUtc(DateTime.UtcNow);
#if (!KeePassLibSD && !KeePassRT)
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
#if KeePassUAP
EnvironmentExt.UserName, EnvironmentExt.MachineName,
EnvironmentExt.UserDomainName);
#elif KeePassLibSD
string.Empty, string.Empty, string.Empty);
#else
Environment.UserName, Environment.MachineName,
Environment.UserDomainName);
#else
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
string.Empty, string.Empty, string.Empty);
#endif
StringBuilder sb = new StringBuilder();
@ -244,8 +244,8 @@ namespace KeePassLib.Serialization
if(bDisposing) Thread.Sleep(50);
}
if(bDisposing && !bFileDeleted)
IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception
// if(bDisposing && !bFileDeleted)
// IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception
m_iocLockFile = null;
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -21,11 +21,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Text;
#if (!KeePassLibSD && !KeePassRT)
#if (!KeePassLibSD && !KeePassUAP)
using System.Security.AccessControl;
#endif
@ -44,6 +44,9 @@ namespace KeePassLib.Serialization
private const string StrTempSuffix = ".tmp";
private static Dictionary<string, bool> g_dEnabled =
new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer);
public FileTransactionEx(IOConnectionInfo iocBaseFile)
{
Initialize(iocBaseFile, true);
@ -101,7 +104,7 @@ namespace KeePassLib.Serialization
{
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
#if (!KeePassLibSD && !KeePassRT)
#if (!KeePassLibSD && !KeePassUAP)
bool bEfsEncrypted = false;
#endif
@ -130,7 +133,7 @@ namespace KeePassLib.Serialization
IOConnection.RenameFile(m_iocTemp, m_iocBase);
#if (!KeePassLibSD && !KeePassRT)
#if (!KeePassLibSD && !KeePassUAP)
if(m_iocBase.IsLocalFile())
{
try
@ -148,5 +151,15 @@ namespace KeePassLib.Serialization
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again
}
// For plugins
public static void Configure(string strPrefix, bool? obTransacted)
{
if(string.IsNullOrEmpty(strPrefix)) { Debug.Assert(false); return; }
if(obTransacted.HasValue)
g_dEnabled[strPrefix] = obTransacted.Value;
else g_dEnabled.Remove(strPrefix);
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -20,11 +20,11 @@
*/
using System;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using System.IO;
using System.Text;
using KeePassLib.Cryptography;
using KeePassLib.Native;
using KeePassLib.Utility;
@ -74,7 +74,7 @@ namespace KeePassLib.Serialization
public sealed class HashedBlockStream : Stream
{
private const int m_nDefaultBufferSize = 1024 * 1024; // 1 MB
private const int NbDefaultBufferSize = 1024 * 1024; // 1 MB
private Stream m_sBaseStream;
private bool m_bWriting;
@ -87,7 +87,7 @@ namespace KeePassLib.Serialization
private byte[] m_pbBuffer;
private int m_nBufferPos = 0;
private uint m_uBufferIndex = 0;
private uint m_uBlockIndex = 0;
public override bool CanRead
{
@ -106,13 +106,13 @@ namespace KeePassLib.Serialization
public override long Length
{
get { throw new NotSupportedException(); }
get { Debug.Assert(false); throw new NotSupportedException(); }
}
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
get { Debug.Assert(false); throw new NotSupportedException(); }
set { Debug.Assert(false); throw new NotSupportedException(); }
}
public HashedBlockStream(Stream sBaseStream, bool bWriting)
@ -137,25 +137,25 @@ namespace KeePassLib.Serialization
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize");
if(nBufferSize == 0) nBufferSize = m_nDefaultBufferSize;
if(nBufferSize == 0) nBufferSize = NbDefaultBufferSize;
m_sBaseStream = sBaseStream;
m_bWriting = bWriting;
m_bVerify = bVerify;
UTF8Encoding utf8 = StrUtil.Utf8;
if(m_bWriting == false) // Reading mode
if(!m_bWriting) // Reading mode
{
if(m_sBaseStream.CanRead == false)
if(!m_sBaseStream.CanRead)
throw new InvalidOperationException();
m_brInput = new BinaryReader(sBaseStream, utf8);
m_pbBuffer = new byte[0];
m_pbBuffer = MemUtil.EmptyByteArray;
}
else // Writing mode
{
if(m_sBaseStream.CanWrite == false)
if(!m_sBaseStream.CanWrite)
throw new InvalidOperationException();
m_bwOutput = new BinaryWriter(sBaseStream, utf8);
@ -169,7 +169,7 @@ namespace KeePassLib.Serialization
if(m_bWriting) m_bwOutput.Flush();
}
#if KeePassRT
#if KeePassUAP
protected override void Dispose(bool disposing)
{
if(!disposing) return;
@ -179,7 +179,7 @@ namespace KeePassLib.Serialization
#endif
if(m_sBaseStream != null)
{
if(m_bWriting == false) // Reading mode
if(!m_bWriting) // Reading mode
{
m_brInput.Close();
m_brInput = null;
@ -224,7 +224,7 @@ namespace KeePassLib.Serialization
if(m_nBufferPos == m_pbBuffer.Length)
{
if(ReadHashedBlock() == false)
return nCount - nRemaining; // Bytes actually read
return (nCount - nRemaining); // Bytes actually read
}
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nRemaining);
@ -246,9 +246,9 @@ namespace KeePassLib.Serialization
m_nBufferPos = 0;
if(m_brInput.ReadUInt32() != m_uBufferIndex)
if(m_brInput.ReadUInt32() != m_uBlockIndex)
throw new InvalidDataException();
++m_uBufferIndex;
++m_uBlockIndex;
byte[] pbStoredHash = m_brInput.ReadBytes(32);
if((pbStoredHash == null) || (pbStoredHash.Length != 32))
@ -273,7 +273,7 @@ namespace KeePassLib.Serialization
}
m_bEos = true;
m_pbBuffer = new byte[0];
m_pbBuffer = MemUtil.EmptyByteArray;
return false;
}
@ -283,17 +283,13 @@ namespace KeePassLib.Serialization
if(m_bVerify)
{
SHA256Managed sha256 = new SHA256Managed();
byte[] pbComputedHash = sha256.ComputeHash(m_pbBuffer);
byte[] pbComputedHash = CryptoUtil.HashSha256(m_pbBuffer);
if((pbComputedHash == null) || (pbComputedHash.Length != 32))
throw new InvalidOperationException();
for(int iHashPos = 0; iHashPos < 32; ++iHashPos)
{
if(pbStoredHash[iHashPos] != pbComputedHash[iHashPos])
if(!MemUtil.ArraysEqual(pbStoredHash, pbComputedHash))
throw new InvalidDataException();
}
}
return true;
}
@ -320,26 +316,24 @@ namespace KeePassLib.Serialization
private void WriteHashedBlock()
{
m_bwOutput.Write(m_uBufferIndex);
++m_uBufferIndex;
m_bwOutput.Write(m_uBlockIndex);
++m_uBlockIndex;
if(m_nBufferPos > 0)
{
SHA256Managed sha256 = new SHA256Managed();
byte[] pbHash = CryptoUtil.HashSha256(m_pbBuffer, 0, m_nBufferPos);
#if !KeePassLibSD
byte[] pbHash = sha256.ComputeHash(m_pbBuffer, 0, m_nBufferPos);
#else
byte[] pbHash;
if(m_nBufferPos == m_pbBuffer.Length)
pbHash = sha256.ComputeHash(m_pbBuffer);
else
{
byte[] pbData = new byte[m_nBufferPos];
Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
pbHash = sha256.ComputeHash(pbData);
}
#endif
// SHA256Managed sha256 = new SHA256Managed();
// byte[] pbHash;
// if(m_nBufferPos == m_pbBuffer.Length)
// pbHash = sha256.ComputeHash(m_pbBuffer);
// else
// {
// byte[] pbData = new byte[m_nBufferPos];
// Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
// pbHash = sha256.ComputeHash(pbData);
// }
m_bwOutput.Write(pbHash);
}

View File

@ -0,0 +1,325 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Resources;
using KeePassLib.Utility;
namespace KeePassLib.Serialization
{
public sealed class HmacBlockStream : Stream
{
private const int NbDefaultBufferSize = 1024 * 1024; // 1 MB
private Stream m_sBase;
private readonly bool m_bWriting;
private readonly bool m_bVerify;
private byte[] m_pbKey;
private bool m_bEos = false;
private byte[] m_pbBuffer;
private int m_iBufferPos = 0;
private ulong m_uBlockIndex = 0;
public override bool CanRead
{
get { return !m_bWriting; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return m_bWriting; }
}
public override long Length
{
get { Debug.Assert(false); throw new NotSupportedException(); }
}
public override long Position
{
get { Debug.Assert(false); throw new NotSupportedException(); }
set { Debug.Assert(false); throw new NotSupportedException(); }
}
public HmacBlockStream(Stream sBase, bool bWriting, bool bVerify,
byte[] pbKey)
{
if(sBase == null) throw new ArgumentNullException("sBase");
if(pbKey == null) throw new ArgumentNullException("pbKey");
m_sBase = sBase;
m_bWriting = bWriting;
m_bVerify = bVerify;
m_pbKey = pbKey;
if(!m_bWriting) // Reading mode
{
if(!m_sBase.CanRead) throw new InvalidOperationException();
m_pbBuffer = MemUtil.EmptyByteArray;
}
else // Writing mode
{
if(!m_sBase.CanWrite) throw new InvalidOperationException();
m_pbBuffer = new byte[NbDefaultBufferSize];
}
}
public override void Flush()
{
Debug.Assert(m_sBase != null); // Object should not be disposed
if(m_bWriting && (m_sBase != null)) m_sBase.Flush();
}
#if KeePassUAP
protected override void Dispose(bool disposing)
{
if(!disposing) return;
#else
public override void Close()
{
#endif
if(m_sBase != null)
{
if(m_bWriting)
{
if(m_iBufferPos == 0) // No data left in buffer
WriteSafeBlock(); // Write terminating block
else
{
WriteSafeBlock(); // Write remaining buffered data
WriteSafeBlock(); // Write terminating block
}
Flush();
}
m_sBase.Close();
m_sBase = null;
}
}
public override long Seek(long lOffset, SeekOrigin soOrigin)
{
Debug.Assert(false);
throw new NotSupportedException();
}
public override void SetLength(long lValue)
{
Debug.Assert(false);
throw new NotSupportedException();
}
internal static byte[] GetHmacKey64(byte[] pbKey, ulong uBlockIndex)
{
if(pbKey == null) throw new ArgumentNullException("pbKey");
Debug.Assert(pbKey.Length == 64);
// We are computing the HMAC using SHA-256, whose internal
// block size is 512 bits; thus create a key that is 512
// bits long (using SHA-512)
byte[] pbBlockKey;
using(SHA512Managed h = new SHA512Managed())
{
byte[] pbIndex = MemUtil.UInt64ToBytes(uBlockIndex);
h.TransformBlock(pbIndex, 0, pbIndex.Length, pbIndex, 0);
h.TransformBlock(pbKey, 0, pbKey.Length, pbKey, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbBlockKey = h.Hash;
}
#if DEBUG
byte[] pbZero = new byte[64];
Debug.Assert((pbBlockKey.Length == 64) && !MemUtil.ArraysEqual(
pbBlockKey, pbZero)); // Ensure we own pbBlockKey
#endif
return pbBlockKey;
}
public override int Read(byte[] pbBuffer, int iOffset, int nCount)
{
if(m_bWriting) throw new InvalidOperationException();
int nRemaining = nCount;
while(nRemaining > 0)
{
if(m_iBufferPos == m_pbBuffer.Length)
{
if(!ReadSafeBlock())
return (nCount - nRemaining); // Bytes actually read
}
int nCopy = Math.Min(m_pbBuffer.Length - m_iBufferPos, nRemaining);
Debug.Assert(nCopy > 0);
Array.Copy(m_pbBuffer, m_iBufferPos, pbBuffer, iOffset, nCopy);
iOffset += nCopy;
m_iBufferPos += nCopy;
nRemaining -= nCopy;
}
return nCount;
}
private bool ReadSafeBlock()
{
if(m_bEos) return false; // End of stream reached already
byte[] pbStoredHmac = MemUtil.Read(m_sBase, 32);
if((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
throw new EndOfStreamException();
// Block index is implicit: it's used in the HMAC computation,
// but does not need to be stored
// byte[] pbBlockIndex = MemUtil.Read(m_sBase, 8);
// if((pbBlockIndex == null) || (pbBlockIndex.Length != 8))
// throw new EndOfStreamException();
// ulong uBlockIndex = MemUtil.BytesToUInt64(pbBlockIndex);
// if((uBlockIndex != m_uBlockIndex) && m_bVerify)
// throw new InvalidDataException();
byte[] pbBlockIndex = MemUtil.UInt64ToBytes(m_uBlockIndex);
byte[] pbBlockSize = MemUtil.Read(m_sBase, 4);
if((pbBlockSize == null) || (pbBlockSize.Length != 4))
throw new EndOfStreamException();
int nBlockSize = MemUtil.BytesToInt32(pbBlockSize);
if(nBlockSize < 0)
throw new InvalidDataException(KLRes.FileCorrupted);
m_iBufferPos = 0;
m_pbBuffer = MemUtil.Read(m_sBase, nBlockSize);
if((m_pbBuffer == null) || ((m_pbBuffer.Length != nBlockSize) && m_bVerify))
throw new EndOfStreamException();
if(m_bVerify)
{
byte[] pbCmpHmac;
byte[] pbBlockKey = GetHmacKey64(m_pbKey, m_uBlockIndex);
using(HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
h.TransformBlock(pbBlockIndex, 0, pbBlockIndex.Length,
pbBlockIndex, 0);
h.TransformBlock(pbBlockSize, 0, pbBlockSize.Length,
pbBlockSize, 0);
if(m_pbBuffer.Length > 0)
h.TransformBlock(m_pbBuffer, 0, m_pbBuffer.Length,
m_pbBuffer, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbCmpHmac = h.Hash;
}
MemUtil.ZeroByteArray(pbBlockKey);
if(!MemUtil.ArraysEqual(pbCmpHmac, pbStoredHmac))
throw new InvalidDataException(KLRes.FileCorrupted);
}
++m_uBlockIndex;
if(nBlockSize == 0)
{
m_bEos = true;
return false; // No further data available
}
return true;
}
public override void Write(byte[] pbBuffer, int iOffset, int nCount)
{
if(!m_bWriting) throw new InvalidOperationException();
while(nCount > 0)
{
if(m_iBufferPos == m_pbBuffer.Length)
WriteSafeBlock();
int nCopy = Math.Min(m_pbBuffer.Length - m_iBufferPos, nCount);
Debug.Assert(nCopy > 0);
Array.Copy(pbBuffer, iOffset, m_pbBuffer, m_iBufferPos, nCopy);
iOffset += nCopy;
m_iBufferPos += nCopy;
nCount -= nCopy;
}
}
private void WriteSafeBlock()
{
byte[] pbBlockIndex = MemUtil.UInt64ToBytes(m_uBlockIndex);
int cbBlockSize = m_iBufferPos;
byte[] pbBlockSize = MemUtil.Int32ToBytes(cbBlockSize);
byte[] pbBlockHmac;
byte[] pbBlockKey = GetHmacKey64(m_pbKey, m_uBlockIndex);
using(HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
h.TransformBlock(pbBlockIndex, 0, pbBlockIndex.Length,
pbBlockIndex, 0);
h.TransformBlock(pbBlockSize, 0, pbBlockSize.Length,
pbBlockSize, 0);
if(cbBlockSize > 0)
h.TransformBlock(m_pbBuffer, 0, cbBlockSize, m_pbBuffer, 0);
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
pbBlockHmac = h.Hash;
}
MemUtil.ZeroByteArray(pbBlockKey);
MemUtil.Write(m_sBase, pbBlockHmac);
// MemUtil.Write(m_sBase, pbBlockIndex); // Implicit
MemUtil.Write(m_sBase, pbBlockSize);
if(cbBlockSize > 0)
m_sBase.Write(m_pbBuffer, 0, cbBlockSize);
++m_uBlockIndex;
m_iBufferPos = 0;
}
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,12 +19,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.ComponentModel;
using System.Xml.Serialization;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using KeePassLib.Interfaces;
using KeePassLib.Utility;
@ -122,7 +121,7 @@ namespace KeePassLib.Serialization
private bool m_bComplete = false;
[XmlIgnore]
internal bool IsComplete // Credentials etc. fully specified
public bool IsComplete // Credentials etc. fully specified
{
get { return m_bComplete; }
set { m_bComplete = value; }
@ -134,16 +133,53 @@ namespace KeePassLib.Serialization
set { m_ioHint = value; }
} */
public IOConnectionInfo CloneDeep()
private IocProperties m_props = new IocProperties();
[XmlIgnore]
public IocProperties Properties
{
return (IOConnectionInfo)this.MemberwiseClone();
get { return m_props; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_props = value;
}
}
/// <summary>
/// For serialization only; use <c>Properties</c> in code.
/// </summary>
[DefaultValue("")]
public string PropertiesEx
{
get { return m_props.Serialize(); }
set
{
if(value == null) throw new ArgumentNullException("value");
IocProperties p = IocProperties.Deserialize(value);
Debug.Assert(p != null);
m_props = (p ?? new IocProperties());
}
}
public IOConnectionInfo CloneDeep()
{
IOConnectionInfo ioc = (IOConnectionInfo)this.MemberwiseClone();
ioc.m_props = m_props.CloneDeep();
return ioc;
}
#if DEBUG // For debugger display only
public override string ToString()
{
return GetDisplayName();
}
#endif
/// <summary>
/// Serialize the current connection info to a string. Credentials
/// are only serialized if the <c>SaveCredentials</c> property
/// is <c>true</c>.
/// are serialized based on the <c>CredSaveMode</c> property.
/// </summary>
/// <param name="iocToCompile">Input object to be serialized.</param>
/// <returns>Serialized object as string.</returns>
@ -215,9 +251,8 @@ namespace KeePassLib.Serialization
s.Password = TransformUnreadable(vParts[3], false);
return s;
}
/// <summary>
/// Very simple string protection. Doesn't really encrypt the input
/// string, only encodes it that it's not readable on the first glance.
@ -256,21 +291,21 @@ namespace KeePassLib.Serialization
return StrUtil.Utf8.GetString(pbBase, 0, pbBase.Length);
}
}
public string GetDisplayName()
{
string str = m_strUrl;
if(m_strUser.Length > 0)
str += " (" + m_strUser + ")";
str += (" (" + m_strUser + ")");
return str;
}
public bool IsEmpty()
{
return (m_strUrl.Length > 0);
return (m_strUrl.Length == 0);
}
public static IOConnectionInfo FromPath(string strPath)
@ -293,7 +328,7 @@ namespace KeePassLib.Serialization
public bool IsLocalFile()
{
// Not just ":/", see e.g. AppConfigEx.ChangePathRelAbs
return (m_strUrl.IndexOf(@"://") < 0);
return (m_strUrl.IndexOf("://") < 0);
}
public void ClearCredentials(bool bDependingOnRememberMode)

View File

@ -0,0 +1,192 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Xml;
using KeePassLib.Interfaces;
using KeePassLib.Utility;
using StrDict = System.Collections.Generic.Dictionary<string, string>;
namespace KeePassLib.Serialization
{
public interface IHasIocProperties
{
IocProperties IOConnectionProperties { get; set; }
}
public sealed class IocProperties : IDeepCloneable<IocProperties>
{
private StrDict m_dict = new StrDict();
public IocProperties()
{
}
public IocProperties CloneDeep()
{
IocProperties p = new IocProperties();
p.m_dict = new StrDict(m_dict);
return p;
}
public string Get(string strKey)
{
if(string.IsNullOrEmpty(strKey)) return null;
foreach(KeyValuePair<string, string> kvp in m_dict)
{
if(kvp.Key.Equals(strKey, StrUtil.CaseIgnoreCmp))
return kvp.Value;
}
return null;
}
public void Set(string strKey, string strValue)
{
if(string.IsNullOrEmpty(strKey)) { Debug.Assert(false); return; }
foreach(KeyValuePair<string, string> kvp in m_dict)
{
if(kvp.Key.Equals(strKey, StrUtil.CaseIgnoreCmp))
{
if(string.IsNullOrEmpty(strValue)) m_dict.Remove(kvp.Key);
else m_dict[kvp.Key] = strValue;
return;
}
}
if(!string.IsNullOrEmpty(strValue)) m_dict[strKey] = strValue;
}
public bool? GetBool(string strKey)
{
string str = Get(strKey);
if(string.IsNullOrEmpty(str)) return null;
return StrUtil.StringToBool(str);
}
public void SetBool(string strKey, bool? ob)
{
if(ob.HasValue) Set(strKey, (ob.Value ? "1" : "0"));
else Set(strKey, null);
}
public long? GetLong(string strKey)
{
string str = Get(strKey);
if(string.IsNullOrEmpty(str)) return null;
long l;
if(StrUtil.TryParseLongInvariant(str, out l)) return l;
Debug.Assert(false);
return null;
}
public void SetLong(string strKey, long? ol)
{
if(ol.HasValue)
Set(strKey, ol.Value.ToString(NumberFormatInfo.InvariantInfo));
else Set(strKey, null);
}
public string Serialize()
{
if(m_dict.Count == 0) return string.Empty;
StringBuilder sbAll = new StringBuilder();
foreach(KeyValuePair<string, string> kvp in m_dict)
{
sbAll.Append(kvp.Key);
sbAll.Append(kvp.Value);
}
string strAll = sbAll.ToString();
char chSepOuter = ';';
if(strAll.IndexOf(chSepOuter) >= 0)
chSepOuter = StrUtil.GetUnusedChar(strAll);
strAll += chSepOuter;
char chSepInner = '=';
if(strAll.IndexOf(chSepInner) >= 0)
chSepInner = StrUtil.GetUnusedChar(strAll);
StringBuilder sb = new StringBuilder();
sb.Append(chSepOuter);
sb.Append(chSepInner);
foreach(KeyValuePair<string, string> kvp in m_dict)
{
sb.Append(chSepOuter);
sb.Append(kvp.Key);
sb.Append(chSepInner);
sb.Append(kvp.Value);
}
return sb.ToString();
}
public static IocProperties Deserialize(string strSerialized)
{
IocProperties p = new IocProperties();
if(string.IsNullOrEmpty(strSerialized)) return p; // No assert
char chSepOuter = strSerialized[0];
string[] v = strSerialized.Substring(1).Split(new char[] { chSepOuter });
if((v == null) || (v.Length < 2)) { Debug.Assert(false); return p; }
string strMeta = v[0];
if(string.IsNullOrEmpty(strMeta)) { Debug.Assert(false); return p; }
char chSepInner = strMeta[0];
char[] vSepInner = new char[] { chSepInner };
for(int i = 1; i < v.Length; ++i)
{
string strProp = v[i];
if(string.IsNullOrEmpty(strProp)) { Debug.Assert(false); continue; }
string[] vProp = strProp.Split(vSepInner);
if((vProp == null) || (vProp.Length < 2)) { Debug.Assert(false); continue; }
Debug.Assert(vProp.Length == 2);
p.Set(vProp[0], vProp[1]);
}
return p;
}
public void CopyTo(IocProperties p)
{
if(p == null) { Debug.Assert(false); return; }
foreach(KeyValuePair<string, string> kvp in m_dict)
{
p.m_dict[kvp.Key] = kvp.Value;
}
}
}
}

View File

@ -0,0 +1,99 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Serialization
{
public sealed class IocPropertyInfo
{
private readonly string m_strName;
public string Name
{
get { return m_strName; }
}
private readonly Type m_t;
public Type Type
{
get { return m_t; }
}
private string m_strDisplayName;
public string DisplayName
{
get { return m_strDisplayName; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strDisplayName = value;
}
}
private List<string> m_lProtocols = new List<string>();
public IEnumerable<string> Protocols
{
get { return m_lProtocols; }
}
public IocPropertyInfo(string strName, Type t, string strDisplayName,
string[] vProtocols)
{
if(strName == null) throw new ArgumentNullException("strName");
if(t == null) throw new ArgumentNullException("t");
if(strDisplayName == null) throw new ArgumentNullException("strDisplayName");
m_strName = strName;
m_t = t;
m_strDisplayName = strDisplayName;
AddProtocols(vProtocols);
}
public void AddProtocols(string[] v)
{
if(v == null) { Debug.Assert(false); return; }
foreach(string strProtocol in v)
{
if(strProtocol == null) continue;
string str = strProtocol.Trim();
if(str.Length == 0) continue;
bool bFound = false;
foreach(string strEx in m_lProtocols)
{
if(strEx.Equals(str, StrUtil.CaseIgnoreCmp))
{
bFound = true;
break;
}
}
if(!bFound) m_lProtocols.Add(str);
}
}
}
}

View File

@ -0,0 +1,123 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using KeePassLib.Resources;
using KeePassLib.Utility;
namespace KeePassLib.Serialization
{
public static class IocKnownProtocols
{
public const string Http = "HTTP";
public const string Https = "HTTPS";
public const string WebDav = "WebDAV";
public const string Ftp = "FTP";
}
public static class IocKnownProperties
{
public const string Timeout = "Timeout";
public const string PreAuth = "PreAuth";
public const string UserAgent = "UserAgent";
public const string Expect100Continue = "Expect100Continue";
public const string Passive = "Passive";
}
public static class IocPropertyInfoPool
{
private static List<IocPropertyInfo> m_l = null;
public static IEnumerable<IocPropertyInfo> PropertyInfos
{
get { EnsureInitialized(); return m_l; }
}
private static void EnsureInitialized()
{
if(m_l != null) return;
string strGen = KLRes.General;
string strHttp = IocKnownProtocols.Http;
string strHttps = IocKnownProtocols.Https;
string strWebDav = IocKnownProtocols.WebDav;
string strFtp = IocKnownProtocols.Ftp;
string[] vGen = new string[] { strGen };
string[] vHttp = new string[] { strHttp, strHttps, strWebDav };
string[] vFtp = new string[] { strFtp };
List<IocPropertyInfo> l = new List<IocPropertyInfo>();
l.Add(new IocPropertyInfo(IocKnownProperties.Timeout,
typeof(long), KLRes.Timeout + " [ms]", vGen));
l.Add(new IocPropertyInfo(IocKnownProperties.PreAuth,
typeof(bool), KLRes.PreAuth, vGen));
l.Add(new IocPropertyInfo(IocKnownProperties.UserAgent,
typeof(string), KLRes.UserAgent, vHttp));
l.Add(new IocPropertyInfo(IocKnownProperties.Expect100Continue,
typeof(bool), KLRes.Expect100Continue, vHttp));
l.Add(new IocPropertyInfo(IocKnownProperties.Passive,
typeof(bool), KLRes.Passive, vFtp));
// l.Add(new IocPropertyInfo("Test", typeof(bool),
// "Long long long long long long long long long long long long long long long long long long long long",
// new string[] { "Proto 1/9", "Proto 2/9", "Proto 3/9", "Proto 4/9", "Proto 5/9",
// "Proto 6/9", "Proto 7/9", "Proto 8/9", "Proto 9/9" }));
m_l = l;
}
public static IocPropertyInfo Get(string strName)
{
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
EnsureInitialized();
foreach(IocPropertyInfo pi in m_l)
{
if(pi.Name.Equals(strName, StrUtil.CaseIgnoreCmp))
return pi;
}
return null;
}
public static bool Add(IocPropertyInfo pi)
{
if(pi == null) { Debug.Assert(false); return false; }
// Name must be non-empty
string strName = pi.Name;
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
IocPropertyInfo piEx = Get(strName); // Ensures initialized
if(piEx != null) { Debug.Assert(false); return false; } // Exists already
m_l.Add(pi);
return true;
}
}
}

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -21,18 +19,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using System.Drawing;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
#if !KeePassUAP
using System.Drawing;
#endif
using KeePassLib;
using KeePassLib.Collections;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Interfaces;
using KeePassLib.Resources;
using KeePassLib.Security;
@ -45,49 +42,6 @@ namespace KeePassLib.Serialization
/// </summary>
public sealed partial class KdbxFile
{
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
if (colorString.StartsWith("#"))
{
colorString = colorString.Substring(1);
}
if (colorString.EndsWith(";"))
{
colorString = colorString.Substring(0, colorString.Length - 1);
}
int red, green, blue;
switch (colorString.Length)
{
case 6:
red = int.Parse(colorString.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 3:
red = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(2, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 1:
red = green = blue = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
default:
throw new ArgumentException("Invalid color: " + colorString);
}
return color;
}
}
private enum KdbContext
{
Null,
@ -104,13 +58,17 @@ namespace KeePassLib.Serialization
DeletedObject,
Group,
GroupTimes,
GroupCustomData,
GroupCustomDataItem,
Entry,
EntryTimes,
EntryString,
EntryBinary,
EntryAutoType,
EntryAutoTypeItem,
EntryHistory
EntryHistory,
EntryCustomData,
EntryCustomDataItem
}
private bool m_bReadNextNode = true;
@ -130,10 +88,14 @@ namespace KeePassLib.Serialization
private byte[] m_pbCustomIconData = null;
private string m_strCustomDataKey = null;
private string m_strCustomDataValue = null;
private string m_strGroupCustomDataKey = null;
private string m_strGroupCustomDataValue = null;
private string m_strEntryCustomDataKey = null;
private string m_strEntryCustomDataValue = null;
private void ReadXmlStreamed(Stream readerStream, Stream sParentStream)
private void ReadXmlStreamed(Stream sXml, Stream sParent)
{
ReadDocumentStreamed(CreateXmlReader(readerStream), sParentStream);
ReadDocumentStreamed(CreateXmlReader(sXml), sParent);
}
internal static XmlReaderSettings CreateStdXmlReaderSettings()
@ -145,9 +107,12 @@ namespace KeePassLib.Serialization
xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = true;
#if !KeePassRT
#if KeePassUAP
xrs.DtdProcessing = DtdProcessing.Prohibit;
#else
#if !KeePassLibSD
xrs.ProhibitDtd = true;
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
#endif
xrs.ValidationType = ValidationType.None;
#endif
@ -258,15 +223,25 @@ namespace KeePassLib.Serialization
ReadString(xr); // Ignore
else if(xr.Name == ElemHeaderHash)
{
// The header hash is typically only stored in
// KDBX <= 3.1 files, not in KDBX >= 4 files
// (here, the header is verified via a HMAC),
// but we also support it for KDBX >= 4 files
// (i.e. if it's present, we check it)
string strHash = ReadString(xr);
if(!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
!m_bRepairMode)
{
Debug.Assert(m_uFileVersion <= FileVersion32_3);
byte[] pbHash = Convert.FromBase64String(strHash);
if(!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))
throw new IOException(KLRes.FileCorrupted);
}
}
else if(xr.Name == ElemSettingsChanged)
m_pwDatabase.SettingsChanged = ReadTime(xr);
else if(xr.Name == ElemDbName)
m_pwDatabase.Name = ReadString(xr);
else if(xr.Name == ElemDbNameChanged)
@ -426,6 +401,8 @@ namespace KeePassLib.Serialization
m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr));
else if(xr.Name == ElemLastTopVisibleEntry)
m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr);
else if(xr.Name == ElemCustomData)
return SwitchContext(ctx, KdbContext.GroupCustomData, xr);
else if(xr.Name == ElemGroup)
{
m_ctxGroup = new PwGroup(false, false);
@ -446,6 +423,20 @@ namespace KeePassLib.Serialization
else ReadUnknown(xr);
break;
case KdbContext.GroupCustomData:
if(xr.Name == ElemStringDictExItem)
return SwitchContext(ctx, KdbContext.GroupCustomDataItem, xr);
else ReadUnknown(xr);
break;
case KdbContext.GroupCustomDataItem:
if(xr.Name == ElemKey)
m_strGroupCustomDataKey = ReadString(xr);
else if(xr.Name == ElemValue)
m_strGroupCustomDataValue = ReadString(xr);
else ReadUnknown(xr);
break;
case KdbContext.Entry:
if(xr.Name == ElemUuid)
m_ctxEntry.Uuid = ReadUuid(xr);
@ -477,6 +468,8 @@ namespace KeePassLib.Serialization
return SwitchContext(ctx, KdbContext.EntryBinary, xr);
else if(xr.Name == ElemAutoType)
return SwitchContext(ctx, KdbContext.EntryAutoType, xr);
else if(xr.Name == ElemCustomData)
return SwitchContext(ctx, KdbContext.EntryCustomData, xr);
else if(xr.Name == ElemHistory)
{
Debug.Assert(m_bEntryInHistory == false);
@ -497,15 +490,15 @@ namespace KeePassLib.Serialization
(ITimeLogger)m_ctxGroup : (ITimeLogger)m_ctxEntry);
Debug.Assert(tl != null);
if(xr.Name == ElemLastModTime)
tl.SetLazyLastModificationTime(ReadString(xr));
else if(xr.Name == ElemCreationTime)
if(xr.Name == ElemCreationTime)
tl.SetLazyCreationTime(ReadString(xr));
else if(xr.Name == ElemLastAccessTime)
else if (xr.Name == ElemLastModTime)
tl.SetLazyLastModificationTime(ReadString(xr));
else if (xr.Name == ElemLastAccessTime)
tl.SetLazyLastAccessTime(ReadString(xr));
else if(xr.Name == ElemExpiryTime)
else if (xr.Name == ElemExpiryTime)
tl.SetLazyExpiryTime(ReadString(xr));
else if(xr.Name == ElemExpires)
else if (xr.Name == ElemExpires)
tl.Expires = ReadBool(xr, false);
else if(xr.Name == ElemUsageCount)
tl.UsageCount = ReadULong(xr, 0);
@ -551,6 +544,20 @@ namespace KeePassLib.Serialization
else ReadUnknown(xr);
break;
case KdbContext.EntryCustomData:
if(xr.Name == ElemStringDictExItem)
return SwitchContext(ctx, KdbContext.EntryCustomDataItem, xr);
else ReadUnknown(xr);
break;
case KdbContext.EntryCustomDataItem:
if(xr.Name == ElemKey)
m_strEntryCustomDataKey = ReadString(xr);
else if(xr.Name == ElemValue)
m_strEntryCustomDataValue = ReadString(xr);
else ReadUnknown(xr);
break;
case KdbContext.EntryHistory:
if(xr.Name == ElemEntry)
{
@ -652,6 +659,19 @@ namespace KeePassLib.Serialization
}
else if((ctx == KdbContext.GroupTimes) && (xr.Name == ElemTimes))
return KdbContext.Group;
else if((ctx == KdbContext.GroupCustomData) && (xr.Name == ElemCustomData))
return KdbContext.Group;
else if((ctx == KdbContext.GroupCustomDataItem) && (xr.Name == ElemStringDictExItem))
{
if((m_strGroupCustomDataKey != null) && (m_strGroupCustomDataValue != null))
m_ctxGroup.CustomData.Set(m_strGroupCustomDataKey, m_strGroupCustomDataValue);
else { Debug.Assert(false); }
m_strGroupCustomDataKey = null;
m_strGroupCustomDataValue = null;
return KdbContext.GroupCustomData;
}
else if((ctx == KdbContext.Entry) && (xr.Name == ElemEntry))
{
// Create new UUID if absent
@ -702,6 +722,19 @@ namespace KeePassLib.Serialization
m_ctxATSeq = null;
return KdbContext.EntryAutoType;
}
else if((ctx == KdbContext.EntryCustomData) && (xr.Name == ElemCustomData))
return KdbContext.Entry;
else if((ctx == KdbContext.EntryCustomDataItem) && (xr.Name == ElemStringDictExItem))
{
if((m_strEntryCustomDataKey != null) && (m_strEntryCustomDataValue != null))
m_ctxEntry.CustomData.Set(m_strEntryCustomDataKey, m_strEntryCustomDataValue);
else { Debug.Assert(false); }
m_strEntryCustomDataKey = null;
m_strEntryCustomDataValue = null;
return KdbContext.EntryCustomData;
}
else if((ctx == KdbContext.EntryHistory) && (xr.Name == ElemHistory))
{
m_bEntryInHistory = false;
@ -852,7 +885,19 @@ namespace KeePassLib.Serialization
if(strRef != null)
{
ProtectedBinary pb = BinPoolGet(strRef);
if(pb != null) return pb;
if(pb != null)
{
// https://sourceforge.net/p/keepass/feature-requests/2023/
xr.MoveToElement();
#if DEBUG
string strInner = ReadStringRaw(xr);
Debug.Assert(string.IsNullOrEmpty(strInner));
#else
ReadStringRaw(xr);
#endif
return pb;
}
else { Debug.Assert(false); }
}
else { Debug.Assert(false); }
@ -914,7 +959,7 @@ namespace KeePassLib.Serialization
byte[] pbEncrypted;
if(strEncrypted.Length > 0)
pbEncrypted = Convert.FromBase64String(strEncrypted);
else pbEncrypted = new byte[0];
else pbEncrypted = MemUtil.EmptyByteArray;
byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length);

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,25 +19,31 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
#if !KeePassLibSD
using System.IO.Compression;
#else
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;
using KeePassLib.Utility;
using keepass2android;
namespace KeePassLib.Serialization
@ -48,80 +54,119 @@ 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 || kdbFormat == KdbxFormat.ProtocolBuffers)
Stream sXml;
if (fmt == KdbxFormat.Default || fmt == KdbxFormat.ProtocolBuffers)
{
br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
ReadHeader(br);
Stream sDecrypted = AttachStreamDecryptor(hashedStream);
if((sDecrypted == null) || (sDecrypted == hashedStream))
throw new SecurityException(KLRes.CryptoStreamFailed);
BinaryReaderEx br = new BinaryReaderEx(sHashing,
encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new InvalidDataException();
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
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);
Stream sHashed = new HashedBlockStream(sDecrypted, false, 0,
!m_bRepairMode);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
lStreams.Add(sDecrypted);
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();
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
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)
{
@ -135,39 +180,60 @@ namespace KeePassLib.Serialization
else m_randomStream = null; // No random stream for plain-text files
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo);
#if KeePassDebug_WriteXml
// FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
// FileAccess.Write, FileShare.None);
// try
// {
// while(true)
// {
// int b = sXml.ReadByte();
// if(b == -1) break;
// fsOut.WriteByte((byte)b);
// }
// }
// catch(Exception) { }
// fsOut.Close();
#endif
var stopWatch = Stopwatch.StartNew();
if (kdbFormat == KdbxFormat.ProtocolBuffers)
if (fmt == KdbxFormat.ProtocolBuffers)
{
KdbpFile.ReadDocument(m_pwDatabase, readerStream, m_pbProtectedStreamKey, m_pbHashOfHeader);
KdbpFile.ReadDocument(m_pwDatabase, sXml, m_pbProtectedStreamKey, m_pbHashOfHeader);
Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
}
else
{
ReadXmlStreamed(readerStream, hashedStream);
ReadXmlStreamed(sXml, sHashing);
Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds));
}
readerStream.Close();
// GC.KeepAlive(br);
// GC.KeepAlive(brDecrypted);
// 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)
@ -180,10 +246,16 @@ namespace KeePassLib.Serialization
// the history maintenance settings)
m_pwDatabase.MaintainBackups(); // Don't mark database as modified
// Expand the root group, such that in case the user accidently
// collapses the root group he can simply reopen the database
PwGroup pgRoot = m_pwDatabase.RootGroup;
if(pgRoot != null) pgRoot.IsExpanded = true;
else { Debug.Assert(false); }
m_pbHashOfHeader = null;
}
private void ReadHeader(BinaryReaderEx br)
private byte[] LoadHeader(BinaryReaderEx br)
{
MemoryStream msHeader = new MemoryStream();
Debug.Assert(br.CopyDataTo == null);
@ -208,18 +280,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)
@ -228,15 +301,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;
}
@ -262,13 +341,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:
@ -281,6 +374,7 @@ namespace KeePassLib.Serialization
break;
case KdbxHeaderFieldID.StreamStartBytes:
Debug.Assert(m_uFileVersion <= FileVersion32_3);
m_pbStreamStartBytes = pbData;
break;
@ -288,6 +382,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)
@ -301,7 +404,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);
@ -325,36 +428,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);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
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)
{

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -21,30 +19,35 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Security;
using System.Security.Cryptography;
using System.Drawing;
using System.Globalization;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
using System.Text;
using System.Xml;
#if !KeePassLibSD
using System.IO.Compression;
#else
#if !KeePassUAP
using System.Drawing;
using System.Security.Cryptography;
#endif
#if KeePassLibSD
using KeePassLibSD;
#else
using System.IO.Compression;
#endif
using KeePassLib.Collections;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Delegates;
using KeePassLib.Interfaces;
using KeePassLib.Keys;
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
using keepass2android;
namespace KeePassLib.Serialization
@ -54,7 +57,7 @@ namespace KeePassLib.Serialization
/// </summary>
public sealed partial class KdbxFile
{
// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat format,
// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat fmt,
// IStatusLogger slLogger)
// {
// bool bMadeUnhidden = UrlUtil.UnhideFile(strFile);
@ -72,175 +75,257 @@ namespace KeePassLib.Serialization
/// <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="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 format,
public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat fmt,
IStatusLogger slLogger)
{
Debug.Assert(sSaveTo != null);
if(sSaveTo == null) throw new ArgumentNullException("sSaveTo");
m_format = format;
m_format = fmt;
m_slLogger = slLogger;
HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null);
UTF8Encoding encNoBom = StrUtil.Utf8;
CryptoRandom cr = CryptoRandom.Instance;
byte[] pbCipherKey = null;
byte[] pbHmacKey64 = null;
List<Stream> lStreams = new List<Stream>();
lStreams.Add(sSaveTo);
HashingStreamEx sHashing = new HashingStreamEx(sSaveTo, true, null);
lStreams.Add(sHashing);
try
{
m_pbMasterSeed = cr.GetRandomBytes(32);
m_pbTransformSeed = cr.GetRandomBytes(32);
m_pbEncryptionIV = cr.GetRandomBytes(16);
m_uFileVersion = GetMinKdbxVersion();
m_pbProtectedStreamKey = cr.GetRandomBytes(32);
m_craInnerRandomStream = CrsAlgorithm.Salsa20;
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_uFileVersion <= FileVersion32_3)
{
m_craInnerRandomStream = CrsAlgorithm.Salsa20;
m_pbProtectedStreamKey = cr.GetRandomBytes(32);
}
else // KDBX >= 4
{
m_craInnerRandomStream = CrsAlgorithm.ChaCha20;
m_pbProtectedStreamKey = cr.GetRandomBytes(64);
}
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbProtectedStreamKey);
m_pbStreamStartBytes = cr.GetRandomBytes(32);
if(m_uFileVersion <= FileVersion32_3)
m_pbStreamStartBytes = cr.GetRandomBytes(32);
Stream writerStream;
if(m_format == KdbxFormat.Default || m_format == KdbxFormat.ProtocolBuffers)
Stream sXml;
if (m_format == KdbxFormat.Default || m_format == KdbxFormat.ProtocolBuffers)
{
WriteHeader(hashedStream); // Also flushes the stream
byte[] pbHeader = GenerateHeader();
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
Stream sEncrypted = AttachStreamEncryptor(hashedStream);
if((sEncrypted == null) || (sEncrypted == hashedStream))
throw new SecurityException(KLRes.CryptoStreamFailed);
MemUtil.Write(sHashing, pbHeader);
sHashing.Flush();
sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
Stream sHashed = new HashedBlockStream(sEncrypted, true);
Stream sPlain;
if(m_uFileVersion <= FileVersion32_3)
{
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
{
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)
writerStream = new GZipStream(sHashed, CompressionMode.Compress);
else
writerStream = sHashed;
{
sXml = new GZipStream(sPlain, CompressionMode.Compress);
lStreams.Add(sXml);
}
else sXml = sPlain;
}
else if(m_format == KdbxFormat.PlainXml)
writerStream = hashedStream;
else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
sXml = sHashing;
else
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("fmt");
}
var stopWatch = Stopwatch.StartNew();
if (m_format == KdbxFormat.ProtocolBuffers)
{
KdbpFile.WriteDocument(m_pwDatabase, writerStream, m_pbProtectedStreamKey, m_pbHashOfHeader);
KdbpFile.WriteDocument(m_pwDatabase, sXml, m_pbProtectedStreamKey, m_pbHashOfHeader);
}
else
{
m_xmlWriter = new XmlTextWriter(writerStream, encNoBom);
#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(pgDataSource);
m_xmlWriter.Flush();
m_xmlWriter.Close();
}
writerStream.Close();
Kp2aLog.Log(String.Format("{1}: {0}ms", stopWatch.ElapsedMilliseconds, m_format == KdbxFormat.ProtocolBuffers ? "KdbpFile.WriteDocument" : "Xml WriteDocument"));
}
finally { CommonCleanUpWrite(sSaveTo, hashedStream); }
finally
{
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
CommonCleanUpWrite(lStreams, sHashing);
}
}
private void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream)
private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
{
hashedStream.Close();
m_pbHashOfFileOnDisk = hashedStream.Hash;
CloseStreams(lStreams);
sSaveTo.Close();
Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
m_pbHashOfFileOnDisk = sHashing.Hash;
Debug.Assert(m_pbHashOfFileOnDisk != null);
m_xmlWriter = null;
m_pbHashOfHeader = null;
}
private void WriteHeader(Stream s)
private byte[] GenerateHeader()
{
MemoryStream ms = new MemoryStream();
byte[] pbHeader;
using(MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(m_uFileVersion));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileVersion32));
WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
m_pwDatabase.DataCipherUuid.UuidBytes);
WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
m_pwDatabase.DataCipherUuid.UuidBytes);
int nCprID = (int)m_pwDatabase.Compression;
WriteHeaderField(ms, KdbxHeaderFieldID.CompressionFlags,
MemUtil.UInt32ToBytes((uint)nCprID));
int nCprID = (int)m_pwDatabase.Compression;
WriteHeaderField(ms, KdbxHeaderFieldID.CompressionFlags,
MemUtil.UInt32ToBytes((uint)nCprID));
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed, m_pbTransformSeed);
WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds,
MemUtil.UInt64ToBytes(m_pwDatabase.KeyEncryptionRounds));
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
WriteHeaderField(ms, KdbxHeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey);
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes, m_pbStreamStartBytes);
if(m_uFileVersion <= FileVersion32_3)
{
Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
(new AesKdf()).Uuid));
WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed,
m_pwDatabase.KdfParameters.GetByteArray(AesKdf.ParamSeed));
WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds,
MemUtil.UInt64ToBytes(m_pwDatabase.KdfParameters.GetUInt64(
AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds)));
}
else
WriteHeaderField(ms, KdbxHeaderFieldID.KdfParameters,
KdfParameters.SerializeExt(m_pwDatabase.KdfParameters));
int nIrsID = (int)m_craInnerRandomStream;
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
MemUtil.UInt32ToBytes((uint)nIrsID));
if(m_pbEncryptionIV.Length > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[]{
(byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' });
WriteHeaderField(ms, KdbxHeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey);
byte[] pbHeader = ms.ToArray();
ms.Close();
if(m_uFileVersion <= FileVersion32_3)
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
m_pbStreamStartBytes);
SHA256Managed sha256 = new SHA256Managed();
m_pbHashOfHeader = sha256.ComputeHash(pbHeader);
int nIrsID = (int)m_craInnerRandomStream;
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
MemUtil.Int32ToBytes(nIrsID));
s.Write(pbHeader, 0, pbHeader.Length);
s.Flush();
// Write public custom data only when there is at least one item,
// because KDBX 3.1 didn't support this field yet
if(m_pwDatabase.PublicCustomData.Count > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.PublicCustomData,
VariantDictionary.Serialize(m_pwDatabase.PublicCustomData));
WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[] {
(byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' });
pbHeader = ms.ToArray();
}
return pbHeader;
}
private static void WriteHeaderField(Stream s, KdbxHeaderFieldID kdbID,
private void WriteHeaderField(Stream s, KdbxHeaderFieldID kdbID,
byte[] pbData)
{
s.WriteByte((byte)kdbID);
if(pbData != null)
byte[] pb = (pbData ?? MemUtil.EmptyByteArray);
int cb = pb.Length;
if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
Debug.Assert(m_uFileVersion > 0);
if(m_uFileVersion <= FileVersion32_3)
{
ushort uLength = (ushort)pbData.Length;
MemUtil.Write(s, MemUtil.UInt16ToBytes(uLength));
if(cb > (int)ushort.MaxValue)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("pbData");
}
if(uLength > 0) s.Write(pbData, 0, pbData.Length);
MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)cb));
}
else MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)0));
}
else MemUtil.Write(s, MemUtil.Int32ToBytes(cb));
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);
SHA256Managed sha256 = new SHA256Managed();
byte[] aesKey = 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);
if(cb > 0) s.Write(pb, 0, cb);
}
private void WriteDocument(PwGroup pgDataSource)
@ -255,10 +340,6 @@ namespace KeePassLib.Serialization
BinPoolBuild(pgRoot);
m_xmlWriter.Formatting = Formatting.Indented;
m_xmlWriter.IndentChar = '\t';
m_xmlWriter.Indentation = 1;
m_xmlWriter.WriteStartDocument(true);
m_xmlWriter.WriteStartElement(ElemDocNode);
@ -330,12 +411,15 @@ namespace KeePassLib.Serialization
{
m_xmlWriter.WriteStartElement(ElemMeta);
WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false); // Generator name
WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false);
if(m_pbHashOfHeader != null)
if((m_pbHashOfHeader != null) && (m_uFileVersion <= FileVersion32_3))
WriteObject(ElemHeaderHash, Convert.ToBase64String(
m_pbHashOfHeader), false);
if(m_uFileVersion > FileVersion32_3)
WriteObject(ElemSettingsChanged, m_pwDatabase.SettingsChanged);
WriteObject(ElemDbName, m_pwDatabase.Name, true);
WriteObject(ElemDbNameChanged, m_pwDatabase.NameChanged);
WriteObject(ElemDbDesc, m_pwDatabase.Description, true);
@ -386,6 +470,9 @@ namespace KeePassLib.Serialization
WriteObject(ElemEnableAutoType, StrUtil.BoolToStringEx(pg.EnableAutoType), false);
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
if(pg.CustomData.Count > 0)
WriteList(ElemCustomData, pg.CustomData);
}
private void EndGroup()
@ -401,7 +488,7 @@ namespace KeePassLib.Serialization
WriteObject(ElemUuid, pe.Uuid);
WriteObject(ElemIcon, (int)pe.IconId);
if(!pe.CustomIconUuid.Equals(PwUuid.Zero))
WriteObject(ElemCustomIconID, pe.CustomIconUuid);
@ -416,6 +503,9 @@ namespace KeePassLib.Serialization
WriteList(pe.Binaries);
WriteList(ElemAutoType, pe.AutoType);
if(pe.CustomData.Count > 0)
WriteList(ElemCustomData, pe.CustomData);
if(!bIsHistory) WriteList(ElemHistory, pe.History, true);
else { Debug.Assert(pe.History.UCount == 0); }
@ -468,8 +558,8 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(name);
WriteObject(ElemLastModTime, times.LastModificationTime);
WriteObject(ElemCreationTime, times.CreationTime);
WriteObject(ElemLastModTime, times.LastModificationTime);
WriteObject(ElemLastAccessTime, times.LastAccessTime);
WriteObject(ElemExpiryTime, times.ExpiryTime);
WriteObject(ElemExpires, times.Expires);

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,20 +19,25 @@
using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Diagnostics;
using System.Security;
using System.Text;
using System.Xml;
#if !KeePassLibSD
using System.IO.Compression;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Collections;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Delegates;
using KeePassLib.Interfaces;
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
@ -61,6 +66,47 @@ namespace KeePassLib.Serialization
/// </summary>
public sealed partial class KdbxFile
{
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
if (colorString.StartsWith("#"))
{
colorString = colorString.Substring(1);
}
if (colorString.EndsWith(";"))
{
colorString = colorString.Substring(0, colorString.Length - 1);
}
int red, green, blue;
switch (colorString.Length)
{
case 6:
red = int.Parse(colorString.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 3:
red = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(2, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 1:
red = green = blue = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
default:
throw new ArgumentException("Invalid color: " + colorString);
}
return color;
}
}
/// <summary>
/// File identifier, first 32-bit value.
/// </summary>
@ -78,7 +124,8 @@ namespace KeePassLib.Serialization
/// The first 2 bytes are critical (i.e. loading will fail, if the
/// file version is too high), the last 2 bytes are informational.
/// </summary>
private const uint FileVersion32 = 0x00030001;
private const uint FileVersion32 = 0x00040000;
private const uint FileVersion32_3 = 0x00030001; // Old format
private const uint FileVersionCriticalMask = 0xFFFF0000;
@ -97,6 +144,7 @@ namespace KeePassLib.Serialization
private const string ElemGenerator = "Generator";
private const string ElemHeaderHash = "HeaderHash";
private const string ElemSettingsChanged = "SettingsChanged";
private const string ElemDbName = "DatabaseName";
private const string ElemDbNameChanged = "DatabaseNameChanged";
private const string ElemDbDesc = "DatabaseDescription";
@ -192,19 +240,20 @@ namespace KeePassLib.Serialization
private PwDatabase m_pwDatabase; // Not null, see constructor
private XmlTextWriter m_xmlWriter = null;
private XmlWriter m_xmlWriter = null;
private CryptoRandomStream m_randomStream = null;
private KdbxFormat m_format = KdbxFormat.Default;
private IStatusLogger m_slLogger = null;
private uint m_uFileVersion = 0;
private byte[] m_pbMasterSeed = null;
private byte[] m_pbTransformSeed = null;
// private byte[] m_pbTransformSeed = null;
private byte[] m_pbEncryptionIV = null;
private byte[] m_pbProtectedStreamKey = null;
private byte[] m_pbStreamStartBytes = null;
// ArcFourVariant only for compatibility; KeePass will default to a
// different (more secure) algorithm when *writing* databases
// ArcFourVariant only for backward compatibility; KeePass defaults
// to a more secure algorithm when *writing* databases
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
private Dictionary<string, ProtectedBinary> m_dictBinPool =
@ -227,12 +276,14 @@ namespace KeePassLib.Serialization
CipherID = 2,
CompressionFlags = 3,
MasterSeed = 4,
TransformSeed = 5,
TransformRounds = 6,
TransformSeed = 5, // KDBX 3.1, for backward compatibility only
TransformRounds = 6, // KDBX 3.1, for backward compatibility only
EncryptionIV = 7,
ProtectedStreamKey = 8,
StreamStartBytes = 9,
InnerRandomStreamID = 10
StreamStartBytes = 9, // KDBX 3.1, for backward compatibility only
InnerRandomStreamID = 10,
KdfParameters = 11, // KDBX 4
PublicCustomData = 12 // KDBX 4
}
public byte[] HashOfFileOnDisk
@ -291,6 +342,152 @@ namespace KeePassLib.Serialization
}
}
private uint GetMinKdbxVersion()
{
AesKdf kdfAes = new AesKdf();
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
return FileVersion32;
if(m_pwDatabase.PublicCustomData.Count > 0)
return FileVersion32;
bool bCustomData = false;
GroupHandler gh = delegate(PwGroup pg)
{
if(pg == null) { Debug.Assert(false); return true; }
if(pg.CustomData.Count > 0) { bCustomData = true; return false; }
return true;
};
EntryHandler eh = delegate(PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return true; }
if(pe.CustomData.Count > 0) { bCustomData = true; return false; }
return true;
};
gh(m_pwDatabase.RootGroup);
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
if(bCustomData) return FileVersion32;
return FileVersion32_3; // KDBX 3.1 is sufficient
}
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
out byte[] pbHmacKey64)
{
byte[] pbCmp = new byte[32 + 32 + 1];
try
{
Debug.Assert(m_pbMasterSeed != null);
if(m_pbMasterSeed == null)
throw new ArgumentNullException("m_pbMasterSeed");
Debug.Assert(m_pbMasterSeed.Length == 32);
if(m_pbMasterSeed.Length != 32)
throw new FormatException(KLRes.MasterSeedLengthInvalid);
Array.Copy(m_pbMasterSeed, 0, pbCmp, 0, 32);
Debug.Assert(m_pwDatabase != null);
Debug.Assert(m_pwDatabase.MasterKey != null);
ProtectedBinary pbinUser = m_pwDatabase.MasterKey.GenerateKey32(
m_pwDatabase.KdfParameters);
Debug.Assert(pbinUser != null);
if(pbinUser == null)
throw new SecurityException(KLRes.InvalidCompositeKey);
byte[] pUserKey32 = pbinUser.ReadData();
if((pUserKey32 == null) || (pUserKey32.Length != 32))
throw new SecurityException(KLRes.InvalidCompositeKey);
Array.Copy(pUserKey32, 0, pbCmp, 32, 32);
MemUtil.ZeroByteArray(pUserKey32);
pbCipherKey = CryptoUtil.ResizeKey(pbCmp, 0, 64, cbCipherKey);
pbCmp[64] = 1;
using(SHA512Managed h = new SHA512Managed())
{
pbHmacKey64 = h.ComputeHash(pbCmp);
}
}
finally { MemUtil.ZeroByteArray(pbCmp); }
}
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;
}
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);
}
private byte[] ComputeHeaderHmac(byte[] pbHeader, byte[] pbKey)
{
byte[] pbHeaderHmac;
byte[] pbBlockKey = HmacBlockStream.GetHmacKey64(
pbKey, ulong.MaxValue);
using(HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
pbHeaderHmac = h.ComputeHash(pbHeader);
}
MemUtil.ZeroByteArray(pbBlockKey);
return pbHeaderHmac;
}
private void CloseStreams(List<Stream> lStreams)
{
if(lStreams == null) { Debug.Assert(false); return; }
// Typically, closing a stream also closes its base
// stream; however, there may be streams that do not
// do this (e.g. some cipher plugin), thus for safety
// we close all streams manually, from the innermost
// to the outermost
for(int i = lStreams.Count - 1; i >= 0; --i)
{
// Check for duplicates
Debug.Assert((lStreams.IndexOf(lStreams[i]) == i) &&
(lStreams.LastIndexOf(lStreams[i]) == i));
try { lStreams[i].Close(); }
catch(Exception) { Debug.Assert(false); }
}
// Do not clear the list
}
private void BinPoolBuild(PwGroup pgDataSource)
{
m_dictBinPool = new Dictionary<string, ProtectedBinary>();

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -21,15 +19,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Xml.Serialization;
using System.Diagnostics;
namespace KeePassLib.Translation
{
public class ToolStripItemCollection
{}
public sealed class KPStringTable
{
private string m_strName = string.Empty;
@ -69,9 +65,9 @@ namespace KeePassLib.Translation
return dict;
}
#if (!KeePassLibSD && !KeePassRT)
/*public void ApplyTo(ToolStripItemCollection tsic)
/*
#if (!KeePassLibSD && !KeePassUAP)
public void ApplyTo(ToolStripItemCollection tsic)
{
if(tsic == null) throw new ArgumentNullException("tsic");
@ -97,7 +93,7 @@ namespace KeePassLib.Translation
if((tsmi != null) && (tsmi.DropDownItems != null))
this.ApplyTo(tsmi.DropDownItems);
}
}*/
#endif
}
#endif*/
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -21,24 +19,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Drawing;
using System.Diagnostics;
#if KeePassLibSD
using ICSharpCode.SharpZipLib.GZip;
#else
using System.IO.Compression;
#endif
using KeePassLib.Interfaces;
using KeePassLib.Utility;
#if !KeePassLibSD
using System.IO.Compression;
#else
using ICSharpCode.SharpZipLib.GZip;
#endif
namespace KeePassLib.Translation
{
[XmlRoot("Translation")]
@ -66,7 +62,7 @@ namespace KeePassLib.Translation
m_vStringTables = value;
}
}
/*
private List<KPFormCustomization> m_vForms = new List<KPFormCustomization>();
[XmlArrayItem("Form")]
@ -80,7 +76,7 @@ namespace KeePassLib.Translation
m_vForms = value;
}
}
*/
private string m_strUnusedText = string.Empty;
[DefaultValue("")]
public string UnusedText
@ -94,18 +90,25 @@ namespace KeePassLib.Translation
}
}
public static void SaveToFile(KPTranslation kpTrl, string strFileName,
public static void Save(KPTranslation kpTrl, string strFileName,
IXmlSerializerEx xs)
{
using(FileStream fs = new FileStream(strFileName, FileMode.Create,
FileAccess.Write, FileShare.None))
{
Save(kpTrl, fs, xs);
}
}
public static void Save(KPTranslation kpTrl, Stream sOut,
IXmlSerializerEx xs)
{
if(xs == null) throw new ArgumentNullException("xs");
FileStream fs = new FileStream(strFileName, FileMode.Create,
FileAccess.Write, FileShare.None);
#if !KeePassLibSD
GZipStream gz = new GZipStream(fs, CompressionMode.Compress);
GZipStream gz = new GZipStream(sOut, CompressionMode.Compress);
#else
GZipOutputStream gz = new GZipOutputStream(fs);
GZipOutputStream gz = new GZipOutputStream(sOut);
#endif
XmlWriterSettings xws = new XmlWriterSettings();
@ -120,27 +123,36 @@ namespace KeePassLib.Translation
xw.Close();
gz.Close();
fs.Close();
sOut.Close();
}
public static KPTranslation LoadFromFile(string strFile,
IXmlSerializerEx xs)
public static KPTranslation Load(string strFile, IXmlSerializerEx xs)
{
KPTranslation kpTrl = null;
using(FileStream fs = new FileStream(strFile, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
kpTrl = Load(fs, xs);
}
return kpTrl;
}
public static KPTranslation Load(Stream s, IXmlSerializerEx xs)
{
if(xs == null) throw new ArgumentNullException("xs");
FileStream fs = new FileStream(strFile, FileMode.Open,
FileAccess.Read, FileShare.Read);
#if !KeePassLibSD
GZipStream gz = new GZipStream(fs, CompressionMode.Decompress);
GZipStream gz = new GZipStream(s, CompressionMode.Decompress);
#else
GZipInputStream gz = new GZipInputStream(fs);
GZipInputStream gz = new GZipInputStream(s);
#endif
KPTranslation kpTrl = (xs.Deserialize(gz) as KPTranslation);
gz.Close();
fs.Close();
s.Close();
return kpTrl;
}
@ -154,9 +166,9 @@ namespace KeePassLib.Translation
return new Dictionary<string, string>();
}
#if (!KeePassLibSD && !KeePassRT)
/*public void ApplyTo(Form form)
/*
#if (!KeePassLibSD && !KeePassUAP)
public void ApplyTo(Form form)
{
if(form == null) throw new ArgumentNullException("form");
@ -185,8 +197,8 @@ namespace KeePassLib.Translation
try { RtlApplyToControls(form.Controls); }
catch(Exception) { Debug.Assert(false); }
}
}*/
/*
}
private static void RtlApplyToControls(Control.ControlCollection cc)
{
foreach(Control c in cc)
@ -212,9 +224,9 @@ namespace KeePassLib.Translation
if((c is GroupBox) || (c is Panel)) RtlMoveChildControls(c);
}
}*/
}
/*private static void RtlMoveChildControls(Control cParent)
private static void RtlMoveChildControls(Control cParent)
{
int nParentWidth = cParent.Size.Width;
@ -248,7 +260,7 @@ namespace KeePassLib.Translation
}
if(kpst != null) kpst.ApplyTo(tsic);
}*/
#endif
}
#endif*/
}
}

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,9 +19,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Text;
#if !KeePassLibSD
using System.IO.Compression;
@ -38,8 +38,7 @@ namespace KeePassLib.Utility
public static void Open(string strPrefix)
{
return; // Logging is not enabled in normal builds of KeePass!
// Logging is not enabled in normal builds of KeePass!
/*
AppLogEx.Close();

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,15 +19,15 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
#if !KeePassLibSD
using System.IO.Compression;
#else
#if KeePassLibSD
using KeePassLibSD;
#else
using System.IO.Compression;
#endif
namespace KeePassLib.Utility
@ -37,6 +37,8 @@ namespace KeePassLib.Utility
/// </summary>
public static class MemUtil
{
internal static readonly byte[] EmptyByteArray = new byte[0];
private static readonly uint[] m_vSBox = new uint[256] {
0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230,
0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4,
@ -187,75 +189,211 @@ namespace KeePassLib.Utility
return sb.ToString();
}
/// <summary>
/// Decode Base32 strings according to RFC 4648.
/// </summary>
public static byte[] ParseBase32(string str)
{
if((str == null) || ((str.Length % 8) != 0))
{
Debug.Assert(false);
return null;
}
ulong uMaxBits = (ulong)str.Length * 5UL;
List<byte> l = new List<byte>((int)(uMaxBits / 8UL) + 1);
Debug.Assert(l.Count == 0);
for(int i = 0; i < str.Length; i += 8)
{
ulong u = 0;
int nBits = 0;
for(int j = 0; j < 8; ++j)
{
char ch = str[i + j];
if(ch == '=') break;
ulong uValue;
if((ch >= 'A') && (ch <= 'Z'))
uValue = (ulong)(ch - 'A');
else if((ch >= 'a') && (ch <= 'z'))
uValue = (ulong)(ch - 'a');
else if((ch >= '2') && (ch <= '7'))
uValue = (ulong)(ch - '2') + 26UL;
else { Debug.Assert(false); return null; }
u <<= 5;
u += uValue;
nBits += 5;
}
int nBitsTooMany = (nBits % 8);
u >>= nBitsTooMany;
nBits -= nBitsTooMany;
Debug.Assert((nBits % 8) == 0);
int idxNewBytes = l.Count;
while(nBits > 0)
{
l.Add((byte)(u & 0xFF));
u >>= 8;
nBits -= 8;
}
l.Reverse(idxNewBytes, l.Count - idxNewBytes);
}
return l.ToArray();
}
/// <summary>
/// Set all bytes in a byte array to zero.
/// </summary>
/// <param name="pbArray">Input array. All bytes of this array will be set
/// to zero.</param>
/// <param name="pbArray">Input array. All bytes of this array
/// will be set to zero.</param>
#if KeePassLibSD
[MethodImpl(MethodImplOptions.NoInlining)]
#else
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
#endif
public static void ZeroByteArray(byte[] pbArray)
{
Debug.Assert(pbArray != null); if(pbArray == null) throw new ArgumentNullException("pbArray");
// for(int i = 0; i < pbArray.Length; ++i)
// pbArray[i] = 0;
Debug.Assert(pbArray != null);
if(pbArray == null) throw new ArgumentNullException("pbArray");
Array.Clear(pbArray, 0, pbArray.Length);
}
/// <summary>
/// Convert 2 bytes to a 16-bit unsigned integer using Little-Endian
/// encoding.
/// Set all elements of an array to the default value.
/// </summary>
/// <param name="v">Input array.</param>
#if KeePassLibSD
[MethodImpl(MethodImplOptions.NoInlining)]
#else
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
#endif
public static void ZeroArray<T>(T[] v)
{
if(v == null) { Debug.Assert(false); throw new ArgumentNullException("v"); }
Array.Clear(v, 0, v.Length);
}
/// <summary>
/// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
/// </summary>
/// <param name="pb">Input bytes. Array must contain at least 2 bytes.</param>
/// <returns>16-bit unsigned integer.</returns>
public static ushort BytesToUInt16(byte[] pb)
{
Debug.Assert((pb != null) && (pb.Length == 2));
if(pb == null) throw new ArgumentNullException("pb");
if(pb.Length != 2) throw new ArgumentException();
if(pb.Length != 2) throw new ArgumentOutOfRangeException("pb");
return (ushort)((ushort)pb[0] | ((ushort)pb[1] << 8));
}
/// <summary>
/// Convert 4 bytes to a 32-bit unsigned integer using Little-Endian
/// encoding.
/// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
/// </summary>
public static ushort BytesToUInt16(byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 1) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
return (ushort)((ushort)pb[iOffset] | ((ushort)pb[iOffset + 1] << 8));
}
/// <summary>
/// Convert 4 bytes to a 32-bit unsigned integer (little-endian).
/// </summary>
/// <param name="pb">Input bytes.</param>
/// <returns>32-bit unsigned integer.</returns>
public static uint BytesToUInt32(byte[] pb)
{
Debug.Assert((pb != null) && (pb.Length == 4));
if(pb == null) throw new ArgumentNullException("pb");
if(pb.Length != 4) throw new ArgumentException("Input array must contain 4 bytes!");
if(pb.Length != 4) throw new ArgumentOutOfRangeException("pb");
return (uint)pb[0] | ((uint)pb[1] << 8) | ((uint)pb[2] << 16) |
((uint)pb[3] << 24);
return ((uint)pb[0] | ((uint)pb[1] << 8) | ((uint)pb[2] << 16) |
((uint)pb[3] << 24));
}
/// <summary>
/// Convert 8 bytes to a 64-bit unsigned integer using Little-Endian
/// encoding.
/// Convert 4 bytes to a 32-bit unsigned integer (little-endian).
/// </summary>
public static uint BytesToUInt32(byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 3) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
return ((uint)pb[iOffset] | ((uint)pb[iOffset + 1] << 8) |
((uint)pb[iOffset + 2] << 16) | ((uint)pb[iOffset + 3] << 24));
}
/// <summary>
/// Convert 8 bytes to a 64-bit unsigned integer (little-endian).
/// </summary>
/// <param name="pb">Input bytes.</param>
/// <returns>64-bit unsigned integer.</returns>
public static ulong BytesToUInt64(byte[] pb)
{
Debug.Assert((pb != null) && (pb.Length == 8));
if(pb == null) throw new ArgumentNullException("pb");
if(pb.Length != 8) throw new ArgumentException();
if(pb.Length != 8) throw new ArgumentOutOfRangeException("pb");
return (ulong)pb[0] | ((ulong)pb[1] << 8) | ((ulong)pb[2] << 16) |
return ((ulong)pb[0] | ((ulong)pb[1] << 8) | ((ulong)pb[2] << 16) |
((ulong)pb[3] << 24) | ((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) |
((ulong)pb[6] << 48) | ((ulong)pb[7] << 56);
((ulong)pb[6] << 48) | ((ulong)pb[7] << 56));
}
/// <summary>
/// Convert a 16-bit unsigned integer to 2 bytes using Little-Endian
/// encoding.
/// Convert 8 bytes to a 64-bit unsigned integer (little-endian).
/// </summary>
public static ulong BytesToUInt64(byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 7) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
// if(BitConverter.IsLittleEndian)
// return BitConverter.ToUInt64(pb, iOffset);
return ((ulong)pb[iOffset] | ((ulong)pb[iOffset + 1] << 8) |
((ulong)pb[iOffset + 2] << 16) | ((ulong)pb[iOffset + 3] << 24) |
((ulong)pb[iOffset + 4] << 32) | ((ulong)pb[iOffset + 5] << 40) |
((ulong)pb[iOffset + 6] << 48) | ((ulong)pb[iOffset + 7] << 56));
}
public static int BytesToInt32(byte[] pb)
{
return (int)BytesToUInt32(pb);
}
public static int BytesToInt32(byte[] pb, int iOffset)
{
return (int)BytesToUInt32(pb, iOffset);
}
public static long BytesToInt64(byte[] pb)
{
return (long)BytesToUInt64(pb);
}
public static long BytesToInt64(byte[] pb, int iOffset)
{
return (long)BytesToUInt64(pb, iOffset);
}
/// <summary>
/// Convert a 16-bit unsigned integer to 2 bytes (little-endian).
/// </summary>
/// <param name="uValue">16-bit input word.</param>
/// <returns>Two bytes representing the 16-bit value.</returns>
public static byte[] UInt16ToBytes(ushort uValue)
{
byte[] pb = new byte[2];
@ -270,11 +408,8 @@ namespace KeePassLib.Utility
}
/// <summary>
/// Convert a 32-bit unsigned integer to 4 bytes using Little-Endian
/// encoding.
/// Convert a 32-bit unsigned integer to 4 bytes (little-endian).
/// </summary>
/// <param name="uValue">32-bit input word.</param>
/// <returns>Four bytes representing the 32-bit value.</returns>
public static byte[] UInt32ToBytes(uint uValue)
{
byte[] pb = new byte[4];
@ -291,11 +426,29 @@ namespace KeePassLib.Utility
}
/// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes using Little-Endian
/// encoding.
/// Convert a 32-bit unsigned integer to 4 bytes (little-endian).
/// </summary>
public static void UInt32ToBytesEx(uint uValue, byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 3) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
unchecked
{
pb[iOffset] = (byte)uValue;
pb[iOffset + 1] = (byte)(uValue >> 8);
pb[iOffset + 2] = (byte)(uValue >> 16);
pb[iOffset + 3] = (byte)(uValue >> 24);
}
}
/// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes (little-endian).
/// </summary>
/// <param name="uValue">64-bit input word.</param>
/// <returns>Eight bytes representing the 64-bit value.</returns>
public static byte[] UInt64ToBytes(ulong uValue)
{
byte[] pb = new byte[8];
@ -315,6 +468,61 @@ namespace KeePassLib.Utility
return pb;
}
/// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes (little-endian).
/// </summary>
public static void UInt64ToBytesEx(ulong uValue, byte[] pb, int iOffset)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 7) >= pb.Length))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset");
}
unchecked
{
pb[iOffset] = (byte)uValue;
pb[iOffset + 1] = (byte)(uValue >> 8);
pb[iOffset + 2] = (byte)(uValue >> 16);
pb[iOffset + 3] = (byte)(uValue >> 24);
pb[iOffset + 4] = (byte)(uValue >> 32);
pb[iOffset + 5] = (byte)(uValue >> 40);
pb[iOffset + 6] = (byte)(uValue >> 48);
pb[iOffset + 7] = (byte)(uValue >> 56);
}
}
public static byte[] Int32ToBytes(int iValue)
{
return UInt32ToBytes((uint)iValue);
}
public static byte[] Int64ToBytes(long lValue)
{
return UInt64ToBytes((ulong)lValue);
}
public static uint RotateLeft32(uint u, int nBits)
{
return ((u << nBits) | (u >> (32 - nBits)));
}
public static uint RotateRight32(uint u, int nBits)
{
return ((u >> nBits) | (u << (32 - nBits)));
}
public static ulong RotateLeft64(ulong u, int nBits)
{
return ((u << nBits) | (u >> (64 - nBits)));
}
public static ulong RotateRight64(ulong u, int nBits)
{
return ((u >> nBits) | (u << (64 - nBits)));
}
public static bool ArraysEqual(byte[] x, byte[] y)
{
// Return false if one of them is null (not comparable)!
@ -330,19 +538,21 @@ namespace KeePassLib.Utility
return true;
}
public static void XorArray(byte[] pbSource, int nSourceOffset,
byte[] pbBuffer, int nBufferOffset, int nLength)
public static void XorArray(byte[] pbSource, int iSourceOffset,
byte[] pbBuffer, int iBufferOffset, int cb)
{
if(pbSource == null) throw new ArgumentNullException("pbSource");
if(nSourceOffset < 0) throw new ArgumentException();
if(iSourceOffset < 0) throw new ArgumentOutOfRangeException("iSourceOffset");
if(pbBuffer == null) throw new ArgumentNullException("pbBuffer");
if(nBufferOffset < 0) throw new ArgumentException();
if(nLength < 0) throw new ArgumentException();
if((nSourceOffset + nLength) > pbSource.Length) throw new ArgumentException();
if((nBufferOffset + nLength) > pbBuffer.Length) throw new ArgumentException();
if(iBufferOffset < 0) throw new ArgumentOutOfRangeException("iBufferOffset");
if(cb < 0) throw new ArgumentOutOfRangeException("cb");
if(iSourceOffset > (pbSource.Length - cb))
throw new ArgumentOutOfRangeException("cb");
if(iBufferOffset > (pbBuffer.Length - cb))
throw new ArgumentOutOfRangeException("cb");
for(int i = 0; i < nLength; ++i)
pbBuffer[nBufferOffset + i] ^= pbSource[nSourceOffset + i];
for(int i = 0; i < cb; ++i)
pbBuffer[iBufferOffset + i] ^= pbSource[iSourceOffset + i];
}
/// <summary>
@ -421,7 +631,8 @@ namespace KeePassLib.Utility
if(s == null) { Debug.Assert(false); return; }
if(pbData == null) { Debug.Assert(false); return; }
s.Write(pbData, 0, pbData.Length);
Debug.Assert(pbData.Length >= 0);
if(pbData.Length > 0) s.Write(pbData, 0, pbData.Length);
}
public static byte[] Compress(byte[] pbData)
@ -429,15 +640,21 @@ namespace KeePassLib.Utility
if(pbData == null) throw new ArgumentNullException("pbData");
if(pbData.Length == 0) return pbData;
MemoryStream msCompressed = new MemoryStream();
GZipStream gz = new GZipStream(msCompressed, CompressionMode.Compress);
MemoryStream msSource = new MemoryStream(pbData, false);
MemUtil.CopyStream(msSource, gz);
gz.Close();
msSource.Close();
byte[] pbCompressed;
using(MemoryStream msSource = new MemoryStream(pbData, false))
{
using(MemoryStream msCompressed = new MemoryStream())
{
using(GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Compress))
{
MemUtil.CopyStream(msSource, gz);
}
pbCompressed = msCompressed.ToArray();
}
}
byte[] pbCompressed = msCompressed.ToArray();
msCompressed.Close();
return pbCompressed;
}
@ -446,15 +663,21 @@ namespace KeePassLib.Utility
if(pbCompressed == null) throw new ArgumentNullException("pbCompressed");
if(pbCompressed.Length == 0) return pbCompressed;
MemoryStream msCompressed = new MemoryStream(pbCompressed, false);
GZipStream gz = new GZipStream(msCompressed, CompressionMode.Decompress);
MemoryStream msData = new MemoryStream();
MemUtil.CopyStream(gz, msData);
gz.Close();
msCompressed.Close();
byte[] pbData;
using(MemoryStream msData = new MemoryStream())
{
using(MemoryStream msCompressed = new MemoryStream(pbCompressed, false))
{
using(GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Decompress))
{
MemUtil.CopyStream(gz, msData);
}
}
pbData = msData.ToArray();
}
byte[] pbData = msData.ToArray();
msData.Close();
return pbData;
}

View File

@ -0,0 +1,183 @@
/*
KeePass Password Safe - The Open-Source Password Manager
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using KeePassLib.Native;
namespace KeePassLib.Utility
{
public static class MonoWorkarounds
{
private static Dictionary<uint, bool> m_dForceReq = new Dictionary<uint, bool>();
private static bool? m_bReq = null;
public static bool IsRequired()
{
if(!m_bReq.HasValue) m_bReq = NativeLib.IsUnix();
return m_bReq.Value;
}
// 1219:
// Mono prepends byte order mark (BOM) to StdIn.
// https://sourceforge.net/p/keepass/bugs/1219/
// 1245:
// Key events not raised while Alt is down, and nav keys out of order.
// https://sourceforge.net/p/keepass/bugs/1245/
// 1254:
// NumericUpDown bug: text is drawn below up/down buttons.
// https://sourceforge.net/p/keepass/bugs/1254/
// 1354:
// Finalizer of NotifyIcon throws on Unity.
// https://sourceforge.net/p/keepass/bugs/1354/
// 1358:
// FileDialog crashes when ~/.recently-used is invalid.
// https://sourceforge.net/p/keepass/bugs/1358/
// 1366:
// Drawing bug when scrolling a RichTextBox.
// https://sourceforge.net/p/keepass/bugs/1366/
// 1378:
// Mono doesn't implement Microsoft.Win32.SystemEvents events.
// https://sourceforge.net/p/keepass/bugs/1378/
// https://github.com/mono/mono/blob/master/mcs/class/System/Microsoft.Win32/SystemEvents.cs
// 1418:
// Minimizing a form while loading it doesn't work.
// https://sourceforge.net/p/keepass/bugs/1418/
// 2139:
// Shortcut keys are ignored.
// https://sourceforge.net/p/keepass/feature-requests/2139/
// 2140:
// Explicit control focusing is ignored.
// https://sourceforge.net/p/keepass/feature-requests/2140/
// 5795:
// Text in input field is incomplete.
// https://bugzilla.xamarin.com/show_bug.cgi?id=5795
// https://sourceforge.net/p/keepass/discussion/329220/thread/d23dc88b/
// 10163:
// WebRequest GetResponse call missing, breaks WebDAV due to no PUT.
// https://bugzilla.xamarin.com/show_bug.cgi?id=10163
// https://sourceforge.net/p/keepass/bugs/1117/
// https://sourceforge.net/p/keepass/discussion/329221/thread/9422258c/
// https://github.com/mono/mono/commit/8e67b8c2fc7cb66bff7816ebf7c1039fb8cfc43b
// https://bugzilla.xamarin.com/show_bug.cgi?id=1512
// https://sourceforge.net/p/keepass/patches/89/
// 12525:
// PictureBox not rendered when bitmap height >= control height.
// https://bugzilla.xamarin.com/show_bug.cgi?id=12525
// https://sourceforge.net/p/keepass/discussion/329220/thread/54f61e9a/
// 586901:
// RichTextBox doesn't handle Unicode string correctly.
// https://bugzilla.novell.com/show_bug.cgi?id=586901
// 620618:
// ListView column headers not drawn.
// https://bugzilla.novell.com/show_bug.cgi?id=620618
// 649266:
// Calling Control.Hide doesn't remove the application from taskbar.
// https://bugzilla.novell.com/show_bug.cgi?id=649266
// 686017:
// Minimum sizes must be enforced.
// http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=686017
// 801414:
// Mono recreates the main window incorrectly.
// https://bugs.launchpad.net/ubuntu/+source/keepass2/+bug/801414
// 891029:
// Increase tab control height, otherwise Mono throws exceptions.
// https://sourceforge.net/projects/keepass/forums/forum/329221/topic/4519750
// https://bugs.launchpad.net/ubuntu/+source/keepass2/+bug/891029
// 836428016:
// ListView group header selection unsupported.
// https://sourceforge.net/p/keepass/discussion/329221/thread/31dae0f0/
// 3574233558:
// Problems with minimizing windows, no content rendered.
// https://sourceforge.net/p/keepass/discussion/329220/thread/d50a79d6/
public static bool IsRequired(uint uBugID)
{
if(!MonoWorkarounds.IsRequired()) return false;
bool bForce;
if(m_dForceReq.TryGetValue(uBugID, out bForce)) return bForce;
ulong v = NativeLib.MonoVersion;
if(v != 0)
{
if(uBugID == 10163)
return (v >= 0x0002000B00000000UL); // >= 2.11
}
return true;
}
internal static void SetEnabled(string strIDs, bool bEnabled)
{
if(string.IsNullOrEmpty(strIDs)) return;
string[] vIDs = strIDs.Split(new char[] { ',' });
foreach(string strID in vIDs)
{
if(string.IsNullOrEmpty(strID)) continue;
uint uID;
if(StrUtil.TryParseUInt(strID.Trim(), out uID))
m_dForceReq[uID] = bEnabled;
}
}
/// <summary>
/// Ensure that the file ~/.recently-used is valid (in order to
/// prevent Mono's FileDialog from crashing).
/// </summary>
internal static void EnsureRecentlyUsedValid()
{
if(!MonoWorkarounds.IsRequired(1358)) return;
try
{
string strFile = Environment.GetFolderPath(
Environment.SpecialFolder.Personal);
strFile = UrlUtil.EnsureTerminatingSeparator(strFile, false);
strFile += ".recently-used";
if(File.Exists(strFile))
{
try
{
// Mono's WriteRecentlyUsedFiles method also loads the
// XML file using XmlDocument
XmlDocument xd = new XmlDocument();
xd.Load(strFile);
}
catch(Exception) // The XML file is invalid
{
File.Delete(strFile);
}
}
}
catch(Exception) { Debug.Assert(false); }
}
}
}

View File

@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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
@ -22,19 +20,21 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Globalization;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
#if !KeePassUAP
using System.Drawing;
using System.Security.Cryptography;
#endif
using KeePassLib.Collections;
using KeePassLib.Cryptography.PasswordGenerator;
using KeePassLib.Native;
using KeePassLib.Security;
using KeePassLib.Resources;
namespace KeePassLib.Utility
{
@ -221,8 +221,8 @@ namespace KeePassLib.Utility
List<StrEncodingInfo> l = new List<StrEncodingInfo>();
l.Add(new StrEncodingInfo(StrEncodingType.Default,
#if KeePassRT
StrUtil.Utf8.WebName, StrUtil.Utf8, 1, null));
#if KeePassUAP
"Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
#else
#if !KeePassLibSD
Encoding.Default.EncodingName,
@ -232,12 +232,11 @@ namespace KeePassLib.Utility
Encoding.Default,
(uint)Encoding.Default.GetBytes("a").Length, null));
#endif
#if !KeePassRT
l.Add(new StrEncodingInfo(StrEncodingType.Ascii,
"ASCII", Encoding.ASCII, 1, null));
l.Add(new StrEncodingInfo(StrEncodingType.Utf7,
"Unicode (UTF-7)", Encoding.UTF7, 1, null));
#endif
l.Add(new StrEncodingInfo(StrEncodingType.Utf8,
"Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
l.Add(new StrEncodingInfo(StrEncodingType.Utf16LE,
@ -246,7 +245,8 @@ namespace KeePassLib.Utility
l.Add(new StrEncodingInfo(StrEncodingType.Utf16BE,
"Unicode (UTF-16 BE)", new UnicodeEncoding(true, false),
2, new byte[] { 0xFE, 0xFF }));
#if (!KeePassLibSD && !KeePassRT)
#if !KeePassLibSD
l.Add(new StrEncodingInfo(StrEncodingType.Utf32LE,
"Unicode (UTF-32 LE)", new UTF32Encoding(false, false),
4, new byte[] { 0xFF, 0xFE, 0x0, 0x0 }));
@ -315,7 +315,7 @@ namespace KeePassLib.Utility
str = str.Replace("\'", @"&#39;");
str = NormalizeNewLines(str, false);
str = str.Replace("\n", @"<br />");
str = str.Replace("\n", @"<br />\n" );
return str;
}
@ -358,9 +358,9 @@ namespace KeePassLib.Utility
}
/// <summary>
/// Split up a command-line into application and argument.
/// Split up a command line into application and argument.
/// </summary>
/// <param name="strCmdLine">Command-line to split.</param>
/// <param name="strCmdLine">Command line to split.</param>
/// <param name="strApp">Application path.</param>
/// <param name="strArgs">Arguments.</param>
public static void SplitCommandLine(string strCmdLine, out string strApp, out string strArgs)
@ -487,53 +487,53 @@ namespace KeePassLib.Utility
public static string FormatException(Exception excp)
{
string strText = string.Empty;
string NewLine = "\n";
if(excp.Message != null)
strText += excp.Message + MessageService.NewLine;
strText += excp.Message + NewLine;
#if !KeePassLibSD
if(excp.Source != null)
strText += excp.Source + MessageService.NewLine;
strText += excp.Source + NewLine;
#endif
if(excp.StackTrace != null)
strText += excp.StackTrace + MessageService.NewLine;
strText += excp.StackTrace + NewLine;
#if !KeePassLibSD
#if !KeePassRT
#if !KeePassUAP
if(excp.TargetSite != null)
strText += excp.TargetSite.ToString() + MessageService.NewLine;
strText += excp.TargetSite.ToString() + NewLine;
#endif
if(excp.Data != null)
{
strText += MessageService.NewLine;
strText += NewLine;
foreach(DictionaryEntry de in excp.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
MessageService.NewLine;
NewLine;
}
#endif
if(excp.InnerException != null)
{
strText += MessageService.NewLine + "Inner:" + MessageService.NewLine;
strText += NewLine + "Inner:" + NewLine;
if(excp.InnerException.Message != null)
strText += excp.InnerException.Message + MessageService.NewLine;
strText += excp.InnerException.Message + NewLine;
#if !KeePassLibSD
if(excp.InnerException.Source != null)
strText += excp.InnerException.Source + MessageService.NewLine;
strText += excp.InnerException.Source + NewLine;
#endif
if(excp.InnerException.StackTrace != null)
strText += excp.InnerException.StackTrace + MessageService.NewLine;
strText += excp.InnerException.StackTrace + NewLine;
#if !KeePassLibSD
#if !KeePassRT
#if !KeePassUAP
if(excp.InnerException.TargetSite != null)
strText += excp.InnerException.TargetSite.ToString();
#endif
if(excp.InnerException.Data != null)
{
strText += MessageService.NewLine;
strText += NewLine;
foreach(DictionaryEntry de in excp.InnerException.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
MessageService.NewLine;
NewLine;
}
#endif
}
@ -760,7 +760,7 @@ namespace KeePassLib.Utility
return sb.ToString();
}
private static Regex m_rxNaturalSplit = null;
/* private static Regex g_rxNaturalSplit = null;
public static int CompareNaturally(string strX, string strY)
{
Debug.Assert(strX != null);
@ -771,39 +771,31 @@ namespace KeePassLib.Utility
if(NativeMethods.SupportsStrCmpNaturally)
return NativeMethods.StrCmpNaturally(strX, strY);
strX = strX.ToLower(); // Case-insensitive comparison
strY = strY.ToLower();
if(g_rxNaturalSplit == null)
g_rxNaturalSplit = new Regex(@"([0-9]+)", RegexOptions.Compiled);
if(m_rxNaturalSplit == null)
m_rxNaturalSplit = new Regex(@"([0-9]+)",
#if KeePassRT
RegexOptions.None);
#else
RegexOptions.Compiled);
#endif
string[] vPartsX = g_rxNaturalSplit.Split(strX);
string[] vPartsY = g_rxNaturalSplit.Split(strY);
string[] vPartsX = m_rxNaturalSplit.Split(strX);
string[] vPartsY = m_rxNaturalSplit.Split(strY);
for(int i = 0; i < Math.Min(vPartsX.Length, vPartsY.Length); ++i)
int n = Math.Min(vPartsX.Length, vPartsY.Length);
for(int i = 0; i < n; ++i)
{
string strPartX = vPartsX[i], strPartY = vPartsY[i];
int iPartCompare;
#if KeePassLibSD
ulong uX = 0, uY = 0;
try
{
uX = ulong.Parse(strPartX);
uY = ulong.Parse(strPartY);
ulong uX = ulong.Parse(strPartX);
ulong uY = ulong.Parse(strPartY);
iPartCompare = uX.CompareTo(uY);
}
catch(Exception) { iPartCompare = strPartX.CompareTo(strPartY); }
catch(Exception) { iPartCompare = string.Compare(strPartX, strPartY, true); }
#else
ulong uX, uY;
if(ulong.TryParse(strPartX, out uX) && ulong.TryParse(strPartY, out uY))
iPartCompare = uX.CompareTo(uY);
else iPartCompare = strPartX.CompareTo(strPartY);
else iPartCompare = string.Compare(strPartX, strPartY, true);
#endif
if(iPartCompare != 0) return iPartCompare;
@ -812,6 +804,106 @@ namespace KeePassLib.Utility
if(vPartsX.Length == vPartsY.Length) return 0;
if(vPartsX.Length < vPartsY.Length) return -1;
return 1;
} */
public static int CompareNaturally(string strX, string strY)
{
Debug.Assert(strX != null);
if(strX == null) throw new ArgumentNullException("strX");
Debug.Assert(strY != null);
if(strY == null) throw new ArgumentNullException("strY");
if(NativeMethods.SupportsStrCmpNaturally)
return NativeMethods.StrCmpNaturally(strX, strY);
int cX = strX.Length;
int cY = strY.Length;
if(cX == 0) return ((cY == 0) ? 0 : -1);
if(cY == 0) return 1;
char chFirstX = strX[0];
char chFirstY = strY[0];
bool bExpNum = ((chFirstX >= '0') && (chFirstX <= '9'));
bool bExpNumY = ((chFirstY >= '0') && (chFirstY <= '9'));
if(bExpNum != bExpNumY) return string.Compare(strX, strY, true);
int pX = 0;
int pY = 0;
while((pX < cX) && (pY < cY))
{
Debug.Assert(((strX[pX] >= '0') && (strX[pX] <= '9')) == bExpNum);
Debug.Assert(((strY[pY] >= '0') && (strY[pY] <= '9')) == bExpNum);
int pExclX = pX + 1;
while(pExclX < cX)
{
char ch = strX[pExclX];
bool bChNum = ((ch >= '0') && (ch <= '9'));
if(bChNum != bExpNum) break;
++pExclX;
}
int pExclY = pY + 1;
while(pExclY < cY)
{
char ch = strY[pExclY];
bool bChNum = ((ch >= '0') && (ch <= '9'));
if(bChNum != bExpNum) break;
++pExclY;
}
string strPartX = strX.Substring(pX, pExclX - pX);
string strPartY = strY.Substring(pY, pExclY - pY);
bool bStrCmp = true;
if(bExpNum)
{
// 2^64 - 1 = 18446744073709551615 has length 20
if((strPartX.Length <= 19) && (strPartY.Length <= 19))
{
ulong uX, uY;
if(ulong.TryParse(strPartX, out uX) && ulong.TryParse(strPartY, out uY))
{
if(uX < uY) return -1;
if(uX > uY) return 1;
bStrCmp = false;
}
else { Debug.Assert(false); }
}
else
{
double dX, dY;
if(double.TryParse(strPartX, out dX) && double.TryParse(strPartY, out dY))
{
if(dX < dY) return -1;
if(dX > dY) return 1;
bStrCmp = false;
}
else { Debug.Assert(false); }
}
}
if(bStrCmp)
{
int c = string.Compare(strPartX, strPartY, true);
if(c != 0) return c;
}
bExpNum = !bExpNum;
pX = pExclX;
pY = pExclY;
}
if(pX >= cX)
{
Debug.Assert(pX == cX);
if(pY >= cY) { Debug.Assert(pY == cY); return 0; }
return -1;
}
Debug.Assert(pY == cY);
return 1;
}
public static string RemoveAccelerator(string strMenuText)
@ -835,6 +927,40 @@ namespace KeePassLib.Utility
return str;
}
public static string AddAccelerator(string strMenuText,
List<char> lAvailKeys)
{
if(strMenuText == null) { Debug.Assert(false); return null; }
if(lAvailKeys == null) { Debug.Assert(false); return strMenuText; }
int xa = -1, xs = 0;
for(int i = 0; i < strMenuText.Length; ++i)
{
char ch = strMenuText[i];
#if KeePassLibSD
char chUpper = char.ToUpper(ch);
#else
char chUpper = char.ToUpperInvariant(ch);
#endif
xa = lAvailKeys.IndexOf(chUpper);
if(xa >= 0) { xs = i; break; }
#if KeePassLibSD
char chLower = char.ToLower(ch);
#else
char chLower = char.ToLowerInvariant(ch);
#endif
xa = lAvailKeys.IndexOf(chLower);
if(xa >= 0) { xs = i; break; }
}
if(xa < 0) return strMenuText;
lAvailKeys.RemoveAt(xa);
return strMenuText.Insert(xs, @"&");
}
public static string EncodeMenuText(string strText)
{
if(strText == null) throw new ArgumentNullException("strText");
@ -852,13 +978,12 @@ namespace KeePassLib.Utility
public static bool IsHexString(string str, bool bStrict)
{
if(str == null) throw new ArgumentNullException("str");
if(str.Length == 0) return true;
foreach(char ch in str)
{
if((ch >= '0') && (ch <= '9')) continue;
if((ch >= 'a') && (ch <= 'z')) continue;
if((ch >= 'A') && (ch <= 'Z')) continue;
if((ch >= 'a') && (ch <= 'f')) continue;
if((ch >= 'A') && (ch <= 'F')) continue;
if(bStrict) return false;
@ -871,8 +996,31 @@ namespace KeePassLib.Utility
return true;
}
public static bool IsHexString(byte[] pbUtf8, bool bStrict)
{
if(pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
for(int i = 0; i < pbUtf8.Length; ++i)
{
byte bt = pbUtf8[i];
if((bt >= (byte)'0') && (bt <= (byte)'9')) continue;
if((bt >= (byte)'a') && (bt <= (byte)'f')) continue;
if((bt >= (byte)'A') && (bt <= (byte)'F')) continue;
if(bStrict) return false;
if((bt == (byte)' ') || (bt == (byte)'\t') ||
(bt == (byte)'\r') || (bt == (byte)'\n'))
continue;
return false;
}
return true;
}
#if !KeePassLibSD
private static readonly char[] m_vPatternPartsSep = new char[]{ '*' };
private static readonly char[] m_vPatternPartsSep = new char[] { '*' };
public static bool SimplePatternMatch(string strPattern, string strText,
StringComparison sc)
{
@ -997,6 +1145,36 @@ namespace KeePassLib.Utility
}
}
public static string GetNewLineSeq(string str)
{
if(str == null) { Debug.Assert(false); return "\n"; }
int n = str.Length, nLf = 0, nCr = 0, nCrLf = 0;
char chLast = char.MinValue;
for(int i = 0; i < n; ++i)
{
char ch = str[i];
if(ch == '\r') ++nCr;
else if(ch == '\n')
{
++nLf;
if(chLast == '\r') ++nCrLf;
}
chLast = ch;
}
nCr -= nCrLf;
nLf -= nCrLf;
int nMax = Math.Max(nCrLf, Math.Max(nCr, nLf));
if(nMax == 0) return "\n";
if(nCrLf == nMax) return "\r\n";
return ((nLf == nMax) ? "\n" : "\r");
}
public static string AlphaNumericOnly(string str)
{
if(string.IsNullOrEmpty(str)) return str;
@ -1074,37 +1252,44 @@ namespace KeePassLib.Utility
public static string VersionToString(ulong uVersion)
{
return VersionToString(uVersion, false);
return VersionToString(uVersion, 1U);
}
[Obsolete]
public static string VersionToString(ulong uVersion,
bool bEnsureAtLeastTwoComp)
{
string str = string.Empty;
bool bMultiComp = false;
return VersionToString(uVersion, (bEnsureAtLeastTwoComp ? 2U : 1U));
}
public static string VersionToString(ulong uVersion, uint uMinComp)
{
StringBuilder sb = new StringBuilder();
uint uComp = 0;
for(int i = 0; i < 4; ++i)
{
ushort us = (ushort)(uVersion & 0xFFFFUL);
if(uVersion == 0UL) break;
if((us != 0) || (str.Length > 0))
{
if(str.Length > 0)
{
str = "." + str;
bMultiComp = true;
}
ushort us = (ushort)(uVersion >> 48);
str = us.ToString(NumberFormatInfo.InvariantInfo) + str;
}
if(sb.Length > 0) sb.Append('.');
uVersion >>= 16;
sb.Append(us.ToString(NumberFormatInfo.InvariantInfo));
++uComp;
uVersion <<= 16;
}
if(bEnsureAtLeastTwoComp && !bMultiComp && (str.Length > 0))
str += ".0";
while(uComp < uMinComp)
{
if(sb.Length > 0) sb.Append('.');
return str;
sb.Append('0');
++uComp;
}
return sb.ToString();
}
private static readonly byte[] m_pbOptEnt = { 0xA5, 0x74, 0x2E, 0xEC };
@ -1169,7 +1354,7 @@ namespace KeePassLib.Utility
return v;
}
private static readonly char[] m_vTagSep = new char[]{ ',', ';', ':' };
private static readonly char[] m_vTagSep = new char[] { ',', ';', ':' };
public static string TagsToString(List<string> vTags, bool bForDisplay)
{
if(vTags == null) throw new ArgumentNullException("vTags");
@ -1222,7 +1407,7 @@ namespace KeePassLib.Utility
Array.Reverse(pb);
for(int i = 0; i < pb.Length; ++i) pb[i] = (byte)(pb[i] ^ 0x65);
#if (!KeePassLibSD && !KeePassRT)
#if (!KeePassLibSD && !KeePassUAP)
return Convert.ToBase64String(pb, Base64FormattingOptions.None);
#else
return Convert.ToBase64String(pb);
@ -1382,7 +1567,7 @@ namespace KeePassLib.Utility
if(strMimeType == null) strMimeType = "application/octet-stream";
#if (!KeePassLibSD && !KeePassRT)
#if (!KeePassLibSD && !KeePassUAP)
return ("data:" + strMimeType + ";base64," + Convert.ToBase64String(
pbData, Base64FormattingOptions.None));
#else
@ -1412,12 +1597,7 @@ namespace KeePassLib.Utility
if(bBase64) return Convert.FromBase64String(strData);
MemoryStream ms = new MemoryStream();
#if KeePassRT
Encoding enc = StrUtil.Utf8;
#else
Encoding enc = Encoding.ASCII;
#endif
string[] v = strData.Split('%');
byte[] pb = enc.GetBytes(v[0]);

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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,7 +18,10 @@
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using KeePassLib.Interfaces;
@ -35,9 +38,29 @@ namespace KeePassLib.Utility
/// </summary>
public const int PwTimeLength = 7;
#if !KeePassLibSD
private static string m_strDtfStd = null;
private static string m_strDtfDate = null;
#endif
/// <summary>
private static DateTime? m_odtUnixRoot = null;
public static DateTime UnixRoot
{
get
{
if(m_odtUnixRoot.HasValue) return m_odtUnixRoot.Value;
/// Pack a <c>DateTime</c> object into 5 bytes. Layout: 2 zero bits,
DateTime dtRoot = (new DateTime(1970, 1, 1, 0, 0, 0, 0,
DateTimeKind.Utc)).ToLocalTime();
/// year 12 bits, month 4 bits, day 5 bits, hour 5 bits, minute 6
m_odtUnixRoot = dtRoot;
return dtRoot;
}
}
/// bits, second 6 bits.
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
/// bits, second 6 bits.
/// </summary>
/// <param name="dt"></param>
@ -140,17 +163,118 @@ namespace KeePassLib.Utility
{
DateTime dt;
#if !KeePassLibSD
if(DateTime.TryParse(strDisplay, out dt)) return dt;
#else
#if KeePassLibSD
try { dt = DateTime.Parse(strDisplay); return dt; }
catch(Exception) { }
#else
if(DateTime.TryParse(strDisplay, out dt)) return dt;
// For some custom formats specified using the Control Panel,
// DateTime.ToString returns the correct string, but
// DateTime.TryParse fails (e.g. for "//dd/MMM/yyyy");
// https://sourceforge.net/p/keepass/discussion/329221/thread/3a225b29/?limit=25&page=1#c6ae
if((m_strDtfStd == null) || (m_strDtfDate == null))
{
DateTime dtUni = new DateTime(2111, 3, 4, 5, 6, 7);
m_strDtfStd = DeriveCustomFormat(ToDisplayString(dtUni), dtUni);
m_strDtfDate = DeriveCustomFormat(ToDisplayStringDateOnly(dtUni), dtUni);
}
const DateTimeStyles dts = DateTimeStyles.AllowWhiteSpaces;
if(DateTime.TryParseExact(strDisplay, m_strDtfStd, null, dts, out dt))
return dt;
if(DateTime.TryParseExact(strDisplay, m_strDtfDate, null, dts, out dt))
return dt;
#endif
Debug.Assert(false);
return DateTime.Now;
}
#if !KeePassLibSD
private static string DeriveCustomFormat(string strDT, DateTime dt)
{
string[] vPlh = new string[] {
// Names, sorted by length
"MMMM", "dddd",
"MMM", "ddd",
"gg", "g",
// Numbers, the ones with prefix '0' first
"yyyy", "yyy", "yy", "y",
"MM", "M",
"dd", "d",
"HH", "hh", "H", "h",
"mm", "m",
"ss", "s",
"tt", "t"
};
List<string> lValues = new List<string>();
foreach(string strPlh in vPlh)
{
string strEval = strPlh;
if(strEval.Length == 1) strEval = @"%" + strPlh; // Make custom
lValues.Add(dt.ToString(strEval));
}
StringBuilder sbAll = new StringBuilder();
sbAll.Append("dfFghHKmMstyz:/\"\'\\%");
sbAll.Append(strDT);
foreach(string strVEnum in lValues) { sbAll.Append(strVEnum); }
List<char> lCodes = new List<char>();
for(int i = 0; i < vPlh.Length; ++i)
{
char ch = StrUtil.GetUnusedChar(sbAll.ToString());
lCodes.Add(ch);
sbAll.Append(ch);
}
string str = strDT;
for(int i = 0; i < vPlh.Length; ++i)
{
string strValue = lValues[i];
if(string.IsNullOrEmpty(strValue)) continue;
str = str.Replace(strValue, new string(lCodes[i], 1));
}
StringBuilder sbFmt = new StringBuilder();
bool bInLiteral = false;
foreach(char ch in str)
{
int iCode = lCodes.IndexOf(ch);
// The escape character doesn't work correctly (e.g.
// "dd\\.MM\\.yyyy\\ HH\\:mm\\:ss" doesn't work, but
// "dd'.'MM'.'yyyy' 'HH':'mm':'ss" does); use '' instead
// if(iCode >= 0) sbFmt.Append(vPlh[iCode]);
// else // Literal
// {
// sbFmt.Append('\\');
// sbFmt.Append(ch);
// }
if(iCode >= 0)
{
if(bInLiteral) { sbFmt.Append('\''); bInLiteral = false; }
sbFmt.Append(vPlh[iCode]);
}
else // Literal
{
if(!bInLiteral) { sbFmt.Append('\''); bInLiteral = true; }
sbFmt.Append(ch);
}
}
if(bInLiteral) sbFmt.Append('\'');
return sbFmt.ToString();
}
#endif
public static string SerializeUtc(DateTime dt)
{
string str = dt.ToUniversalTime().ToString("s");

View File

@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 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
@ -19,10 +19,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Text;
using KeePassLib.Native;
@ -41,17 +40,14 @@ namespace KeePassLib.Utility
public static char LocalDirSepChar
{
#if KeePassRT
get { return '\\'; }
#else
get { return Path.DirectorySeparatorChar; }
#endif
}
/// <summary>
/// Get the directory (path) of a file name. The returned string is
/// Get the directory (path) of a file name. The returned string may be
/// terminated by a directory separator character. Example:
/// passing <c>C:\\My Documents\\My File.kdb</c> in <paramref name="strFile" />
/// and <c>true</c> to <paramref name="bAppendTerminatingChar"/>
/// would produce this string: <c>C:\\My Documents\\</c>.
/// </summary>
/// <param name="strFile">Full path of a file.</param>
@ -62,8 +58,7 @@ namespace KeePassLib.Utility
/// of <c>X:</c>, overriding <paramref name="bAppendTerminatingChar" />).
/// This should only be set to <c>true</c>, if the returned path is directly
/// passed to some directory API.</param>
/// <returns>Directory of the file. The return value is an empty string
/// (<c>""</c>) if the input parameter is <c>null</c>.</returns>
/// <returns>Directory of the file.</returns>
public static string GetFileDirectory(string strFile, bool bAppendTerminatingChar,
bool bEnsureValidDirSpec)
{
@ -71,14 +66,15 @@ namespace KeePassLib.Utility
if(strFile == null) throw new ArgumentNullException("strFile");
int nLastSep = strFile.LastIndexOfAny(m_vDirSeps);
if(nLastSep < 0) return strFile; // None
if(nLastSep < 0) return string.Empty; // No directory
if(bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
(strFile[2] == '\\')) // Length >= 3 and Windows root directory
bAppendTerminatingChar = true;
if(!bAppendTerminatingChar) return strFile.Substring(0, nLastSep);
return EnsureTerminatingSeparator(strFile.Substring(0, nLastSep), false);
return EnsureTerminatingSeparator(strFile.Substring(0, nLastSep),
(strFile[nLastSep] == '/'));
}
/// <summary>
@ -317,10 +313,11 @@ namespace KeePassLib.Utility
return strTargetFile;
}
#if (!KeePassLibSD && !KeePassRT)
#if (!KeePassLibSD && !KeePassUAP)
if(NativeLib.IsUnix())
#endif
{
bool bBaseUnc = IsUncPath(strBaseFile);
bool bTargetUnc = IsUncPath(strTargetFile);
if((!bBaseUnc && bTargetUnc) || (bBaseUnc && !bTargetUnc))
@ -348,9 +345,9 @@ namespace KeePassLib.Utility
}
return sbRel.ToString();
#if (!KeePassLibSD && !KeePassUAP)
}
#if (!KeePassLibSD && !KeePassRT)
try // Windows
{
const int nMaxPath = NativeMethods.MAX_PATH * 2;
@ -624,7 +621,7 @@ namespace KeePassLib.Utility
string strDir;
if(NativeLib.IsUnix())
strDir = NativeMethods.GetUserRuntimeDir();
#if KeePassRT
#if KeePassUAP
else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
#else
else strDir = Path.GetTempPath();
@ -632,8 +629,7 @@ namespace KeePassLib.Utility
try
{
if(Directory.Exists(strDir) == false)
Directory.CreateDirectory(strDir);
if(!Directory.Exists(strDir)) Directory.CreateDirectory(strDir);
}
catch(Exception) { Debug.Assert(false); }