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 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 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 it under the terms of the GNU General Public License as published by
@ -194,13 +194,6 @@ namespace KeePassLib.Collections
return true; 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) public AutoTypeAssociation GetAt(int iIndex)
{ {
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count)) if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
@ -209,6 +202,22 @@ namespace KeePassLib.Collections
return m_lWindowAssocs[iIndex]; 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) public void RemoveAt(int iIndex)
{ {
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count)) if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
@ -216,5 +225,20 @@ namespace KeePassLib.Collections
m_lWindowAssocs.RemoveAt(iIndex); 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 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 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 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.Interfaces;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Utility;
#if KeePassLibSD #if KeePassLibSD
using KeePassLibSD; using KeePassLibSD;

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -284,11 +284,7 @@ namespace KeePassLib.Collections
public List<string> GetKeys() public List<string> GetKeys()
{ {
List<string> v = new List<string>(); return new List<string>(m_vStrings.Keys);
foreach(string strKey in m_vStrings.Keys) v.Add(strKey);
return v;
} }
public void EnableProtection(string strField, bool bProtect) public void EnableProtection(string strField, bool bProtect)
@ -300,7 +296,8 @@ namespace KeePassLib.Collections
{ {
byte[] pbData = ps.ReadUtf8(); byte[] pbData = ps.ReadUtf8();
Set(strField, new ProtectedString(bProtect, pbData)); 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 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 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 it under the terms of the GNU General Public License as published by
@ -233,7 +233,7 @@ namespace KeePassLib.Collections
if(nCount <= 1) return; if(nCount <= 1) return;
int nIndex = m_vObjects.IndexOf(tObject); 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 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> /// <summary>
/// Move some of the objects in this list to the top/bottom. /// Move some of the objects in this list to the top/bottom.
/// </summary> /// </summary>

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -18,6 +18,7 @@
*/ */
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Diagnostics; using System.Diagnostics;
@ -77,4 +78,154 @@ namespace KeePassLib.Collections
return true; 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 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 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 it under the terms of the GNU General Public License as published by
@ -32,14 +32,14 @@ using KeePassLibSD;
namespace KeePassLib.Collections namespace KeePassLib.Collections
{ {
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>, 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>(); new SortedDictionary<string, string>();
public int Count public int Count
{ {
get { return m_vDict.Count; } get { return m_dict.Count; }
} }
public StringDictionaryEx() public StringDictionaryEx()
@ -48,39 +48,53 @@ namespace KeePassLib.Collections
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return m_vDict.GetEnumerator(); return m_dict.GetEnumerator();
} }
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{ {
return m_vDict.GetEnumerator(); return m_dict.GetEnumerator();
} }
public StringDictionaryEx CloneDeep() public StringDictionaryEx CloneDeep()
{ {
StringDictionaryEx plNew = new StringDictionaryEx(); StringDictionaryEx sdNew = new StringDictionaryEx();
foreach(KeyValuePair<string, string> kvpStr in m_vDict) foreach(KeyValuePair<string, string> kvp in m_dict)
plNew.Set(kvpStr.Key, kvpStr.Value); 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) 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; string s;
if(m_vDict.TryGetValue(strName, out s)) return s; if(m_dict.TryGetValue(strName, out s)) return s;
return null; return null;
} }
public bool Exists(string strName) 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> /// <summary>
@ -92,10 +106,10 @@ namespace KeePassLib.Collections
/// parameters is <c>null</c>.</exception> /// parameters is <c>null</c>.</exception>
public void Set(string strField, string strNewValue) public void Set(string strField, string strNewValue)
{ {
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField"); if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
Debug.Assert(strNewValue != null); if(strNewValue == null) throw new ArgumentNullException("strNewValue"); if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
m_vDict[strField] = strNewValue; m_dict[strField] = strNewValue;
} }
/// <summary> /// <summary>
@ -108,9 +122,9 @@ namespace KeePassLib.Collections
/// parameter is <c>null</c>.</exception> /// parameter is <c>null</c>.</exception>
public bool Remove(string strField) 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 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 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 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.Text;
using System.IO; using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.Security;
namespace KeePassLib.Cryptography.Cipher namespace KeePassLib.Cryptography.Cipher
{ {
@ -41,12 +40,17 @@ namespace KeePassLib.Cryptography.Cipher
{ {
get 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 = cp;
m_poolGlobal.AddCipher(new StandardAesEngine()); }
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 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 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 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> /// <returns>Stream, from which the decrypted data can be read.</returns>
Stream DecryptStream(Stream sEncrypted, byte[] pbKey, byte[] pbIV); 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 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 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 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. // Implementation of the Salsa20 cipher, based on the eSTREAM submission.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using KeePassLib.Utility; using KeePassLib.Utility;
namespace KeePassLib.Cryptography.Cipher 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 uint[] m_x = new uint[16]; // Working buffer
private byte[] m_output = new byte[64]; private static readonly uint[] g_sigma = new uint[4] {
private int m_outputPos = 64;
private static readonly uint[] m_sigma = new uint[4]{
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574 0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
}; };
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) public override int BlockSize
{ {
KeySetup(pbKey32); get { return 64; }
IvSetup(pbIV8);
} }
~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 // Clear sensitive data
Array.Clear(m_state, 0, m_state.Length); // Key setup
Array.Clear(m_x, 0, m_x.Length); 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 // 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(); 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 unchecked
{ {
// 10 * 8 quarter rounds = 20 rounds
for(int i = 0; i < 10; ++i) // (int i = 20; i > 0; i -= 2) for(int i = 0; i < 10; ++i) // (int i = 20; i > 0; i -= 2)
{ {
x[ 4] ^= Rotl32(x[ 0] + x[12], 7); x[ 4] ^= MemUtil.RotateLeft32(x[ 0] + x[12], 7);
x[ 8] ^= Rotl32(x[ 4] + x[ 0], 9); x[ 8] ^= MemUtil.RotateLeft32(x[ 4] + x[ 0], 9);
x[12] ^= Rotl32(x[ 8] + x[ 4], 13); x[12] ^= MemUtil.RotateLeft32(x[ 8] + x[ 4], 13);
x[ 0] ^= Rotl32(x[12] + x[ 8], 18); x[ 0] ^= MemUtil.RotateLeft32(x[12] + x[ 8], 18);
x[ 9] ^= Rotl32(x[ 5] + x[ 1], 7);
x[13] ^= Rotl32(x[ 9] + x[ 5], 9); x[ 9] ^= MemUtil.RotateLeft32(x[ 5] + x[ 1], 7);
x[ 1] ^= Rotl32(x[13] + x[ 9], 13); x[13] ^= MemUtil.RotateLeft32(x[ 9] + x[ 5], 9);
x[ 5] ^= Rotl32(x[ 1] + x[13], 18); x[ 1] ^= MemUtil.RotateLeft32(x[13] + x[ 9], 13);
x[14] ^= Rotl32(x[10] + x[ 6], 7); x[ 5] ^= MemUtil.RotateLeft32(x[ 1] + x[13], 18);
x[ 2] ^= Rotl32(x[14] + x[10], 9);
x[ 6] ^= Rotl32(x[ 2] + x[14], 13); x[14] ^= MemUtil.RotateLeft32(x[10] + x[ 6], 7);
x[10] ^= Rotl32(x[ 6] + x[ 2], 18); x[ 2] ^= MemUtil.RotateLeft32(x[14] + x[10], 9);
x[ 3] ^= Rotl32(x[15] + x[11], 7); x[ 6] ^= MemUtil.RotateLeft32(x[ 2] + x[14], 13);
x[ 7] ^= Rotl32(x[ 3] + x[15], 9); x[10] ^= MemUtil.RotateLeft32(x[ 6] + x[ 2], 18);
x[11] ^= Rotl32(x[ 7] + x[ 3], 13);
x[15] ^= Rotl32(x[11] + x[ 7], 18); x[ 3] ^= MemUtil.RotateLeft32(x[15] + x[11], 7);
x[ 1] ^= Rotl32(x[ 0] + x[ 3], 7); x[ 7] ^= MemUtil.RotateLeft32(x[ 3] + x[15], 9);
x[ 2] ^= Rotl32(x[ 1] + x[ 0], 9); x[11] ^= MemUtil.RotateLeft32(x[ 7] + x[ 3], 13);
x[ 3] ^= Rotl32(x[ 2] + x[ 1], 13); x[15] ^= MemUtil.RotateLeft32(x[11] + x[ 7], 18);
x[ 0] ^= Rotl32(x[ 3] + x[ 2], 18);
x[ 6] ^= Rotl32(x[ 5] + x[ 4], 7); x[ 1] ^= MemUtil.RotateLeft32(x[ 0] + x[ 3], 7);
x[ 7] ^= Rotl32(x[ 6] + x[ 5], 9); x[ 2] ^= MemUtil.RotateLeft32(x[ 1] + x[ 0], 9);
x[ 4] ^= Rotl32(x[ 7] + x[ 6], 13); x[ 3] ^= MemUtil.RotateLeft32(x[ 2] + x[ 1], 13);
x[ 5] ^= Rotl32(x[ 4] + x[ 7], 18); x[ 0] ^= MemUtil.RotateLeft32(x[ 3] + x[ 2], 18);
x[11] ^= Rotl32(x[10] + x[ 9], 7);
x[ 8] ^= Rotl32(x[11] + x[10], 9); x[ 6] ^= MemUtil.RotateLeft32(x[ 5] + x[ 4], 7);
x[ 9] ^= Rotl32(x[ 8] + x[11], 13); x[ 7] ^= MemUtil.RotateLeft32(x[ 6] + x[ 5], 9);
x[10] ^= Rotl32(x[ 9] + x[ 8], 18); x[ 4] ^= MemUtil.RotateLeft32(x[ 7] + x[ 6], 13);
x[12] ^= Rotl32(x[15] + x[14], 7); x[ 5] ^= MemUtil.RotateLeft32(x[ 4] + x[ 7], 18);
x[13] ^= Rotl32(x[12] + x[15], 9);
x[14] ^= Rotl32(x[13] + x[12], 13); x[11] ^= MemUtil.RotateLeft32(x[10] + x[ 9], 7);
x[15] ^= Rotl32(x[14] + x[13], 18); 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) 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) pBlock[i4] = (byte)xi;
{ pBlock[i4 + 1] = (byte)(xi >> 8);
m_output[i << 2] = (byte)x[i]; pBlock[i4 + 2] = (byte)(xi >> 16);
m_output[(i << 2) + 1] = (byte)(x[i] >> 8); pBlock[i4 + 3] = (byte)(xi >> 24);
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];
}
} }
private static uint Rotl32(uint x, int b) ++s[8];
{ if(s[8] == 0) ++s[9];
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;
} }
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 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.Security;
using System.Diagnostics; using System.Diagnostics;
#if !KeePassRT #if !KeePassUAP
using System.Security.Cryptography; 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 #endif
using KeePassLib.Resources; using KeePassLib.Resources;
@ -44,12 +37,12 @@ namespace KeePassLib.Cryptography.Cipher
/// </summary> /// </summary>
public sealed class StandardAesEngine : ICipherEngine public sealed class StandardAesEngine : ICipherEngine
{ {
#if !KeePassRT #if !KeePassUAP
private const CipherMode m_rCipherMode = CipherMode.CBC; private const CipherMode m_rCipherMode = CipherMode.CBC;
private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7; private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7;
#endif #endif
private static PwUuid m_uuidAes = null; private static PwUuid g_uuidAes = null;
/// <summary> /// <summary>
/// UUID of the cipher engine. This ID uniquely identifies the /// UUID of the cipher engine. This ID uniquely identifies the
@ -59,14 +52,16 @@ namespace KeePassLib.Cryptography.Cipher
{ {
get 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, 0x31, 0xC1, 0xF2, 0xE6, 0xBF, 0x71, 0x43, 0x50,
0xBE, 0x58, 0x05, 0x21, 0x6A, 0xFC, 0x5A, 0xFF }); 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> /// <summary>
/// Get a displayable name describing this cipher engine. /// Get a displayable name describing this cipher engine.
/// </summary> /// </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) private static void ValidateArguments(Stream stream, bool bEncrypt, byte[] pbKey, byte[] pbIV)
{ {
@ -98,12 +100,12 @@ namespace KeePassLib.Cryptography.Cipher
if(bEncrypt) if(bEncrypt)
{ {
Debug.Assert(stream.CanWrite); 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 else // Decrypt
{ {
Debug.Assert(stream.CanRead); 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]; byte[] pbLocalKey = new byte[32];
Array.Copy(pbKey, pbLocalKey, 32); Array.Copy(pbKey, pbLocalKey, 32);
#if !KeePassRT #if KeePassUAP
return StandardAesEngineExt.CreateStream(s, bEncrypt, pbLocalKey, pbLocalIV);
#else
RijndaelManaged r = new RijndaelManaged(); RijndaelManaged r = new RijndaelManaged();
if(r.BlockSize != 128) // AES block size if(r.BlockSize != 128) // AES block size
{ {
@ -137,18 +141,6 @@ namespace KeePassLib.Cryptography.Cipher
return new CryptoStream(s, iTransform, bEncrypt ? CryptoStreamMode.Write : return new CryptoStream(s, iTransform, bEncrypt ? CryptoStreamMode.Write :
CryptoStreamMode.Read); 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 #endif
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -20,11 +20,13 @@
*/ */
using System; using System;
using System.Security;
using System.Security.Cryptography;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
#if !KeePassUAP
using System.Drawing; using System.Drawing;
using System.Security.Cryptography;
#endif
using KeePassLib.Native; using KeePassLib.Native;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -39,21 +41,30 @@ namespace KeePassLib.Cryptography
public sealed class CryptoRandom public sealed class CryptoRandom
{ {
private byte[] m_pbEntropyPool = new byte[64]; private byte[] m_pbEntropyPool = new byte[64];
private uint m_uCounter; private ulong m_uCounter;
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider(); private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
private ulong m_uGeneratedBytesCount = 0; private ulong m_uGeneratedBytesCount = 0;
private static object g_oSyncRoot = new object();
private object m_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 public static CryptoRandom Instance
{ {
get 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 cr;
return m_pInstance;
} }
} }
@ -80,10 +91,12 @@ namespace KeePassLib.Cryptography
private CryptoRandom() private CryptoRandom()
{ {
Random r = new Random(); Random rWeak = new Random();
m_uCounter = (uint)r.Next(); byte[] pb = new byte[8];
rWeak.NextBytes(pb);
m_uCounter = MemUtil.BytesToUInt64(pb);
AddEntropy(GetSystemData(r)); AddEntropy(GetSystemData(rWeak));
AddEntropy(GetCspData()); AddEntropy(GetCspData());
} }
@ -99,32 +112,40 @@ namespace KeePassLib.Cryptography
if(pbEntropy.Length == 0) { Debug.Assert(false); return; } if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
byte[] pbNewData = pbEntropy; byte[] pbNewData = pbEntropy;
if(pbEntropy.Length >= 64) if(pbEntropy.Length > 64)
{ {
#if !KeePassLibSD #if KeePassLibSD
SHA512Managed shaNew = new SHA512Managed(); using(SHA256Managed shaNew = new SHA256Managed())
#else #else
SHA256Managed shaNew = new SHA256Managed(); using(SHA512Managed shaNew = new SHA512Managed())
#endif #endif
{
pbNewData = shaNew.ComputeHash(pbEntropy); pbNewData = shaNew.ComputeHash(pbEntropy);
} }
}
MemoryStream ms = new MemoryStream();
lock(m_oSyncRoot) lock(m_oSyncRoot)
{ {
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length); int cbPool = m_pbEntropyPool.Length;
ms.Write(pbNewData, 0, pbNewData.Length); int cbNew = pbNewData.Length;
byte[] pbFinal = ms.ToArray(); byte[] pbCmp = new byte[cbPool + cbNew];
#if !KeePassLibSD Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
Debug.Assert(pbFinal.Length == (64 + pbNewData.Length)); Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew);
SHA512Managed shaPool = new SHA512Managed();
MemUtil.ZeroByteArray(m_pbEntropyPool);
#if KeePassLibSD
using(SHA256Managed shaPool = new SHA256Managed())
#else #else
SHA256Managed shaPool = new SHA256Managed(); using(SHA512Managed shaPool = new SHA512Managed())
#endif #endif
m_pbEntropyPool = shaPool.ComputeHash(pbFinal); {
m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
} }
ms.Close();
MemUtil.ZeroByteArray(pbCmp);
}
} }
private static byte[] GetSystemData(Random rWeak) private static byte[] GetSystemData(Random rWeak)
@ -132,71 +153,104 @@ namespace KeePassLib.Cryptography
MemoryStream ms = new MemoryStream(); MemoryStream ms = new MemoryStream();
byte[] pb; byte[] pb;
pb = MemUtil.UInt32ToBytes((uint)Environment.TickCount); pb = MemUtil.Int32ToBytes(Environment.TickCount);
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
pb = TimeUtil.PackTime(DateTime.Now); pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
#if !KeePassLibSD
pb = MemUtil.UInt32ToBytes((uint)rWeak.Next()); /*Not supported on Android
ms.Write(pb, 0, pb.Length); // In try-catch for systems without GUI;
// https://sourceforge.net/p/keepass/discussion/329221/thread/20335b73/
pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
ms.Write(pb, 0, pb.Length);
#if (!KeePassLibSD && !KeePassRT)
try try
{ {
pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount); Point pt = Cursor.Position;
ms.Write(pb, 0, pb.Length); pb = MemUtil.Int32ToBytes(pt.X);
pb = MemUtil.UInt64ToBytes((ulong)Environment.WorkingSet); MemUtil.Write(ms, pb);
ms.Write(pb, 0, pb.Length); pb = MemUtil.Int32ToBytes(pt.Y);
MemUtil.Write(ms, pb);
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);
} }
catch(Exception) { } 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 #endif
pb = Guid.NewGuid().ToByteArray(); pb = Guid.NewGuid().ToByteArray();
ms.Write(pb, 0, pb.Length); MemUtil.Write(ms, pb);
byte[] pbAll = ms.ToArray(); byte[] pbAll = ms.ToArray();
ms.Close(); ms.Close();
@ -215,28 +269,31 @@ namespace KeePassLib.Cryptography
if(this.GenerateRandom256Pre != null) if(this.GenerateRandom256Pre != null)
this.GenerateRandom256Pre(this, EventArgs.Empty); this.GenerateRandom256Pre(this, EventArgs.Empty);
byte[] pbFinal; byte[] pbCmp;
lock(m_oSyncRoot) lock(m_oSyncRoot)
{ {
unchecked { m_uCounter += 386047; } // Prime number m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
byte[] pbCounter = MemUtil.UInt32ToBytes(m_uCounter); byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
byte[] pbCspRandom = GetCspData(); byte[] pbCspRandom = GetCspData();
MemoryStream ms = new MemoryStream(); int cbPool = m_pbEntropyPool.Length;
ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length); int cbCtr = pbCounter.Length;
ms.Write(pbCounter, 0, pbCounter.Length); int cbCsp = pbCspRandom.Length;
ms.Write(pbCspRandom, 0, pbCspRandom.Length);
pbFinal = ms.ToArray(); pbCmp = new byte[cbPool + cbCtr + cbCsp];
Debug.Assert(pbFinal.Length == (m_pbEntropyPool.Length + Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
pbCounter.Length + pbCspRandom.Length)); Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
ms.Close(); Array.Copy(pbCspRandom, 0, pbCmp, cbPool + cbCtr, cbCsp);
MemUtil.ZeroByteArray(pbCspRandom);
m_uGeneratedBytesCount += 32; m_uGeneratedBytesCount += 32;
} }
SHA256Managed sha256 = new SHA256Managed(); byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
return sha256.ComputeHash(pbFinal); MemUtil.ZeroByteArray(pbCmp);
return pbRet;
} }
/// <summary> /// <summary>
@ -248,29 +305,32 @@ namespace KeePassLib.Cryptography
/// random bytes.</returns> /// random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedBytes) 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]; int cbRem = (int)uRequestedBytes;
long lPos = 0; byte[] pbRes = new byte[cbRem];
int iPos = 0;
while(uRequestedBytes != 0) while(cbRem != 0)
{ {
byte[] pbRandom256 = GenerateRandom256(); byte[] pbRandom256 = GenerateRandom256();
Debug.Assert(pbRandom256.Length == 32); 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) MemUtil.ZeroByteArray(pbRandom256);
Array.Copy(pbRandom256, 0, pbRes, lPos, lCopy);
#else
Array.Copy(pbRandom256, 0, pbRes, (int)lPos, (int)lCopy);
#endif
lPos += lCopy; iPos += cbCopy;
uRequestedBytes -= (uint)lCopy; cbRem -= cbCopy;
} }
Debug.Assert((int)lPos == pbRes.Length); Debug.Assert(iPos == pbRes.Length);
return pbRes; return pbRes;
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -19,9 +19,13 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
#if !KeePassUAP
using System.Security.Cryptography; using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography.Cipher; using KeePassLib.Cryptography.Cipher;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography namespace KeePassLib.Cryptography
{ {
@ -38,6 +42,7 @@ namespace KeePassLib.Cryptography
/// <summary> /// <summary>
/// A variant of the ARCFour algorithm (RC4 incompatible). /// A variant of the ARCFour algorithm (RC4 incompatible).
/// </summary> /// </summary>
/// </summary>
ArcFourVariant = 1, ArcFourVariant = 1,
/// <summary> /// <summary>
@ -45,7 +50,12 @@ namespace KeePassLib.Cryptography
/// </summary> /// </summary>
Salsa20 = 2, Salsa20 = 2,
Count = 3 /// <summary>
/// ChaCha20 stream cipher algorithm.
/// </summary>
ChaCha20 = 3,
Count = 4
} }
/// <summary> /// <summary>
@ -56,13 +66,14 @@ namespace KeePassLib.Cryptography
/// </summary> /// </summary>
public sealed class CryptoRandomStream public sealed class CryptoRandomStream
{ {
private CrsAlgorithm m_crsAlgorithm; private readonly CrsAlgorithm m_crsAlgorithm;
private byte[] m_pbState = null; private byte[] m_pbState = null;
private byte m_i = 0; private byte m_i = 0;
private byte m_j = 0; private byte m_j = 0;
private Salsa20Cipher m_salsa20 = null; private Salsa20Cipher m_salsa20 = null;
private ChaCha20Cipher m_chacha20 = null;
/// <summary> /// <summary>
/// Construct a new cryptographically secure random stream object. /// Construct a new cryptographically secure random stream object.
@ -70,31 +81,53 @@ namespace KeePassLib.Cryptography
/// <param name="genAlgorithm">Algorithm to use.</param> /// <param name="genAlgorithm">Algorithm to use.</param>
/// <param name="pbKey">Initialization key. Must not be <c>null</c> and /// <param name="pbKey">Initialization key. Must not be <c>null</c> and
/// must contain at least 1 byte.</param> /// must contain at least 1 byte.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
/// <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)
{ {
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"); m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8);
}
uint uKeyLen = (uint)pbKey.Length; else if(a == CrsAlgorithm.ArcFourVariant)
Debug.Assert(uKeyLen != 0); if(uKeyLen == 0) throw new ArgumentException();
if(genAlgorithm == CrsAlgorithm.ArcFourVariant)
{ {
// Fill the state linearly // Fill the state linearly
m_pbState = new byte[256]; 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 unchecked
{ {
byte j = 0, t; byte j = 0, t;
uint inxKey = 0; int inxKey = 0;
for(uint w = 0; w < 256; ++w) // Key setup for(int w = 0; w < 256; ++w) // Key setup
{ {
j += (byte)(m_pbState[w] + pbKey[inxKey]); j += (byte)(m_pbState[w] + pbKey[inxKey]);
@ -103,25 +136,16 @@ namespace KeePassLib.Cryptography
m_pbState[j] = t; m_pbState[j] = t;
++inxKey; ++inxKey;
if(inxKey >= uKeyLen) inxKey = 0; if(inxKey >= cbKey) inxKey = 0;
} }
} }
GetRandomBytes(512); // Increases security, see cryptanalysis 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 else // Unknown algorithm
{ {
Debug.Assert(false); 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> /// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedCount) 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 unchecked
{ {
for(uint w = 0; w < uRequestedCount; ++w) for(int w = 0; w < cb; ++w)
{ {
++m_i; ++m_i;
m_j += m_pbState[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); } else { Debug.Assert(false); }
return pbRet; return pbRet;
@ -164,15 +194,8 @@ namespace KeePassLib.Cryptography
public ulong GetRandomUInt64() public ulong GetRandomUInt64()
{ {
byte[] pb = GetRandomBytes(8); byte[] pb = GetRandomBytes(8);
return MemUtil.BytesToUInt64(pb);
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);
} }
}
#if CRSBENCHMARK #if CRSBENCHMARK
public static string Benchmark() 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> </Reference>
</ItemGroup> </ItemGroup>
<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="IDatabaseFormat.cs" />
<Compile Include="Kp2aLog.cs" /> <Compile Include="Kp2aLog.cs" />
<Compile Include="Resources\Resource.designer.cs" /> <Compile Include="Resources\Resource.designer.cs" />
@ -87,7 +99,6 @@
<Compile Include="Cryptography\PasswordGenerator\PwProfile.cs" /> <Compile Include="Cryptography\PasswordGenerator\PwProfile.cs" />
<Compile Include="Cryptography\PopularPasswords.cs" /> <Compile Include="Cryptography\PopularPasswords.cs" />
<Compile Include="Cryptography\QualityEstimation.cs" /> <Compile Include="Cryptography\QualityEstimation.cs" />
<Compile Include="Cryptography\SelfTest.cs" />
<Compile Include="Cryptography\PasswordGenerator\PwGenerator.cs" /> <Compile Include="Cryptography\PasswordGenerator\PwGenerator.cs" />
<Compile Include="PwCustomIcon.cs" /> <Compile Include="PwCustomIcon.cs" />
<Compile Include="PwDatabase.cs" /> <Compile Include="PwDatabase.cs" />
@ -124,9 +135,13 @@
<Compile Include="Serialization\FileLock.cs" /> <Compile Include="Serialization\FileLock.cs" />
<Compile Include="Serialization\FileTransactionEx.cs" /> <Compile Include="Serialization\FileTransactionEx.cs" />
<Compile Include="Serialization\HashedBlockStream.cs" /> <Compile Include="Serialization\HashedBlockStream.cs" />
<Compile Include="Serialization\HmacBlockStream.cs" />
<Compile Include="Serialization\IOConnection.cs"> <Compile Include="Serialization\IOConnection.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </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.cs" />
<Compile Include="Serialization\KdbxFile.Read.cs" /> <Compile Include="Serialization\KdbxFile.Read.cs" />
<Compile Include="Serialization\KdbxFile.Read.Streamed.cs" /> <Compile Include="Serialization\KdbxFile.Read.Streamed.cs" />
@ -134,8 +149,6 @@
<Compile Include="Serialization\IOConnectionInfo.cs" /> <Compile Include="Serialization\IOConnectionInfo.cs" />
<Compile Include="Serialization\OldFormatException.cs" /> <Compile Include="Serialization\OldFormatException.cs" />
<Compile Include="Serialization\ProtoBuf\KdbpFile.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\KPStringTable.cs" />
<Compile Include="Translation\KPStringTableItem.cs" /> <Compile Include="Translation\KPStringTableItem.cs" />
<Compile Include="Translation\KPTranslation.cs" /> <Compile Include="Translation\KPTranslation.cs" />
@ -144,6 +157,7 @@
<Compile Include="Utility\GfxUtil.cs" /> <Compile Include="Utility\GfxUtil.cs" />
<Compile Include="Utility\MemUtil.cs" /> <Compile Include="Utility\MemUtil.cs" />
<Compile Include="Utility\MessageService.cs" /> <Compile Include="Utility\MessageService.cs" />
<Compile Include="Utility\MonoWorkarounds.cs" />
<Compile Include="Utility\StrUtil.cs" /> <Compile Include="Utility\StrUtil.cs" />
<Compile Include="Utility\UrlUtil.cs" /> <Compile Include="Utility\UrlUtil.cs" />
<Compile Include="Utility\TimeUtil.cs" /> <Compile Include="Utility\TimeUtil.cs" />

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -18,22 +18,16 @@
*/ */
using System; using System;
using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.Text;
using System.Security.Cryptography;
#if KeePassRT
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
#endif
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Native; using KeePassLib.Native;
using KeePassLib.Resources; using KeePassLib.Resources;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Utility; using KeePassLib.Utility;
using keepass2android;
namespace KeePassLib.Keys namespace KeePassLib.Keys
{ {
@ -109,7 +103,6 @@ namespace KeePassLib.Keys
return m_vUserKeys.Remove(pKey); return m_vUserKeys.Remove(pKey);
} }
#if !KeePassRT
/// <summary> /// <summary>
/// Test whether the composite key contains a specific type of /// Test whether the composite key contains a specific type of
/// user keys (password, key file, ...). If at least one user /// user keys (password, key file, ...). If at least one user
@ -125,8 +118,15 @@ namespace KeePassLib.Keys
foreach(IUserKey pKey in m_vUserKeys) 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)) if(tUserKeyType.IsInstanceOfType(pKey))
return true; return true;
#endif
} }
return false; return false;
@ -145,8 +145,15 @@ namespace KeePassLib.Keys
foreach(IUserKey pKey in m_vUserKeys) 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)) if(tUserKeyType.IsInstanceOfType(pKey))
return pKey; return pKey;
#endif
} }
return null; return null;
@ -156,8 +163,6 @@ namespace KeePassLib.Keys
{ {
return (T) GetUserKey(typeof (T)); return (T) GetUserKey(typeof (T));
} }
#endif
/// <summary> /// <summary>
/// Creates the composite key from the supplied user key sources (password, /// Creates the composite key from the supplied user key sources (password,
/// key file, user account, computer ID, etc.). /// key file, user account, computer ID, etc.).
@ -167,21 +172,31 @@ namespace KeePassLib.Keys
ValidateUserKeys(); ValidateUserKeys();
// Concatenate user key data // Concatenate user key data
MemoryStream ms = new MemoryStream(); List<byte[]> lData = new List<byte[]>();
int cbData = 0;
foreach(IUserKey pKey in m_vUserKeys) foreach(IUserKey pKey in m_vUserKeys)
{ {
ProtectedBinary b = pKey.KeyData; ProtectedBinary b = pKey.KeyData;
if(b != null) if(b != null)
{ {
byte[] pbKeyData = b.ReadData(); byte[] pbKeyData = b.ReadData();
ms.Write(pbKeyData, 0, pbKeyData.Length); lData.Add(pbKeyData);
MemUtil.ZeroByteArray(pbKeyData); cbData += pbKeyData.Length;
} }
} }
SHA256Managed sha256 = new SHA256Managed(); byte[] pbAllData = new byte[cbData];
byte[] pbHash = sha256.ComputeHash(ms.ToArray()); int p = 0;
ms.Close(); 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; return pbHash;
} }
@ -192,21 +207,13 @@ namespace KeePassLib.Keys
byte[] pbThis = CreateRawCompositeKey32(); byte[] pbThis = CreateRawCompositeKey32();
byte[] pbOther = ckOther.CreateRawCompositeKey32(); byte[] pbOther = ckOther.CreateRawCompositeKey32();
bool bResult = MemUtil.ArraysEqual(pbThis, pbOther); bool bResult = MemUtil.ArraysEqual(pbThis, pbOther);
Array.Clear(pbOther, 0, pbOther.Length); MemUtil.ZeroByteArray(pbOther);
Array.Clear(pbThis, 0, pbThis.Length); MemUtil.ZeroByteArray(pbThis);
return bResult; return bResult;
} }
/// <summary> [Obsolete]
/// 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>
public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds) public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds)
{ {
Debug.Assert(pbKeySeed32 != null); Debug.Assert(pbKeySeed32 != null);
@ -214,18 +221,43 @@ namespace KeePassLib.Keys
Debug.Assert(pbKeySeed32.Length == 32); Debug.Assert(pbKeySeed32.Length == 32);
if(pbKeySeed32.Length != 32) throw new ArgumentException("pbKeySeed32"); 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(); byte[] pbRaw32 = CreateRawCompositeKey32();
if((pbRaw32 == null) || (pbRaw32.Length != 32)) if((pbRaw32 == null) || (pbRaw32.Length != 32))
{ Debug.Assert(false); return null; } { Debug.Assert(false); return null; }
byte[] pbTrf32 = TransformKey(pbRaw32, pbKeySeed32, uNumRounds); KdfEngine kdf = KdfPool.Get(p.KdfUuid);
if((pbTrf32 == null) || (pbTrf32.Length != 32)) if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
{ Debug.Assert(false); return null; } 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); ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
MemUtil.ZeroByteArray(pbTrf32); MemUtil.ZeroByteArray(pbTrf32);
MemUtil.ZeroByteArray(pbRaw32); MemUtil.ZeroByteArray(pbRaw32);
return pbRet; return pbRet;
} }
@ -245,192 +277,6 @@ namespace KeePassLib.Keys
throw new InvalidOperationException(); 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 public sealed class InvalidCompositeKeyException : Exception

View File

@ -1,8 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -20,12 +18,15 @@
*/ */
using System; using System;
using System.Text;
using System.IO;
using System.Xml;
using System.Security;
using System.Security.Cryptography;
using System.Diagnostics; 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.Cryptography;
using KeePassLib.Resources; using KeePassLib.Resources;
@ -103,12 +104,12 @@ namespace KeePassLib.Keys
if (pbFileData == null) throw new Java.IO.FileNotFoundException(); if (pbFileData == null) throw new Java.IO.FileNotFoundException();
m_pbFileData = new ProtectedBinary(true, pbFileData); 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 uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4)); uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
if (((uSig1 == KdbxFile.FileSignature1) && if(((uSig1 == KdbxFile.FileSignature1) &&
(uSig2 == KdbxFile.FileSignature2)) || (uSig2 == KdbxFile.FileSignature2)) ||
((uSig1 == KdbxFile.FileSignaturePreRelease1) && ((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
(uSig2 == KdbxFile.FileSignaturePreRelease2)) || (uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
@ -122,9 +123,9 @@ namespace KeePassLib.Keys
} }
byte[] pbKey = LoadXmlKeyFile(pbFileData); 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_ioc = iocKeyFile;
m_pbKeyData = new ProtectedBinary(true, pbKey); m_pbKeyData = new ProtectedBinary(true, pbKey);
@ -137,13 +138,13 @@ namespace KeePassLib.Keys
byte[] pbFileData = IOConnection.ReadFile(iocFile); byte[] pbFileData = IOConnection.ReadFile(iocFile);
Construct(pbFileData, iocFile, bThrowIfDbFile); Construct(pbFileData, iocFile, bThrowIfDbFile);
} }
// public void Clear() // public void Clear()
// { // {
// m_strPath = string.Empty; // m_strPath = string.Empty;
// m_pbKeyData = null; // m_pbKeyData = null;
// } // }
private static byte[] LoadKeyFile(byte[] pbFileData) private static byte[] LoadKeyFile(byte[] pbFileData)
{ {
if(pbFileData == null) { Debug.Assert(false); return null; } if(pbFileData == null) { Debug.Assert(false); return null; }
@ -155,10 +156,7 @@ namespace KeePassLib.Keys
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData); else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
if(pbKey == null) if(pbKey == null)
{ pbKey = CryptoUtil.HashSha256(pbFileData);
SHA256Managed sha256 = new SHA256Managed();
pbKey = sha256.ComputeHash(pbFileData);
}
return pbKey; return pbKey;
} }
@ -178,12 +176,15 @@ namespace KeePassLib.Keys
try try
{ {
string strHex = StrUtil.Utf8.GetString(pbFileData, 0, 64); if(!StrUtil.IsHexString(pbFileData, true)) return null;
if(!StrUtil.IsHexString(strHex, true)) return null;
string strHex = StrUtil.Utf8.GetString(pbFileData);
byte[] pbKey = MemUtil.HexStringToByteArray(strHex); byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
if((pbKey == null) || (pbKey.Length != 32)) if((pbKey == null) || (pbKey.Length != 32))
{
Debug.Assert(false);
return null; return null;
}
return pbKey; return pbKey;
} }
@ -211,13 +212,13 @@ namespace KeePassLib.Keys
pbFinalKey32 = pbKey32; pbFinalKey32 = pbKey32;
else else
{ {
MemoryStream ms = new MemoryStream(); using(MemoryStream ms = new MemoryStream())
ms.Write(pbAdditionalEntropy, 0, pbAdditionalEntropy.Length); {
ms.Write(pbKey32, 0, 32); MemUtil.Write(ms, pbAdditionalEntropy);
MemUtil.Write(ms, pbKey32);
SHA256Managed sha256 = new SHA256Managed(); pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
pbFinalKey32 = sha256.ComputeHash(ms.ToArray()); }
ms.Close();
} }
CreateXmlKeyFile(strFilePath, pbFinalKey32); CreateXmlKeyFile(strFilePath, pbFinalKey32);
@ -292,7 +293,15 @@ namespace KeePassLib.Keys
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
Stream sOut = IOConnection.OpenWrite(ioc); 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); XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8);
#endif
xtw.WriteStartDocument(); xtw.WriteStartDocument();
xtw.WriteWhitespace("\r\n"); xtw.WriteWhitespace("\r\n");
@ -334,6 +343,6 @@ namespace KeePassLib.Keys
public void ResetIoc(IOConnectionInfo newIoc) public void ResetIoc(IOConnectionInfo newIoc)
{ {
m_ioc = newIoc; m_ioc = newIoc;
}
} }
} }
}

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -18,10 +18,10 @@
*/ */
using System; using System;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.Security.Cryptography; using System.Text;
using KeePassLib.Cryptography;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -68,8 +68,11 @@ namespace KeePassLib.Keys
Debug.Assert(pbPasswordUtf8 != null); Debug.Assert(pbPasswordUtf8 != null);
if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8"); if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8");
SHA256Managed sha256 = new SHA256Managed(); #if (DEBUG && !KeePassLibSD)
byte[] pbRaw = sha256.ComputeHash(pbPasswordUtf8); Debug.Assert(ValidatePassword(pbPasswordUtf8));
#endif
byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
m_psPassword = new ProtectedString(true, pbPasswordUtf8); m_psPassword = new ProtectedString(true, pbPasswordUtf8);
m_pbKeyData = new ProtectedBinary(true, pbRaw); m_pbKeyData = new ProtectedBinary(true, pbRaw);
@ -80,5 +83,19 @@ namespace KeePassLib.Keys
// m_psPassword = null; // m_psPassword = null;
// m_pbKeyData = 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 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 Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -20,9 +20,13 @@
*/ */
using System; using System;
using System.Security; using System.Diagnostics;
using System.Security.Cryptography;
using System.IO; using System.IO;
using System.Security;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Resources; using KeePassLib.Resources;

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,12 +19,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics; using System.Diagnostics;
using Android.Util; using System.Reflection;
using KeePassLib.Utility; using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using Java.Text;
using keepass2android; using keepass2android;
#if !KeePassUAP
using System.IO;
using System.Threading;
#endif
using KeePassLib.Utility;
namespace KeePassLib.Native namespace KeePassLib.Native
{ {
@ -48,6 +53,61 @@ namespace KeePassLib.Native
set { m_bAllowNative = value; } 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> /// <summary>
/// Determine if the native library is installed. /// Determine if the native library is installed.
/// </summary> /// </summary>
@ -91,15 +151,15 @@ namespace KeePassLib.Native
{ {
if(m_platID.HasValue) return m_platID.Value; if(m_platID.HasValue) return m_platID.Value;
#if KeePassRT #if KeePassUAP
m_platID = PlatformID.Win32NT; m_platID = EnvironmentExt.OSVersion.Platform;
#else #else
m_platID = Environment.OSVersion.Platform; m_platID = Environment.OSVersion.Platform;
#endif #endif
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassUAP)
/*// Mono returns PlatformID.Unix on Mac OS X, workaround this /*// Mono returns PlatformID.Unix on Mac OS X, workaround this
//fails on Anroid //not supported on Mono
if(m_platID.Value == PlatformID.Unix) if(m_platID.Value == PlatformID.Unix)
{ {
if((RunConsoleApp("uname", null) ?? string.Empty).Trim().Equals( if((RunConsoleApp("uname", null) ?? string.Empty).Trim().Equals(
@ -111,7 +171,111 @@ namespace KeePassLib.Native
return m_platID.Value; 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> /// <summary>
/// Transform a key. /// Transform a key.
/// </summary> /// </summary>
@ -122,7 +286,7 @@ namespace KeePassLib.Native
public static bool TransformKey256(byte[] pBuf256, byte[] pKey256, public static bool TransformKey256(byte[] pBuf256, byte[] pKey256,
ulong uRounds) ulong uRounds)
{ {
if(m_bAllowNative == false) return false; if (m_bAllowNative == false) return false;
try try
{ {
@ -136,7 +300,7 @@ namespace KeePassLib.Native
return false; return false;
#endif #endif
} }
catch(Exception e) catch (Exception e)
{ {
Kp2aLog.Log(e.Message); Kp2aLog.Log(e.Message);
return false; return false;
@ -148,19 +312,23 @@ namespace KeePassLib.Native
/// <summary> /// <summary>
/// Benchmark key transformation. /// Benchmark key transformation.
/// </summary> /// </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> /// <param name="puRounds">Number of transformations done.</param>
/// <returns>Returns <c>true</c>, if the benchmark was successful.</returns> /// <returns>Returns <c>true</c>, if the benchmark was successful.</returns>
public static bool TransformKeyBenchmark256(uint uTimeMs, out ulong puRounds) public static bool TransformKeyBenchmark256(uint uTimeMs, out ulong puRounds)
{ {
puRounds = 0; puRounds = 0;
if(m_bAllowNative == false) return false; #if KeePassUAP
return false;
#else
if(!m_bAllowNative) return false;
try { puRounds = NativeMethods.TransformKeyBenchmark(uTimeMs); } try { puRounds = NativeMethods.TransformKeyBenchmark(uTimeMs); }
catch(Exception) { return false; } catch(Exception) { return false; }
return true; return true;
#endif
} }
private static KeyValuePair<IntPtr, IntPtr> PrepareArrays256(byte[] pBuf256, 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 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 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 it under the terms of the GNU General Public License as published by
@ -18,20 +18,22 @@
*/ */
using System; using System;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using KeePassLib.Utility; using KeePassLib.Utility;
namespace KeePassLib.Native namespace KeePassLib.Native
{ {
internal static class NativeMethods internal static partial class NativeMethods
{ {
internal const int MAX_PATH = 260; 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")] /* [DllImport("KeePassNtv32.dll", EntryPoint = "TransformKey")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey32(IntPtr pBuf256, private static extern bool TransformKey32(IntPtr pBuf256,
@ -70,6 +72,7 @@ namespace KeePassLib.Native
return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds); return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds);
} */ } */
#if !KeePassUAP
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKey256")] [DllImport("KeePassLibC32.dll", EntryPoint = "TransformKey256")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TransformKey32(IntPtr pBuf256, private static extern bool TransformKey32(IntPtr pBuf256,
@ -83,7 +86,7 @@ namespace KeePassLib.Native
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256, internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds) UInt64 uRounds)
{ {
if(Marshal.SizeOf(typeof(IntPtr)) == 8) if(NativeLib.PointerSize == 8)
return TransformKey64(pBuf256, pKey256, uRounds); return TransformKey64(pBuf256, pKey256, uRounds);
else else
return TransformKey32(pBuf256, pKey256, uRounds); return TransformKey32(pBuf256, pKey256, uRounds);
@ -97,69 +100,87 @@ namespace KeePassLib.Native
internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs) internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs)
{ {
if(Marshal.SizeOf(typeof(IntPtr)) == 8) if(NativeLib.PointerSize == 8)
return TransformKeyBenchmark64(uTimeMs); return TransformKeyBenchmark64(uTimeMs);
else return TransformKeyBenchmark32(uTimeMs);
return TransformKeyBenchmark32(uTimeMs);
} }
#endif
#if (!KeePassLibSD && !KeePassRT) /* [DllImport("KeePassLibC32.dll", EntryPoint = "TF_ShowLangBar")]
[DllImport("ShlWApi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] [return: MarshalAs(UnmanagedType.Bool)]
internal static extern int StrCmpLogicalW(string x, string y); 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)] [DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool PathRelativePathTo([Out] StringBuilder pszPath, internal static extern bool PathRelativePathTo([Out] StringBuilder pszPath,
[In] string pszFrom, [In] uint dwAttrFrom, [In] string pszTo, [In] string pszFrom, uint dwAttrFrom, [In] string pszTo, uint dwAttrTo);
[In] uint dwAttrTo);
#endif
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() private static void TestNaturalComparisonsSupport()
{ {
#if (KeePassLibSD || KeePassRT)
#warning No native natural comparisons supported.
m_bSupportsLogicalCmp = false;
#else
try try
{ {
StrCmpLogicalW("0", "0"); // Throws exception if unsupported StrCmpLogicalW("0", "0"); // Throws exception if unsupported
m_bSupportsLogicalCmp = true; m_obSupportsLogicalCmp = true;
} }
catch(Exception) { m_bSupportsLogicalCmp = false; } catch(Exception) { m_obSupportsLogicalCmp = false; }
#endif
} }
#endif
internal static bool SupportsStrCmpNaturally internal static bool SupportsStrCmpNaturally
{ {
get get
{ {
if(m_bSupportsLogicalCmp.HasValue == false) #if (!KeePassLibSD && !KeePassUAP)
if(!m_obSupportsLogicalCmp.HasValue)
TestNaturalComparisonsSupport(); TestNaturalComparisonsSupport();
return m_bSupportsLogicalCmp.Value; return m_obSupportsLogicalCmp.Value;
#else
return false;
#endif
} }
} }
internal static int StrCmpNaturally(string x, string y) internal static int StrCmpNaturally(string x, string y)
{ {
if(m_bSupportsLogicalCmp.HasValue == false) TestNaturalComparisonsSupport(); #if (!KeePassLibSD && !KeePassUAP)
if(m_bSupportsLogicalCmp.Value == false) return 0; 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); return StrCmpLogicalW(x, y);
#else
Debug.Assert(false);
return string.Compare(x, y, true);
#endif #endif
} }
internal static string GetUserRuntimeDir() internal static string GetUserRuntimeDir()
{ {
#if !KeePassLibSD #if KeePassLibSD
#if KeePassRT return Path.GetTempPath();
string strRtDir = Windows.Storage.ApplicationData.Current.LocalFolder.Path; #else
#if KeePassUAP
string strRtDir = EnvironmentExt.AppDataLocalFolderPath;
#else #else
string strRtDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR"); string strRtDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
if(string.IsNullOrEmpty(strRtDir)) if(string.IsNullOrEmpty(strRtDir))
@ -175,8 +196,6 @@ namespace KeePassLib.Native
strRtDir += PwDefs.ShortProductName; strRtDir += PwDefs.ShortProductName;
return strRtDir; return strRtDir;
#else
return Path.GetTempPath();
#endif #endif
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -27,7 +27,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Dominik Reichl")] [assembly: AssemblyCompany("Dominik Reichl")]
[assembly: AssemblyProduct("KeePassLib")] [assembly: AssemblyProduct("KeePassLib")]
[assembly: AssemblyCopyright("Copyright © 2003-2013 Dominik Reichl")] [assembly: AssemblyCopyright("Copyright © 2003-2016 Dominik Reichl")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@ -38,5 +38,5 @@ using System.Runtime.InteropServices;
[assembly: Guid("395f6eec-a1e0-4438-aa82-b75099348134")] [assembly: Guid("395f6eec-a1e0-4438-aa82-b75099348134")]
// Assembly version information // Assembly version information
[assembly: AssemblyVersion("2.24.0.*")] [assembly: AssemblyVersion("2.34.0.*")]
[assembly: AssemblyFileVersion("2.24.0.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 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 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 it under the terms of the GNU General Public License as published by
@ -19,9 +19,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml.Serialization;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Xml.Serialization;
using KeePassLib.Delegates; using KeePassLib.Delegates;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
@ -55,20 +55,20 @@ namespace KeePassLib
/// e.g. 2.19 = 0x02130000. /// e.g. 2.19 = 0x02130000.
/// It is highly recommended to use <c>FileVersion64</c> instead. /// It is highly recommended to use <c>FileVersion64</c> instead.
/// </summary> /// </summary>
public const uint Version32 = 0x02180000; public const uint Version32 = 0x02220000;
/// <summary> /// <summary>
/// Version, encoded as 64-bit unsigned integer /// Version, encoded as 64-bit unsigned integer
/// (component-wise, 16 bits per component). /// (component-wise, 16 bits per component).
/// </summary> /// </summary>
public const ulong FileVersion64 = 0x0002001800000000UL; public const ulong FileVersion64 = 0x0002002200000000UL;
/// <summary> /// <summary>
/// Version, encoded as string. /// Version, encoded as string.
/// </summary> /// </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> /// <summary>
/// Product website URL. Terminated by a forward slash. /// Product website URL. Terminated by a forward slash.
@ -94,7 +94,8 @@ namespace KeePassLib
/// URL to a TXT file (eventually compressed) that contains information /// URL to a TXT file (eventually compressed) that contains information
/// about the latest KeePass version available on the website. /// about the latest KeePass version available on the website.
/// </summary> /// </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> /// <summary>
/// URL to the root path of the online KeePass help. Terminated by /// 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> /// <summary>
/// Search parameters for group and entry searches. /// Search parameters for group and entry searches.
/// </summary> /// </summary>
@ -318,7 +319,7 @@ namespace KeePassLib
set { m_bSearchInTags = value; } set { m_bSearchInTags = value; }
} }
#if KeePassRT #if KeePassUAP
private StringComparison m_scType = StringComparison.OrdinalIgnoreCase; private StringComparison m_scType = StringComparison.OrdinalIgnoreCase;
#else #else
private StringComparison m_scType = StringComparison.InvariantCultureIgnoreCase; private StringComparison m_scType = StringComparison.InvariantCultureIgnoreCase;
@ -410,9 +411,9 @@ namespace KeePassLib
return (SearchParameters)this.MemberwiseClone(); 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> /// <summary>
/// Memory protection configuration structure (for default fields). /// Memory protection configuration structure (for default fields).
/// </summary> /// </summary>
@ -442,7 +443,7 @@ namespace KeePassLib
return false; return false;
} }
} }
#pragma warning restore 1591 // Missing XML comments warning // #pragma warning restore 1591 // Missing XML comments warning
public sealed class ObjectTouchedEventArgs : EventArgs public sealed class ObjectTouchedEventArgs : EventArgs
{ {

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 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 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 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 it under the terms of the GNU General Public License as published by
@ -20,8 +20,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Xml;
#if !KeePassUAP
using System.Drawing; using System.Drawing;
#endif
using KeePassLib.Collections; using KeePassLib.Collections;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
@ -70,9 +72,11 @@ namespace KeePassLib
private List<string> m_vTags = new List<string>(); private List<string> m_vTags = new List<string>();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary> /// <summary>
/// UUID of this entry. /// UUID of this entry.
/// </summary> /// </summary>
/// </summary>
public PwUuid Uuid public PwUuid Uuid
{ {
get { return m_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 static EventHandler<ObjectTouchedEventArgs> EntryTouched;
public EventHandler<ObjectTouchedEventArgs> Touched; public EventHandler<ObjectTouchedEventArgs> Touched;
@ -352,12 +373,20 @@ namespace KeePassLib
} }
} }
#if DEBUG
/// <summary> /// <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 /// Clone the current entry. The returned entry is an exact value copy
/// of the current entry (including UUID and parent group reference). /// of the current entry (including UUID and parent group reference).
/// All mutable members are cloned. /// All mutable members are cloned.
/// </summary> /// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns> /// <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() public PwEntry CloneDeep()
{ {
PwEntry peNew = new PwEntry(false, false); PwEntry peNew = new PwEntry(false, false);
@ -394,6 +423,8 @@ namespace KeePassLib
peNew.m_vTags = new List<string>(m_vTags); peNew.m_vTags = new List<string>(m_vTags);
peNew.m_dCustomData = m_dCustomData.CloneDeep();
return peNew; return peNew;
} }
@ -515,6 +546,8 @@ namespace KeePassLib
if(m_vTags[iTag] != pe.m_vTags[iTag]) return false; if(m_vTags[iTag] != pe.m_vTags[iTag]) return false;
} }
if(!m_dCustomData.Equals(pe.m_dCustomData)) return false;
return true; return true;
} }
@ -531,7 +564,7 @@ namespace KeePassLib
public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer, public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer,
bool bIncludeHistory, bool bAssignLocationChanged) 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; if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.LastModificationTime,LastModificationTime,true) < 0)) return;
@ -542,10 +575,11 @@ namespace KeePassLib
if(bAssignLocationChanged) if(bAssignLocationChanged)
m_tParentGroupLastMod = peTemplate.LocationChanged; m_tParentGroupLastMod = peTemplate.LocationChanged;
m_listStrings = peTemplate.m_listStrings; m_listStrings = peTemplate.m_listStrings.CloneDeep();
m_listBinaries = peTemplate.m_listBinaries; m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
m_listAutoType = peTemplate.m_listAutoType; m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
if(bIncludeHistory) m_listHistory = peTemplate.m_listHistory; if(bIncludeHistory)
m_listHistory = peTemplate.m_listHistory.CloneDeep();
m_pwIcon = peTemplate.m_pwIcon; m_pwIcon = peTemplate.m_pwIcon;
m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
@ -563,6 +597,8 @@ namespace KeePassLib
m_strOverrideUrl = peTemplate.m_strOverrideUrl; m_strOverrideUrl = peTemplate.m_strOverrideUrl;
m_vTags = new List<string>(peTemplate.m_vTags); m_vTags = new List<string>(peTemplate.m_vTags);
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
} }
/// <summary> /// <summary>
@ -814,6 +850,9 @@ namespace KeePassLib
foreach(string strTag in m_vTags) foreach(string strTag in m_vTags)
uSize += (ulong)strTag.Length; uSize += (ulong)strTag.Length;
foreach(KeyValuePair<string, string> kvp in m_dCustomData)
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return uSize; return uSize;
} }
@ -893,6 +932,25 @@ namespace KeePassLib
} }
return dateTime; 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> public sealed class PwEntryComparer : IComparer<PwEntry>
@ -918,12 +976,7 @@ namespace KeePassLib
if(m_bCompareNaturally) return StrUtil.CompareNaturally(strA, strB); 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); return string.Compare(strA, strB, m_bCaseInsensitive);
#endif
} }
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -163,6 +163,26 @@ namespace KeePassLib
Manual = 2 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> /// <summary>
/// Comparison modes for in-memory protected objects. /// Comparison modes for in-memory protected objects.
/// </summary> /// </summary>
@ -204,6 +224,9 @@ namespace KeePassLib
IgnoreHistory = 0x10, IgnoreHistory = 0x10,
IgnoreLastBackup = 0x20, IgnoreLastBackup = 0x20,
// For groups:
PropertiesOnly = 0x40,
IgnoreTimes = (IgnoreLastAccess | IgnoreLastMod) IgnoreTimes = (IgnoreLastAccess | IgnoreLastMod)
} }
@ -254,14 +277,43 @@ namespace KeePassLib
GetStdOutput = 1, GetStdOutput = 1,
WaitForExit = 2, 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/ // 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, GCKeepAlive = 4,
// https://sourceforge.net/p/keepass/patches/85/ // https://sourceforge.net/p/keepass/patches/85/
DoEvents = 8, DoEvents = 8,
DisableForms = 16 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 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 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 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; } if(other == null) { Debug.Assert(false); return false; }
// Shortcut
if (Object.ReferenceEquals(this, other))
{
return true;
}
for(int i = 0; i < (int)UuidSize; ++i) for(int i = 0; i < (int)UuidSize; ++i)
{ {
if(m_pbUuid[i] != other.m_pbUuid[i]) return false; 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"); if(dictNew == null) throw new ArgumentNullException("dictNew");
m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed); 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_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
m_strExpect100Continue = TryGetEx(dictNew, "Expect100Continue", m_strExpect100Continue);
m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError); m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError);
m_strFatalErrorText = TryGetEx(dictNew, "FatalErrorText", m_strFatalErrorText); m_strFatalErrorText = TryGetEx(dictNew, "FatalErrorText", m_strFatalErrorText);
m_strFileCorrupted = TryGetEx(dictNew, "FileCorrupted", m_strFileCorrupted); m_strFileCorrupted = TryGetEx(dictNew, "FileCorrupted", m_strFileCorrupted);
m_strFileHeaderEndEarly = TryGetEx(dictNew, "FileHeaderEndEarly", m_strFileHeaderEndEarly); m_strFileHeaderEndEarly = TryGetEx(dictNew, "FileHeaderEndEarly", m_strFileHeaderEndEarly);
m_strFileLoadFailed = TryGetEx(dictNew, "FileLoadFailed", m_strFileLoadFailed); m_strFileLoadFailed = TryGetEx(dictNew, "FileLoadFailed", m_strFileLoadFailed);
m_strFileLockedWrite = TryGetEx(dictNew, "FileLockedWrite", m_strFileLockedWrite); m_strFileLockedWrite = TryGetEx(dictNew, "FileLockedWrite", m_strFileLockedWrite);
m_strFileNewVerOrPlgReq = TryGetEx(dictNew, "FileNewVerOrPlgReq", m_strFileNewVerOrPlgReq);
m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq); m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq);
m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning); m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning);
m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed); m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
@ -44,28 +46,37 @@ namespace KeePassLib.Resources
m_strFileVersionUnsupported = TryGetEx(dictNew, "FileVersionUnsupported", m_strFileVersionUnsupported); m_strFileVersionUnsupported = TryGetEx(dictNew, "FileVersionUnsupported", m_strFileVersionUnsupported);
m_strFinalKeyCreationFailed = TryGetEx(dictNew, "FinalKeyCreationFailed", m_strFinalKeyCreationFailed); m_strFinalKeyCreationFailed = TryGetEx(dictNew, "FinalKeyCreationFailed", m_strFinalKeyCreationFailed);
m_strFrameworkNotImplExcp = TryGetEx(dictNew, "FrameworkNotImplExcp", m_strFrameworkNotImplExcp); m_strFrameworkNotImplExcp = TryGetEx(dictNew, "FrameworkNotImplExcp", m_strFrameworkNotImplExcp);
m_strGeneral = TryGetEx(dictNew, "General", m_strGeneral);
m_strInvalidCompositeKey = TryGetEx(dictNew, "InvalidCompositeKey", m_strInvalidCompositeKey); m_strInvalidCompositeKey = TryGetEx(dictNew, "InvalidCompositeKey", m_strInvalidCompositeKey);
m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint); m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint);
m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding); m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding);
m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint); m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel); m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid); m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat); 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_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId); m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError); m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
m_strUserAgent = TryGetEx(dictNew, "UserAgent", m_strUserAgent);
} }
private static readonly string[] m_vKeyNames = { private static readonly string[] m_vKeyNames = {
"CryptoStreamFailed", "CryptoStreamFailed",
"EncAlgorithmAes", "EncDataTooLarge",
"ErrorInClipboard", "ErrorInClipboard",
"Expect100Continue",
"FatalError", "FatalError",
"FatalErrorText", "FatalErrorText",
"FileCorrupted", "FileCorrupted",
"FileHeaderEndEarly", "FileHeaderEndEarly",
"FileLoadFailed", "FileLoadFailed",
"FileLockedWrite", "FileLockedWrite",
"FileNewVerOrPlgReq",
"FileNewVerReq", "FileNewVerReq",
"FileSaveCorruptionWarning", "FileSaveCorruptionWarning",
"FileSaveFailed", "FileSaveFailed",
@ -75,16 +86,23 @@ namespace KeePassLib.Resources
"FileVersionUnsupported", "FileVersionUnsupported",
"FinalKeyCreationFailed", "FinalKeyCreationFailed",
"FrameworkNotImplExcp", "FrameworkNotImplExcp",
"General",
"InvalidCompositeKey", "InvalidCompositeKey",
"InvalidCompositeKeyHint", "InvalidCompositeKeyHint",
"InvalidDataWhileDecoding", "InvalidDataWhileDecoding",
"KeePass1xHint", "KeePass1xHint",
"KeyBits",
"KeyFileDbSel", "KeyFileDbSel",
"MasterSeedLengthInvalid", "MasterSeedLengthInvalid",
"OldFormat", "OldFormat",
"Passive",
"PreAuth",
"Timeout",
"TryAgainSecs", "TryAgainSecs",
"UnknownHeaderId", "UnknownHeaderId",
"UserAccountKeyError" "UnknownKdf",
"UserAccountKeyError",
"UserAgent"
}; };
public static string[] GetKeyNames() public static string[] GetKeyNames()
@ -103,15 +121,15 @@ namespace KeePassLib.Resources
get { return m_strCryptoStreamFailed; } get { return m_strCryptoStreamFailed; }
} }
private static string m_strEncAlgorithmAes = private static string m_strEncDataTooLarge =
@"AES/Rijndael (256-Bit Key)"; @"The data is too large to be encrypted/decrypted securely using {PARAM}.";
/// <summary> /// <summary>
/// Look up a localized string similar to /// 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> /// </summary>
public static string EncAlgorithmAes public static string EncDataTooLarge
{ {
get { return m_strEncAlgorithmAes; } get { return m_strEncDataTooLarge; }
} }
private static string m_strErrorInClipboard = private static string m_strErrorInClipboard =
@ -125,6 +143,17 @@ namespace KeePassLib.Resources
get { return m_strErrorInClipboard; } 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 = private static string m_strFatalError =
@"Fatal Error"; @"Fatal Error";
/// <summary> /// <summary>
@ -191,6 +220,17 @@ namespace KeePassLib.Resources
get { return m_strFileLockedWrite; } 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 = private static string m_strFileNewVerReq =
@"A newer KeePass version is required to open this file."; @"A newer KeePass version is required to open this file.";
/// <summary> /// <summary>
@ -290,6 +330,17 @@ namespace KeePassLib.Resources
get { return m_strFrameworkNotImplExcp; } 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 = private static string m_strInvalidCompositeKey =
@"The composite key is invalid!"; @"The composite key is invalid!";
/// <summary> /// <summary>
@ -334,6 +385,17 @@ namespace KeePassLib.Resources
get { return m_strKeePass1xHint; } 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 = private static string m_strKeyFileDbSel =
@"Database files cannot be used as key files."; @"Database files cannot be used as key files.";
/// <summary> /// <summary>
@ -367,6 +429,39 @@ namespace KeePassLib.Resources
get { return m_strOldFormat; } 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 = private static string m_strTryAgainSecs =
@"Please try it again in a few seconds."; @"Please try it again in a few seconds.";
/// <summary> /// <summary>
@ -389,6 +484,17 @@ namespace KeePassLib.Resources
get { return m_strUnknownHeaderId; } 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 = 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."; @"The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored.";
/// <summary> /// <summary>
@ -399,5 +505,16 @@ namespace KeePassLib.Resources
{ {
get { return m_strUserAccountKeyError; } 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 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -20,10 +18,16 @@
*/ */
using System; using System;
using System.Security.Cryptography;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Native;
using KeePassLib.Utility; using KeePassLib.Utility;
#if KeePassLibSD #if KeePassLibSD
@ -32,6 +36,17 @@ using KeePassLibSD;
namespace KeePassLib.Security namespace KeePassLib.Security
{ {
[Flags]
public enum PbCryptFlags
{
None = 0,
Encrypt = 1,
Decrypt = 2
}
public delegate void PbCryptDelegate(byte[] pbData, PbCryptFlags cf,
long lID);
/// <summary> /// <summary>
/// Represents a protected binary, i.e. a byte array that is encrypted /// Represents a protected binary, i.e. a byte array that is encrypted
/// in memory. A <c>ProtectedBinary</c> object is immutable and /// in memory. A <c>ProtectedBinary</c> object is immutable and
@ -39,26 +54,97 @@ namespace KeePassLib.Security
/// </summary> /// </summary>
public sealed class ProtectedBinary : IEquatable<ProtectedBinary> 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 private static PbCryptDelegate g_fExtCrypt = null;
// higher. /// <summary>
private static bool m_bProtectionSupported; /// 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 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 // 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 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 object m_objSync = new object();
private static byte[] g_pbKey32 = null;
/// <summary> /// <summary>
/// A flag specifying whether the <c>ProtectedBinary</c> object has /// A flag specifying whether the <c>ProtectedBinary</c> object has
/// turned on in-memory protection or not. /// turned on memory protection or not.
/// </summary> /// </summary>
public bool IsProtected public bool IsProtected
{ {
@ -73,19 +159,13 @@ namespace KeePassLib.Security
get { return m_uDataLen; } get { return m_uDataLen; }
} }
static ProtectedBinary()
{
//protection not supported on Android currently
m_bProtectionSupported = false;
}
/// <summary> /// <summary>
/// Construct a new, empty protected binary data object. Protection /// Construct a new, empty protected binary data object.
/// is disabled. /// Protection is disabled.
/// </summary> /// </summary>
public ProtectedBinary() public ProtectedBinary()
{ {
Init(false, new byte[0]); Init(false, MemUtil.EmptyByteArray);
} }
/// <summary> /// <summary>
@ -110,34 +190,99 @@ namespace KeePassLib.Security
/// <param name="bEnableProtection">Enable protection or not.</param> /// <param name="bEnableProtection">Enable protection or not.</param>
/// <param name="xbProtected"><c>XorredBuffer</c> object used to /// <param name="xbProtected"><c>XorredBuffer</c> object used to
/// initialize the <c>ProtectedBinary</c> object.</param> /// 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) 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(); byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb); Init(bEnableProtection, pb);
MemUtil.ZeroByteArray(pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
} }
private void Init(bool bEnableProtection, byte[] pbData) private void Init(bool bEnableProtection, byte[] pbData)
{ {
if(pbData == null) throw new ArgumentNullException("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_bProtected = bEnableProtection;
m_uDataLen = (uint)pbData.Length; m_uDataLen = (uint)pbData.Length;
int nBlocks = (int)m_uDataLen / PmBlockSize; const int bs = ProtectedBinary.BlockSize;
if((nBlocks * PmBlockSize) < (int)m_uDataLen) ++nBlocks; int nBlocks = (int)m_uDataLen / bs;
Debug.Assert((nBlocks * PmBlockSize) >= (int)m_uDataLen); 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); Array.Copy(pbData, m_pbData, (int)m_uDataLen);
// Data size must be > 0, otherwise 'Protect' throws Encrypt();
if(m_bProtected && m_bProtectionSupported && (m_uDataLen > 0)) }
throw new NotSupportedException();
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> /// <summary>
@ -150,18 +295,16 @@ namespace KeePassLib.Security
/// protected data and can therefore be cleared safely.</returns> /// protected data and can therefore be cleared safely.</returns>
public byte[] ReadData() public byte[] ReadData()
{ {
if(m_uDataLen == 0) return new byte[0]; if(m_uDataLen == 0) return MemUtil.EmptyByteArray;
byte[] pbReturn = new byte[m_uDataLen]; byte[] pbReturn = new byte[m_uDataLen];
if(m_bProtected && m_bProtectionSupported) lock(m_objSync)
{ {
lock(m_objSync) Decrypt();
{ Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
throw new NotSupportedException(); Encrypt();
}
} }
else Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
return pbReturn; return pbReturn;
} }
@ -171,9 +314,6 @@ namespace KeePassLib.Security
/// of bytes generated by a random stream. /// of bytes generated by a random stream.
/// </summary> /// </summary>
/// <param name="crsRandomSource">Random number source.</param> /// <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) public byte[] ReadXorredData(CryptoRandomStream crsRandomSource)
{ {
Debug.Assert(crsRandomSource != null); Debug.Assert(crsRandomSource != null);
@ -183,7 +323,7 @@ namespace KeePassLib.Security
uint uLen = (uint)pbData.Length; uint uLen = (uint)pbData.Length;
byte[] randomPad = crsRandomSource.GetRandomBytes(uLen); byte[] randomPad = crsRandomSource.GetRandomBytes(uLen);
Debug.Assert(randomPad.Length == uLen); Debug.Assert(randomPad.Length == pbData.Length);
for(uint i = 0; i < uLen; ++i) for(uint i = 0; i < uLen; ++i)
pbData[i] ^= randomPad[i]; pbData[i] ^= randomPad[i];
@ -191,8 +331,11 @@ namespace KeePassLib.Security
return pbData; return pbData;
} }
private int? m_hash = null;
public override int GetHashCode() public override int GetHashCode()
{ {
if(m_hash.HasValue) return m_hash.Value;
int h = (m_bProtected ? 0x7B11D289 : 0); int h = (m_bProtected ? 0x7B11D289 : 0);
byte[] pb = ReadData(); byte[] pb = ReadData();
@ -203,6 +346,7 @@ namespace KeePassLib.Security
} }
MemUtil.ZeroByteArray(pb); MemUtil.ZeroByteArray(pb);
m_hash = h;
return h; return h;
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -18,8 +18,8 @@
*/ */
using System; using System;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -47,7 +47,7 @@ namespace KeePassLib.Security
private bool m_bIsProtected; private bool m_bIsProtected;
private static ProtectedString m_psEmpty = new ProtectedString(); private static readonly ProtectedString m_psEmpty = new ProtectedString();
public static ProtectedString Empty public static ProtectedString Empty
{ {
get { return m_psEmpty; } get { return m_psEmpty; }
@ -55,7 +55,7 @@ namespace KeePassLib.Security
/// <summary> /// <summary>
/// A flag specifying whether the <c>ProtectedString</c> object /// 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> /// </summary>
public bool IsProtected public bool IsProtected
{ {
@ -112,10 +112,9 @@ namespace KeePassLib.Security
/// to the value supplied in the parameters. /// to the value supplied in the parameters.
/// </summary> /// </summary>
/// <param name="bEnableProtection">If this parameter is <c>true</c>, /// <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> /// is <c>false</c>, the string will be stored as plain-text.</param>
/// <param name="strValue">The initial string value. This /// <param name="strValue">The initial string value.</param>
/// parameter won't be modified.</param>
public ProtectedString(bool bEnableProtection, string strValue) public ProtectedString(bool bEnableProtection, string strValue)
{ {
Init(bEnableProtection, strValue); Init(bEnableProtection, strValue);
@ -126,7 +125,7 @@ namespace KeePassLib.Security
/// to the value supplied in the parameters (UTF-8 encoded string). /// to the value supplied in the parameters (UTF-8 encoded string).
/// </summary> /// </summary>
/// <param name="bEnableProtection">If this parameter is <c>true</c>, /// <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> /// is <c>false</c>, the string will be stored as plain-text.</param>
/// <param name="vUtf8Value">The initial string value, encoded as /// <param name="vUtf8Value">The initial string value, encoded as
/// UTF-8 byte array. This parameter won't be modified; the caller /// 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 /// <param name="xbProtected"><c>XorredBuffer</c> object containing the
/// string in UTF-8 representation. The UTF-8 string must not /// string in UTF-8 representation. The UTF-8 string must not
/// be <c>null</c>-terminated.</param> /// 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) public ProtectedString(bool bEnableProtection, XorredBuffer xbProtected)
{ {
Debug.Assert(xbProtected != null);
if(xbProtected == null) throw new ArgumentNullException("xbProtected"); if(xbProtected == null) throw new ArgumentNullException("xbProtected");
byte[] pb = xbProtected.ReadPlainText(); byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb); Init(bEnableProtection, pb);
MemUtil.ZeroByteArray(pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
} }
private void Init(bool bEnableProtection, string str) private void Init(bool bEnableProtection, string str)
@ -222,8 +221,6 @@ namespace KeePassLib.Security
/// </summary> /// </summary>
/// <param name="crsRandomSource">Random number source.</param> /// <param name="crsRandomSource">Random number source.</param>
/// <returns>Protected string.</returns> /// <returns>Protected string.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource) public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
{ {
Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource"); Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource");
@ -232,7 +229,7 @@ namespace KeePassLib.Security
uint uLen = (uint)pbData.Length; uint uLen = (uint)pbData.Length;
byte[] randomPad = crsRandomSource.GetRandomBytes(uLen); byte[] randomPad = crsRandomSource.GetRandomBytes(uLen);
Debug.Assert(randomPad.Length == uLen); Debug.Assert(randomPad.Length == pbData.Length);
for(uint i = 0; i < uLen; ++i) for(uint i = 0; i < uLen; ++i)
pbData[i] ^= randomPad[i]; pbData[i] ^= randomPad[i];
@ -246,7 +243,103 @@ namespace KeePassLib.Security
byte[] pb = ReadUtf8(); byte[] pb = ReadUtf8();
ProtectedString ps = new ProtectedString(bProtect, pb); 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; return ps;
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 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 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 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 it under the terms of the GNU General Public License as published by
@ -19,8 +19,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO; using System.IO;
using System.Text;
using KeePassLib.Utility; using KeePassLib.Utility;

View File

@ -1,8 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,10 +19,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Resources; using KeePassLib.Resources;
@ -139,7 +137,7 @@ namespace KeePassLib.Serialization
if(!v[0].StartsWith(LockFileHeader)) { Debug.Assert(false); return null; } if(!v[0].StartsWith(LockFileHeader)) { Debug.Assert(false); return null; }
return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]); return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
} }
catch(Java.IO.FileNotFoundException) { } catch(FileNotFoundException) { }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
finally { if(s != null) s.Close(); } finally { if(s != null) s.Close(); }
@ -154,15 +152,17 @@ namespace KeePassLib.Serialization
try try
{ {
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16); 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, 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.UserName, Environment.MachineName,
Environment.UserDomainName); Environment.UserDomainName);
#else
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
string.Empty, string.Empty, string.Empty);
#endif #endif
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -244,8 +244,8 @@ namespace KeePassLib.Serialization
if(bDisposing) Thread.Sleep(50); if(bDisposing) Thread.Sleep(50);
} }
if(bDisposing && !bFileDeleted) // if(bDisposing && !bFileDeleted)
IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception // IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception
m_iocLockFile = null; m_iocLockFile = null;
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -21,11 +21,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text;
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassUAP)
using System.Security.AccessControl; using System.Security.AccessControl;
#endif #endif
@ -44,6 +44,9 @@ namespace KeePassLib.Serialization
private const string StrTempSuffix = ".tmp"; private const string StrTempSuffix = ".tmp";
private static Dictionary<string, bool> g_dEnabled =
new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer);
public FileTransactionEx(IOConnectionInfo iocBaseFile) public FileTransactionEx(IOConnectionInfo iocBaseFile)
{ {
Initialize(iocBaseFile, true); Initialize(iocBaseFile, true);
@ -101,7 +104,7 @@ namespace KeePassLib.Serialization
{ {
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path); bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassUAP)
bool bEfsEncrypted = false; bool bEfsEncrypted = false;
#endif #endif
@ -130,7 +133,7 @@ namespace KeePassLib.Serialization
IOConnection.RenameFile(m_iocTemp, m_iocBase); IOConnection.RenameFile(m_iocTemp, m_iocBase);
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassUAP)
if(m_iocBase.IsLocalFile()) if(m_iocBase.IsLocalFile())
{ {
try try
@ -148,5 +151,15 @@ namespace KeePassLib.Serialization
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again 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 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 Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
@ -20,11 +20,11 @@
*/ */
using System; using System;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text; using System.Text;
using KeePassLib.Cryptography;
using KeePassLib.Native; using KeePassLib.Native;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -74,7 +74,7 @@ namespace KeePassLib.Serialization
public sealed class HashedBlockStream : Stream 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 Stream m_sBaseStream;
private bool m_bWriting; private bool m_bWriting;
@ -87,7 +87,7 @@ namespace KeePassLib.Serialization
private byte[] m_pbBuffer; private byte[] m_pbBuffer;
private int m_nBufferPos = 0; private int m_nBufferPos = 0;
private uint m_uBufferIndex = 0; private uint m_uBlockIndex = 0;
public override bool CanRead public override bool CanRead
{ {
@ -106,13 +106,13 @@ namespace KeePassLib.Serialization
public override long Length public override long Length
{ {
get { throw new NotSupportedException(); } get { Debug.Assert(false); throw new NotSupportedException(); }
} }
public override long Position public override long Position
{ {
get { throw new NotSupportedException(); } get { Debug.Assert(false); throw new NotSupportedException(); }
set { throw new NotSupportedException(); } set { Debug.Assert(false); throw new NotSupportedException(); }
} }
public HashedBlockStream(Stream sBaseStream, bool bWriting) public HashedBlockStream(Stream sBaseStream, bool bWriting)
@ -137,25 +137,25 @@ namespace KeePassLib.Serialization
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream"); if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize"); if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize");
if(nBufferSize == 0) nBufferSize = m_nDefaultBufferSize; if(nBufferSize == 0) nBufferSize = NbDefaultBufferSize;
m_sBaseStream = sBaseStream; m_sBaseStream = sBaseStream;
m_bWriting = bWriting; m_bWriting = bWriting;
m_bVerify = bVerify; m_bVerify = bVerify;
UTF8Encoding utf8 = StrUtil.Utf8; 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(); throw new InvalidOperationException();
m_brInput = new BinaryReader(sBaseStream, utf8); m_brInput = new BinaryReader(sBaseStream, utf8);
m_pbBuffer = new byte[0]; m_pbBuffer = MemUtil.EmptyByteArray;
} }
else // Writing mode else // Writing mode
{ {
if(m_sBaseStream.CanWrite == false) if(!m_sBaseStream.CanWrite)
throw new InvalidOperationException(); throw new InvalidOperationException();
m_bwOutput = new BinaryWriter(sBaseStream, utf8); m_bwOutput = new BinaryWriter(sBaseStream, utf8);
@ -169,7 +169,7 @@ namespace KeePassLib.Serialization
if(m_bWriting) m_bwOutput.Flush(); if(m_bWriting) m_bwOutput.Flush();
} }
#if KeePassRT #if KeePassUAP
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if(!disposing) return; if(!disposing) return;
@ -179,7 +179,7 @@ namespace KeePassLib.Serialization
#endif #endif
if(m_sBaseStream != null) if(m_sBaseStream != null)
{ {
if(m_bWriting == false) // Reading mode if(!m_bWriting) // Reading mode
{ {
m_brInput.Close(); m_brInput.Close();
m_brInput = null; m_brInput = null;
@ -224,7 +224,7 @@ namespace KeePassLib.Serialization
if(m_nBufferPos == m_pbBuffer.Length) if(m_nBufferPos == m_pbBuffer.Length)
{ {
if(ReadHashedBlock() == false) 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); int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nRemaining);
@ -246,9 +246,9 @@ namespace KeePassLib.Serialization
m_nBufferPos = 0; m_nBufferPos = 0;
if(m_brInput.ReadUInt32() != m_uBufferIndex) if(m_brInput.ReadUInt32() != m_uBlockIndex)
throw new InvalidDataException(); throw new InvalidDataException();
++m_uBufferIndex; ++m_uBlockIndex;
byte[] pbStoredHash = m_brInput.ReadBytes(32); byte[] pbStoredHash = m_brInput.ReadBytes(32);
if((pbStoredHash == null) || (pbStoredHash.Length != 32)) if((pbStoredHash == null) || (pbStoredHash.Length != 32))
@ -273,7 +273,7 @@ namespace KeePassLib.Serialization
} }
m_bEos = true; m_bEos = true;
m_pbBuffer = new byte[0]; m_pbBuffer = MemUtil.EmptyByteArray;
return false; return false;
} }
@ -283,17 +283,13 @@ namespace KeePassLib.Serialization
if(m_bVerify) if(m_bVerify)
{ {
SHA256Managed sha256 = new SHA256Managed(); byte[] pbComputedHash = CryptoUtil.HashSha256(m_pbBuffer);
byte[] pbComputedHash = sha256.ComputeHash(m_pbBuffer);
if((pbComputedHash == null) || (pbComputedHash.Length != 32)) if((pbComputedHash == null) || (pbComputedHash.Length != 32))
throw new InvalidOperationException(); throw new InvalidOperationException();
for(int iHashPos = 0; iHashPos < 32; ++iHashPos) if(!MemUtil.ArraysEqual(pbStoredHash, pbComputedHash))
{
if(pbStoredHash[iHashPos] != pbComputedHash[iHashPos])
throw new InvalidDataException(); throw new InvalidDataException();
} }
}
return true; return true;
} }
@ -320,26 +316,24 @@ namespace KeePassLib.Serialization
private void WriteHashedBlock() private void WriteHashedBlock()
{ {
m_bwOutput.Write(m_uBufferIndex); m_bwOutput.Write(m_uBlockIndex);
++m_uBufferIndex; ++m_uBlockIndex;
if(m_nBufferPos > 0) 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); // SHA256Managed sha256 = new SHA256Managed();
#else // byte[] pbHash;
byte[] pbHash; // if(m_nBufferPos == m_pbBuffer.Length)
if(m_nBufferPos == m_pbBuffer.Length) // pbHash = sha256.ComputeHash(m_pbBuffer);
pbHash = sha256.ComputeHash(m_pbBuffer); // else
else // {
{ // byte[] pbData = new byte[m_nBufferPos];
byte[] pbData = new byte[m_nBufferPos]; // Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos); // pbHash = sha256.ComputeHash(pbData);
pbHash = sha256.ComputeHash(pbData); // }
}
#endif
m_bwOutput.Write(pbHash); 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 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 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 it under the terms of the GNU General Public License as published by
@ -19,12 +19,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.ComponentModel; using System.ComponentModel;
using System.Xml.Serialization;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -122,7 +121,7 @@ namespace KeePassLib.Serialization
private bool m_bComplete = false; private bool m_bComplete = false;
[XmlIgnore] [XmlIgnore]
internal bool IsComplete // Credentials etc. fully specified public bool IsComplete // Credentials etc. fully specified
{ {
get { return m_bComplete; } get { return m_bComplete; }
set { m_bComplete = value; } set { m_bComplete = value; }
@ -134,16 +133,53 @@ namespace KeePassLib.Serialization
set { m_ioHint = value; } set { m_ioHint = value; }
} */ } */
private IocProperties m_props = new IocProperties();
[XmlIgnore]
public IocProperties Properties
{
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() public IOConnectionInfo CloneDeep()
{ {
return (IOConnectionInfo)this.MemberwiseClone(); 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> /// <summary>
/// Serialize the current connection info to a string. Credentials /// Serialize the current connection info to a string. Credentials
/// are only serialized if the <c>SaveCredentials</c> property /// are serialized based on the <c>CredSaveMode</c> property.
/// is <c>true</c>.
/// </summary> /// </summary>
/// <param name="iocToCompile">Input object to be serialized.</param> /// <param name="iocToCompile">Input object to be serialized.</param>
/// <returns>Serialized object as string.</returns> /// <returns>Serialized object as string.</returns>
@ -217,7 +253,6 @@ namespace KeePassLib.Serialization
} }
/// <summary> /// <summary>
/// Very simple string protection. Doesn't really encrypt the input /// Very simple string protection. Doesn't really encrypt the input
/// string, only encodes it that it's not readable on the first glance. /// string, only encodes it that it's not readable on the first glance.
@ -263,14 +298,14 @@ namespace KeePassLib.Serialization
string str = m_strUrl; string str = m_strUrl;
if(m_strUser.Length > 0) if(m_strUser.Length > 0)
str += " (" + m_strUser + ")"; str += (" (" + m_strUser + ")");
return str; return str;
} }
public bool IsEmpty() public bool IsEmpty()
{ {
return (m_strUrl.Length > 0); return (m_strUrl.Length == 0);
} }
public static IOConnectionInfo FromPath(string strPath) public static IOConnectionInfo FromPath(string strPath)
@ -293,7 +328,7 @@ namespace KeePassLib.Serialization
public bool IsLocalFile() public bool IsLocalFile()
{ {
// Not just ":/", see e.g. AppConfigEx.ChangePathRelAbs // Not just ":/", see e.g. AppConfigEx.ChangePathRelAbs
return (m_strUrl.IndexOf(@"://") < 0); return (m_strUrl.IndexOf("://") < 0);
} }
public void ClearCredentials(bool bDependingOnRememberMode) 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 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,18 +19,17 @@
using System; using System;
using System.Collections.Generic; 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.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
#if !KeePassUAP
using System.Drawing;
#endif
using KeePassLib; using KeePassLib;
using KeePassLib.Collections; using KeePassLib.Collections;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
using KeePassLib.Resources; using KeePassLib.Resources;
using KeePassLib.Security; using KeePassLib.Security;
@ -45,49 +42,6 @@ namespace KeePassLib.Serialization
/// </summary> /// </summary>
public sealed partial class KdbxFile 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 private enum KdbContext
{ {
Null, Null,
@ -104,13 +58,17 @@ namespace KeePassLib.Serialization
DeletedObject, DeletedObject,
Group, Group,
GroupTimes, GroupTimes,
GroupCustomData,
GroupCustomDataItem,
Entry, Entry,
EntryTimes, EntryTimes,
EntryString, EntryString,
EntryBinary, EntryBinary,
EntryAutoType, EntryAutoType,
EntryAutoTypeItem, EntryAutoTypeItem,
EntryHistory EntryHistory,
EntryCustomData,
EntryCustomDataItem
} }
private bool m_bReadNextNode = true; private bool m_bReadNextNode = true;
@ -130,10 +88,14 @@ namespace KeePassLib.Serialization
private byte[] m_pbCustomIconData = null; private byte[] m_pbCustomIconData = null;
private string m_strCustomDataKey = null; private string m_strCustomDataKey = null;
private string m_strCustomDataValue = 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() internal static XmlReaderSettings CreateStdXmlReaderSettings()
@ -145,9 +107,12 @@ namespace KeePassLib.Serialization
xrs.IgnoreProcessingInstructions = true; xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = true; xrs.IgnoreWhitespace = true;
#if !KeePassRT #if KeePassUAP
xrs.DtdProcessing = DtdProcessing.Prohibit;
#else
#if !KeePassLibSD #if !KeePassLibSD
xrs.ProhibitDtd = true; xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
#endif #endif
xrs.ValidationType = ValidationType.None; xrs.ValidationType = ValidationType.None;
#endif #endif
@ -258,15 +223,25 @@ namespace KeePassLib.Serialization
ReadString(xr); // Ignore ReadString(xr); // Ignore
else if(xr.Name == ElemHeaderHash) 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); string strHash = ReadString(xr);
if(!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) && if(!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
!m_bRepairMode) !m_bRepairMode)
{ {
Debug.Assert(m_uFileVersion <= FileVersion32_3);
byte[] pbHash = Convert.FromBase64String(strHash); byte[] pbHash = Convert.FromBase64String(strHash);
if(!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader)) if(!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))
throw new IOException(KLRes.FileCorrupted); throw new IOException(KLRes.FileCorrupted);
} }
} }
else if(xr.Name == ElemSettingsChanged)
m_pwDatabase.SettingsChanged = ReadTime(xr);
else if(xr.Name == ElemDbName) else if(xr.Name == ElemDbName)
m_pwDatabase.Name = ReadString(xr); m_pwDatabase.Name = ReadString(xr);
else if(xr.Name == ElemDbNameChanged) else if(xr.Name == ElemDbNameChanged)
@ -426,6 +401,8 @@ namespace KeePassLib.Serialization
m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr)); m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr));
else if(xr.Name == ElemLastTopVisibleEntry) else if(xr.Name == ElemLastTopVisibleEntry)
m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr); m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr);
else if(xr.Name == ElemCustomData)
return SwitchContext(ctx, KdbContext.GroupCustomData, xr);
else if(xr.Name == ElemGroup) else if(xr.Name == ElemGroup)
{ {
m_ctxGroup = new PwGroup(false, false); m_ctxGroup = new PwGroup(false, false);
@ -446,6 +423,20 @@ namespace KeePassLib.Serialization
else ReadUnknown(xr); else ReadUnknown(xr);
break; 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: case KdbContext.Entry:
if(xr.Name == ElemUuid) if(xr.Name == ElemUuid)
m_ctxEntry.Uuid = ReadUuid(xr); m_ctxEntry.Uuid = ReadUuid(xr);
@ -477,6 +468,8 @@ namespace KeePassLib.Serialization
return SwitchContext(ctx, KdbContext.EntryBinary, xr); return SwitchContext(ctx, KdbContext.EntryBinary, xr);
else if(xr.Name == ElemAutoType) else if(xr.Name == ElemAutoType)
return SwitchContext(ctx, KdbContext.EntryAutoType, xr); return SwitchContext(ctx, KdbContext.EntryAutoType, xr);
else if(xr.Name == ElemCustomData)
return SwitchContext(ctx, KdbContext.EntryCustomData, xr);
else if(xr.Name == ElemHistory) else if(xr.Name == ElemHistory)
{ {
Debug.Assert(m_bEntryInHistory == false); Debug.Assert(m_bEntryInHistory == false);
@ -497,15 +490,15 @@ namespace KeePassLib.Serialization
(ITimeLogger)m_ctxGroup : (ITimeLogger)m_ctxEntry); (ITimeLogger)m_ctxGroup : (ITimeLogger)m_ctxEntry);
Debug.Assert(tl != null); Debug.Assert(tl != null);
if(xr.Name == ElemLastModTime) if(xr.Name == ElemCreationTime)
tl.SetLazyLastModificationTime(ReadString(xr));
else if(xr.Name == ElemCreationTime)
tl.SetLazyCreationTime(ReadString(xr)); 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)); tl.SetLazyLastAccessTime(ReadString(xr));
else if(xr.Name == ElemExpiryTime) else if (xr.Name == ElemExpiryTime)
tl.SetLazyExpiryTime(ReadString(xr)); tl.SetLazyExpiryTime(ReadString(xr));
else if(xr.Name == ElemExpires) else if (xr.Name == ElemExpires)
tl.Expires = ReadBool(xr, false); tl.Expires = ReadBool(xr, false);
else if(xr.Name == ElemUsageCount) else if(xr.Name == ElemUsageCount)
tl.UsageCount = ReadULong(xr, 0); tl.UsageCount = ReadULong(xr, 0);
@ -551,6 +544,20 @@ namespace KeePassLib.Serialization
else ReadUnknown(xr); else ReadUnknown(xr);
break; 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: case KdbContext.EntryHistory:
if(xr.Name == ElemEntry) if(xr.Name == ElemEntry)
{ {
@ -652,6 +659,19 @@ namespace KeePassLib.Serialization
} }
else if((ctx == KdbContext.GroupTimes) && (xr.Name == ElemTimes)) else if((ctx == KdbContext.GroupTimes) && (xr.Name == ElemTimes))
return KdbContext.Group; 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)) else if((ctx == KdbContext.Entry) && (xr.Name == ElemEntry))
{ {
// Create new UUID if absent // Create new UUID if absent
@ -702,6 +722,19 @@ namespace KeePassLib.Serialization
m_ctxATSeq = null; m_ctxATSeq = null;
return KdbContext.EntryAutoType; 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)) else if((ctx == KdbContext.EntryHistory) && (xr.Name == ElemHistory))
{ {
m_bEntryInHistory = false; m_bEntryInHistory = false;
@ -852,7 +885,19 @@ namespace KeePassLib.Serialization
if(strRef != null) if(strRef != null)
{ {
ProtectedBinary pb = BinPoolGet(strRef); 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); }
} }
else { Debug.Assert(false); } else { Debug.Assert(false); }
@ -914,7 +959,7 @@ namespace KeePassLib.Serialization
byte[] pbEncrypted; byte[] pbEncrypted;
if(strEncrypted.Length > 0) if(strEncrypted.Length > 0)
pbEncrypted = Convert.FromBase64String(strEncrypted); pbEncrypted = Convert.FromBase64String(strEncrypted);
else pbEncrypted = new byte[0]; else pbEncrypted = MemUtil.EmptyByteArray;
byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length); byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length);

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -19,25 +19,31 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Security; using System.Security;
using System.Security.Cryptography; using System.Text;
using System.Xml; using System.Xml;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
#if !KeePassLibSD #if !KeePassLibSD
using System.IO.Compression; using System.IO.Compression;
#else #else
using KeePassLibSD; using KeePassLibSD;
#endif #endif
using KeePassLib.Collections;
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher; using KeePassLib.Cryptography.Cipher;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
using KeePassLib.Keys; using KeePassLib.Keys;
using KeePassLib.Resources; using KeePassLib.Resources;
using KeePassLib.Utility; using KeePassLib.Utility;
using keepass2android; using keepass2android;
namespace KeePassLib.Serialization namespace KeePassLib.Serialization
@ -48,80 +54,119 @@ namespace KeePassLib.Serialization
public sealed partial class KdbxFile public sealed partial class KdbxFile
{ {
/// <summary> /// <summary>
/// Load a KDB file from a file. /// Load a KDBX file.
/// </summary> /// </summary>
/// <param name="strFilePath">File to load.</param> /// <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> /// <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); IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
Load(IOConnection.OpenRead(ioc), kdbFormat, slLogger); Load(IOConnection.OpenRead(ioc), fmt, slLogger);
} }
/// <summary> /// <summary>
/// Load a KDB file from a stream. /// Load a KDBX file from a stream.
/// </summary> /// </summary>
/// <param name="sSource">Stream to read the data from. Must contain /// <param name="sSource">Stream to read the data from. Must contain
/// a KDBX stream.</param> /// a KDBX stream.</param>
/// <param name="kdbFormat">Format specifier.</param> /// <param name="fmt">Format.</param>
/// <param name="slLogger">Status logger (optional).</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); Debug.Assert(sSource != null);
if(sSource == null) throw new ArgumentNullException("sSource"); if(sSource == null) throw new ArgumentNullException("sSource");
m_format = kdbFormat; m_format = fmt;
m_slLogger = slLogger; m_slLogger = slLogger;
HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null);
UTF8Encoding encNoBom = StrUtil.Utf8; 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 try
{ {
BinaryReaderEx br = null; Stream sXml;
BinaryReaderEx brDecrypted = null; if (fmt == KdbxFormat.Default || fmt == KdbxFormat.ProtocolBuffers)
Stream readerStream = null;
if(kdbFormat == KdbxFormat.Default || kdbFormat == KdbxFormat.ProtocolBuffers)
{ {
br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted); BinaryReaderEx br = new BinaryReaderEx(sHashing,
ReadHeader(br); encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
Stream sDecrypted = AttachStreamDecryptor(hashedStream); int cbEncKey, cbEncIV;
if((sDecrypted == null) || (sDecrypted == hashedStream)) ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
throw new SecurityException(KLRes.CryptoStreamFailed);
if (m_slLogger != null) if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo); m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted); ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) Stream sPlain;
throw new InvalidDataException(); if(m_uFileVersion <= FileVersion32_3)
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
for(int iStart = 0; iStart < 32; ++iStart)
{ {
if(pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart]) Stream sDecrypted = EncryptStream(sHashing, iCipher,
throw new InvalidCompositeKeyException(); pbCipherKey, cbEncIV, false);
} if((sDecrypted == null) || (sDecrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
Stream sHashed = new HashedBlockStream(sDecrypted, false, 0, if (m_slLogger != null)
!m_bRepairMode); 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) 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) else if(fmt == KdbxFormat.PlainXml)
readerStream = hashedStream; sXml = sHashing;
else { Debug.Assert(false); throw new FormatException("KdbFormat"); } else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
if(kdbFormat != KdbxFormat.PlainXml) // Is an encrypted format if(fmt == KdbxFormat.Default)
{ {
if(m_pbProtectedStreamKey == null) if(m_pbProtectedStreamKey == null)
{ {
@ -135,39 +180,60 @@ namespace KeePassLib.Serialization
else m_randomStream = null; // No random stream for plain-text files else m_randomStream = null; // No random stream for plain-text files
if (m_slLogger != null) if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo); 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(); 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)); Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
} }
else else
{ {
ReadXmlStreamed(readerStream, hashedStream);
ReadXmlStreamed(sXml, sHashing);
Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds)); Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds));
} }
// ReadXmlDom(sXml);
readerStream.Close();
// GC.KeepAlive(br);
// GC.KeepAlive(brDecrypted);
} }
catch(CryptographicException) // Thrown on invalid padding catch(CryptographicException) // Thrown on invalid padding
{ {
throw new CryptographicException(KLRes.FileCorrupted); 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(); CloseStreams(lStreams);
m_pbHashOfFileOnDisk = hashedStream.Hash;
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 // Reset memory protection settings (to always use reasonable
// defaults) // defaults)
@ -180,10 +246,16 @@ namespace KeePassLib.Serialization
// the history maintenance settings) // the history maintenance settings)
m_pwDatabase.MaintainBackups(); // Don't mark database as modified 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; m_pbHashOfHeader = null;
} }
private void ReadHeader(BinaryReaderEx br) private byte[] LoadHeader(BinaryReaderEx br)
{ {
MemoryStream msHeader = new MemoryStream(); MemoryStream msHeader = new MemoryStream();
Debug.Assert(br.CopyDataTo == null); Debug.Assert(br.CopyDataTo == null);
@ -208,18 +280,19 @@ namespace KeePassLib.Serialization
if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask)) if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
throw new FormatException(KLRes.FileVersionUnsupported + throw new FormatException(KLRes.FileVersionUnsupported +
MessageService.NewParagraph + KLRes.FileNewVerReq); MessageService.NewParagraph + KLRes.FileNewVerReq);
m_uFileVersion = uVersion;
while(true) while(true)
{ {
if(ReadHeaderField(br) == false) if(!ReadHeaderField(br)) break;
break;
} }
br.CopyDataTo = null; br.CopyDataTo = null;
byte[] pbHeader = msHeader.ToArray(); byte[] pbHeader = msHeader.ToArray();
msHeader.Close(); msHeader.Close();
SHA256Managed sha256 = new SHA256Managed();
m_pbHashOfHeader = sha256.ComputeHash(pbHeader); m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
return pbHeader;
} }
private bool ReadHeaderField(BinaryReaderEx brSource) private bool ReadHeaderField(BinaryReaderEx brSource)
@ -228,15 +301,21 @@ namespace KeePassLib.Serialization
if(brSource == null) throw new ArgumentNullException("brSource"); if(brSource == null) throw new ArgumentNullException("brSource");
byte btFieldID = brSource.ReadByte(); byte btFieldID = brSource.ReadByte();
ushort uSize = MemUtil.BytesToUInt16(brSource.ReadBytes(2));
byte[] pbData = null; int cbSize;
if(uSize > 0) 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; string strPrevExcpText = brSource.ReadExceptionText;
brSource.ReadExceptionText = KLRes.FileHeaderEndEarly; brSource.ReadExceptionText = KLRes.FileHeaderEndEarly;
pbData = brSource.ReadBytes(uSize); pbData = brSource.ReadBytes(cbSize);
brSource.ReadExceptionText = strPrevExcpText; brSource.ReadExceptionText = strPrevExcpText;
} }
@ -262,13 +341,27 @@ namespace KeePassLib.Serialization
CryptoRandom.Instance.AddEntropy(pbData); CryptoRandom.Instance.AddEntropy(pbData);
break; break;
// Obsolete; for backward compatibility only
case KdbxHeaderFieldID.TransformSeed: 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); CryptoRandom.Instance.AddEntropy(pbData);
break; break;
// Obsolete; for backward compatibility only
case KdbxHeaderFieldID.TransformRounds: 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; break;
case KdbxHeaderFieldID.EncryptionIV: case KdbxHeaderFieldID.EncryptionIV:
@ -281,6 +374,7 @@ namespace KeePassLib.Serialization
break; break;
case KdbxHeaderFieldID.StreamStartBytes: case KdbxHeaderFieldID.StreamStartBytes:
Debug.Assert(m_uFileVersion <= FileVersion32_3);
m_pbStreamStartBytes = pbData; m_pbStreamStartBytes = pbData;
break; break;
@ -288,6 +382,15 @@ namespace KeePassLib.Serialization
SetInnerRandomStreamID(pbData); SetInnerRandomStreamID(pbData);
break; 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: default:
Debug.Assert(false); Debug.Assert(false);
if(m_slLogger != null) if(m_slLogger != null)
@ -301,7 +404,7 @@ namespace KeePassLib.Serialization
private void SetCipher(byte[] pbID) private void SetCipher(byte[] pbID)
{ {
if((pbID == null) || (pbID.Length != 16)) if((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
throw new FormatException(KLRes.FileUnknownCipher); throw new FormatException(KLRes.FileUnknownCipher);
m_pwDatabase.DataCipherUuid = new PwUuid(pbID); m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
@ -325,36 +428,6 @@ namespace KeePassLib.Serialization
m_craInnerRandomStream = (CrsAlgorithm)uID; 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] [Obsolete]
public static List<PwEntry> ReadEntries(PwDatabase pwDatabase, Stream msData) public static List<PwEntry> ReadEntries(PwDatabase pwDatabase, Stream msData)
{ {

View File

@ -1,8 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,30 +19,35 @@
using System; using System;
using System.Collections.Generic; 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.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security;
using System.Text;
using System.Xml;
#if !KeePassLibSD #if !KeePassUAP
using System.IO.Compression; using System.Drawing;
#else using System.Security.Cryptography;
#endif
#if KeePassLibSD
using KeePassLibSD; using KeePassLibSD;
#else
using System.IO.Compression;
#endif #endif
using KeePassLib.Collections; using KeePassLib.Collections;
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher; using KeePassLib.Cryptography.Cipher;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Delegates; using KeePassLib.Delegates;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
using KeePassLib.Keys; using KeePassLib.Keys;
using KeePassLib.Resources; using KeePassLib.Resources;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Utility; using KeePassLib.Utility;
using keepass2android; using keepass2android;
namespace KeePassLib.Serialization namespace KeePassLib.Serialization
@ -54,7 +57,7 @@ namespace KeePassLib.Serialization
/// </summary> /// </summary>
public sealed partial class KdbxFile 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) // IStatusLogger slLogger)
// { // {
// bool bMadeUnhidden = UrlUtil.UnhideFile(strFile); // bool bMadeUnhidden = UrlUtil.UnhideFile(strFile);
@ -72,175 +75,257 @@ namespace KeePassLib.Serialization
/// <param name="pgDataSource">Group containing all groups and /// <param name="pgDataSource">Group containing all groups and
/// entries to write. If <c>null</c>, the complete database will /// entries to write. If <c>null</c>, the complete database will
/// be written.</param> /// 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> /// <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) IStatusLogger slLogger)
{ {
Debug.Assert(sSaveTo != null); Debug.Assert(sSaveTo != null);
if(sSaveTo == null) throw new ArgumentNullException("sSaveTo"); if(sSaveTo == null) throw new ArgumentNullException("sSaveTo");
m_format = format; m_format = fmt;
m_slLogger = slLogger; m_slLogger = slLogger;
HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null);
UTF8Encoding encNoBom = StrUtil.Utf8; UTF8Encoding encNoBom = StrUtil.Utf8;
CryptoRandom cr = CryptoRandom.Instance; 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 try
{ {
m_pbMasterSeed = cr.GetRandomBytes(32); m_uFileVersion = GetMinKdbxVersion();
m_pbTransformSeed = cr.GetRandomBytes(32);
m_pbEncryptionIV = cr.GetRandomBytes(16);
m_pbProtectedStreamKey = cr.GetRandomBytes(32); int cbEncKey, cbEncIV;
m_craInnerRandomStream = CrsAlgorithm.Salsa20; 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_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbProtectedStreamKey); m_pbProtectedStreamKey);
m_pbStreamStartBytes = cr.GetRandomBytes(32); if(m_uFileVersion <= FileVersion32_3)
m_pbStreamStartBytes = cr.GetRandomBytes(32);
Stream writerStream; Stream sXml;
if(m_format == KdbxFormat.Default || m_format == KdbxFormat.ProtocolBuffers) 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); MemUtil.Write(sHashing, pbHeader);
if((sEncrypted == null) || (sEncrypted == hashedStream)) sHashing.Flush();
throw new SecurityException(KLRes.CryptoStreamFailed);
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) if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
writerStream = new GZipStream(sHashed, CompressionMode.Compress); {
else sXml = new GZipStream(sPlain, CompressionMode.Compress);
writerStream = sHashed; lStreams.Add(sXml);
}
else sXml = sPlain;
} }
else if(m_format == KdbxFormat.PlainXml) else if(m_format == KdbxFormat.PlainXml)
writerStream = hashedStream; sXml = sHashing;
else { Debug.Assert(false); throw new FormatException("KdbFormat"); } else
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("fmt");
}
var stopWatch = Stopwatch.StartNew(); var stopWatch = Stopwatch.StartNew();
if (m_format == KdbxFormat.ProtocolBuffers) if (m_format == KdbxFormat.ProtocolBuffers)
{ {
KdbpFile.WriteDocument(m_pwDatabase, writerStream, m_pbProtectedStreamKey, m_pbHashOfHeader); KdbpFile.WriteDocument(m_pwDatabase, sXml, m_pbProtectedStreamKey, m_pbHashOfHeader);
} }
else 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); WriteDocument(pgDataSource);
m_xmlWriter.Flush(); m_xmlWriter.Flush();
m_xmlWriter.Close(); m_xmlWriter.Close();
} }
writerStream.Close();
Kp2aLog.Log(String.Format("{1}: {0}ms", stopWatch.ElapsedMilliseconds, m_format == KdbxFormat.ProtocolBuffers ? "KdbpFile.WriteDocument" : "Xml WriteDocument")); 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(); CloseStreams(lStreams);
m_pbHashOfFileOnDisk = hashedStream.Hash;
sSaveTo.Close(); Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
m_pbHashOfFileOnDisk = sHashing.Hash;
Debug.Assert(m_pbHashOfFileOnDisk != null);
m_xmlWriter = null; m_xmlWriter = null;
m_pbHashOfHeader = 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)); WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2)); m_pwDatabase.DataCipherUuid.UuidBytes);
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileVersion32));
WriteHeaderField(ms, KdbxHeaderFieldID.CipherID, int nCprID = (int)m_pwDatabase.Compression;
m_pwDatabase.DataCipherUuid.UuidBytes); WriteHeaderField(ms, KdbxHeaderFieldID.CompressionFlags,
MemUtil.UInt32ToBytes((uint)nCprID));
int nCprID = (int)m_pwDatabase.Compression; WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
WriteHeaderField(ms, KdbxHeaderFieldID.CompressionFlags,
MemUtil.UInt32ToBytes((uint)nCprID));
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed); if(m_uFileVersion <= FileVersion32_3)
WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed, m_pbTransformSeed); {
WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds, Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
MemUtil.UInt64ToBytes(m_pwDatabase.KeyEncryptionRounds)); (new AesKdf()).Uuid));
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV); WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed,
WriteHeaderField(ms, KdbxHeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey); m_pwDatabase.KdfParameters.GetByteArray(AesKdf.ParamSeed));
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes, m_pbStreamStartBytes); 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; if(m_pbEncryptionIV.Length > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID, WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
MemUtil.UInt32ToBytes((uint)nIrsID));
WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[]{ WriteHeaderField(ms, KdbxHeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey);
(byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' });
byte[] pbHeader = ms.ToArray(); if(m_uFileVersion <= FileVersion32_3)
ms.Close(); WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
m_pbStreamStartBytes);
SHA256Managed sha256 = new SHA256Managed(); int nIrsID = (int)m_craInnerRandomStream;
m_pbHashOfHeader = sha256.ComputeHash(pbHeader); WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
MemUtil.Int32ToBytes(nIrsID));
s.Write(pbHeader, 0, pbHeader.Length); // Write public custom data only when there is at least one item,
s.Flush(); // 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) byte[] pbData)
{ {
s.WriteByte((byte)kdbID); 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; if(cb > (int)ushort.MaxValue)
MemUtil.Write(s, MemUtil.UInt16ToBytes(uLength)); {
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) if(cb > 0) s.Write(pb, 0, cb);
{
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);
} }
private void WriteDocument(PwGroup pgDataSource) private void WriteDocument(PwGroup pgDataSource)
@ -255,10 +340,6 @@ namespace KeePassLib.Serialization
BinPoolBuild(pgRoot); BinPoolBuild(pgRoot);
m_xmlWriter.Formatting = Formatting.Indented;
m_xmlWriter.IndentChar = '\t';
m_xmlWriter.Indentation = 1;
m_xmlWriter.WriteStartDocument(true); m_xmlWriter.WriteStartDocument(true);
m_xmlWriter.WriteStartElement(ElemDocNode); m_xmlWriter.WriteStartElement(ElemDocNode);
@ -330,12 +411,15 @@ namespace KeePassLib.Serialization
{ {
m_xmlWriter.WriteStartElement(ElemMeta); 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( WriteObject(ElemHeaderHash, Convert.ToBase64String(
m_pbHashOfHeader), false); m_pbHashOfHeader), false);
if(m_uFileVersion > FileVersion32_3)
WriteObject(ElemSettingsChanged, m_pwDatabase.SettingsChanged);
WriteObject(ElemDbName, m_pwDatabase.Name, true); WriteObject(ElemDbName, m_pwDatabase.Name, true);
WriteObject(ElemDbNameChanged, m_pwDatabase.NameChanged); WriteObject(ElemDbNameChanged, m_pwDatabase.NameChanged);
WriteObject(ElemDbDesc, m_pwDatabase.Description, true); WriteObject(ElemDbDesc, m_pwDatabase.Description, true);
@ -386,6 +470,9 @@ namespace KeePassLib.Serialization
WriteObject(ElemEnableAutoType, StrUtil.BoolToStringEx(pg.EnableAutoType), false); WriteObject(ElemEnableAutoType, StrUtil.BoolToStringEx(pg.EnableAutoType), false);
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false); WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry); WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
if(pg.CustomData.Count > 0)
WriteList(ElemCustomData, pg.CustomData);
} }
private void EndGroup() private void EndGroup()
@ -416,6 +503,9 @@ namespace KeePassLib.Serialization
WriteList(pe.Binaries); WriteList(pe.Binaries);
WriteList(ElemAutoType, pe.AutoType); WriteList(ElemAutoType, pe.AutoType);
if(pe.CustomData.Count > 0)
WriteList(ElemCustomData, pe.CustomData);
if(!bIsHistory) WriteList(ElemHistory, pe.History, true); if(!bIsHistory) WriteList(ElemHistory, pe.History, true);
else { Debug.Assert(pe.History.UCount == 0); } else { Debug.Assert(pe.History.UCount == 0); }
@ -468,8 +558,8 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(name); m_xmlWriter.WriteStartElement(name);
WriteObject(ElemLastModTime, times.LastModificationTime);
WriteObject(ElemCreationTime, times.CreationTime); WriteObject(ElemCreationTime, times.CreationTime);
WriteObject(ElemLastModTime, times.LastModificationTime);
WriteObject(ElemLastAccessTime, times.LastAccessTime); WriteObject(ElemLastAccessTime, times.LastAccessTime);
WriteObject(ElemExpiryTime, times.ExpiryTime); WriteObject(ElemExpiryTime, times.ExpiryTime);
WriteObject(ElemExpires, times.Expires); WriteObject(ElemExpires, times.Expires);

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -19,20 +19,25 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml; using System.Diagnostics;
using System.Text; using System.Drawing;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Diagnostics; using System.Security;
using System.Text;
using System.Xml;
#if !KeePassLibSD #if !KeePassUAP
using System.IO.Compression; using System.Security.Cryptography;
#endif #endif
using KeePassLib.Collections; using KeePassLib.Collections;
using KeePassLib.Cryptography; using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Delegates; using KeePassLib.Delegates;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
using KeePassLib.Resources;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Utility; using KeePassLib.Utility;
@ -61,6 +66,47 @@ namespace KeePassLib.Serialization
/// </summary> /// </summary>
public sealed partial class KdbxFile 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> /// <summary>
/// File identifier, first 32-bit value. /// File identifier, first 32-bit value.
/// </summary> /// </summary>
@ -78,7 +124,8 @@ namespace KeePassLib.Serialization
/// The first 2 bytes are critical (i.e. loading will fail, if the /// The first 2 bytes are critical (i.e. loading will fail, if the
/// file version is too high), the last 2 bytes are informational. /// file version is too high), the last 2 bytes are informational.
/// </summary> /// </summary>
private const uint FileVersion32 = 0x00030001; private const uint FileVersion32 = 0x00040000;
private const uint FileVersion32_3 = 0x00030001; // Old format
private const uint FileVersionCriticalMask = 0xFFFF0000; private const uint FileVersionCriticalMask = 0xFFFF0000;
@ -97,6 +144,7 @@ namespace KeePassLib.Serialization
private const string ElemGenerator = "Generator"; private const string ElemGenerator = "Generator";
private const string ElemHeaderHash = "HeaderHash"; private const string ElemHeaderHash = "HeaderHash";
private const string ElemSettingsChanged = "SettingsChanged";
private const string ElemDbName = "DatabaseName"; private const string ElemDbName = "DatabaseName";
private const string ElemDbNameChanged = "DatabaseNameChanged"; private const string ElemDbNameChanged = "DatabaseNameChanged";
private const string ElemDbDesc = "DatabaseDescription"; private const string ElemDbDesc = "DatabaseDescription";
@ -192,19 +240,20 @@ namespace KeePassLib.Serialization
private PwDatabase m_pwDatabase; // Not null, see constructor private PwDatabase m_pwDatabase; // Not null, see constructor
private XmlTextWriter m_xmlWriter = null; private XmlWriter m_xmlWriter = null;
private CryptoRandomStream m_randomStream = null; private CryptoRandomStream m_randomStream = null;
private KdbxFormat m_format = KdbxFormat.Default; private KdbxFormat m_format = KdbxFormat.Default;
private IStatusLogger m_slLogger = null; private IStatusLogger m_slLogger = null;
private uint m_uFileVersion = 0;
private byte[] m_pbMasterSeed = null; private byte[] m_pbMasterSeed = null;
private byte[] m_pbTransformSeed = null; // private byte[] m_pbTransformSeed = null;
private byte[] m_pbEncryptionIV = null; private byte[] m_pbEncryptionIV = null;
private byte[] m_pbProtectedStreamKey = null; private byte[] m_pbProtectedStreamKey = null;
private byte[] m_pbStreamStartBytes = null; private byte[] m_pbStreamStartBytes = null;
// ArcFourVariant only for compatibility; KeePass will default to a // ArcFourVariant only for backward compatibility; KeePass defaults
// different (more secure) algorithm when *writing* databases // to a more secure algorithm when *writing* databases
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant; private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
private Dictionary<string, ProtectedBinary> m_dictBinPool = private Dictionary<string, ProtectedBinary> m_dictBinPool =
@ -227,12 +276,14 @@ namespace KeePassLib.Serialization
CipherID = 2, CipherID = 2,
CompressionFlags = 3, CompressionFlags = 3,
MasterSeed = 4, MasterSeed = 4,
TransformSeed = 5, TransformSeed = 5, // KDBX 3.1, for backward compatibility only
TransformRounds = 6, TransformRounds = 6, // KDBX 3.1, for backward compatibility only
EncryptionIV = 7, EncryptionIV = 7,
ProtectedStreamKey = 8, ProtectedStreamKey = 8,
StreamStartBytes = 9, StreamStartBytes = 9, // KDBX 3.1, for backward compatibility only
InnerRandomStreamID = 10 InnerRandomStreamID = 10,
KdfParameters = 11, // KDBX 4
PublicCustomData = 12 // KDBX 4
} }
public byte[] HashOfFileOnDisk 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) private void BinPoolBuild(PwGroup pgDataSource)
{ {
m_dictBinPool = new Dictionary<string, ProtectedBinary>(); m_dictBinPool = new Dictionary<string, ProtectedBinary>();

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 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 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,15 +19,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Text; using System.Text;
using System.Xml.Serialization; using System.Xml.Serialization;
using System.Diagnostics;
namespace KeePassLib.Translation namespace KeePassLib.Translation
{ {
public class ToolStripItemCollection
{}
public sealed class KPStringTable public sealed class KPStringTable
{ {
private string m_strName = string.Empty; private string m_strName = string.Empty;
@ -69,9 +65,9 @@ namespace KeePassLib.Translation
return dict; return dict;
} }
/*
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassUAP)
/*public void ApplyTo(ToolStripItemCollection tsic) public void ApplyTo(ToolStripItemCollection tsic)
{ {
if(tsic == null) throw new ArgumentNullException("tsic"); if(tsic == null) throw new ArgumentNullException("tsic");
@ -97,7 +93,7 @@ namespace KeePassLib.Translation
if((tsmi != null) && (tsmi.DropDownItems != null)) if((tsmi != null) && (tsmi.DropDownItems != null))
this.ApplyTo(tsmi.DropDownItems); this.ApplyTo(tsmi.DropDownItems);
} }
}*/ }
#endif #endif*/
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 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 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,24 +19,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.ComponentModel;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Text;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using System.ComponentModel; #if KeePassLibSD
using System.Drawing; using ICSharpCode.SharpZipLib.GZip;
using System.Diagnostics; #else
using System.IO.Compression;
#endif
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
using KeePassLib.Utility; using KeePassLib.Utility;
#if !KeePassLibSD
using System.IO.Compression;
#else
using ICSharpCode.SharpZipLib.GZip;
#endif
namespace KeePassLib.Translation namespace KeePassLib.Translation
{ {
[XmlRoot("Translation")] [XmlRoot("Translation")]
@ -66,7 +62,7 @@ namespace KeePassLib.Translation
m_vStringTables = value; m_vStringTables = value;
} }
} }
/*
private List<KPFormCustomization> m_vForms = new List<KPFormCustomization>(); private List<KPFormCustomization> m_vForms = new List<KPFormCustomization>();
[XmlArrayItem("Form")] [XmlArrayItem("Form")]
@ -80,7 +76,7 @@ namespace KeePassLib.Translation
m_vForms = value; m_vForms = value;
} }
} }
*/
private string m_strUnusedText = string.Empty; private string m_strUnusedText = string.Empty;
[DefaultValue("")] [DefaultValue("")]
public string UnusedText 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) IXmlSerializerEx xs)
{ {
if(xs == null) throw new ArgumentNullException("xs"); if(xs == null) throw new ArgumentNullException("xs");
FileStream fs = new FileStream(strFileName, FileMode.Create,
FileAccess.Write, FileShare.None);
#if !KeePassLibSD #if !KeePassLibSD
GZipStream gz = new GZipStream(fs, CompressionMode.Compress); GZipStream gz = new GZipStream(sOut, CompressionMode.Compress);
#else #else
GZipOutputStream gz = new GZipOutputStream(fs); GZipOutputStream gz = new GZipOutputStream(sOut);
#endif #endif
XmlWriterSettings xws = new XmlWriterSettings(); XmlWriterSettings xws = new XmlWriterSettings();
@ -120,27 +123,36 @@ namespace KeePassLib.Translation
xw.Close(); xw.Close();
gz.Close(); gz.Close();
fs.Close(); sOut.Close();
} }
public static KPTranslation LoadFromFile(string strFile, public static KPTranslation Load(string strFile, IXmlSerializerEx xs)
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"); if(xs == null) throw new ArgumentNullException("xs");
FileStream fs = new FileStream(strFile, FileMode.Open,
FileAccess.Read, FileShare.Read);
#if !KeePassLibSD #if !KeePassLibSD
GZipStream gz = new GZipStream(fs, CompressionMode.Decompress); GZipStream gz = new GZipStream(s, CompressionMode.Decompress);
#else #else
GZipInputStream gz = new GZipInputStream(fs); GZipInputStream gz = new GZipInputStream(s);
#endif #endif
KPTranslation kpTrl = (xs.Deserialize(gz) as KPTranslation); KPTranslation kpTrl = (xs.Deserialize(gz) as KPTranslation);
gz.Close(); gz.Close();
fs.Close(); s.Close();
return kpTrl; return kpTrl;
} }
@ -154,9 +166,9 @@ namespace KeePassLib.Translation
return new Dictionary<string, string>(); return new Dictionary<string, string>();
} }
/*
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassUAP)
/*public void ApplyTo(Form form) public void ApplyTo(Form form)
{ {
if(form == null) throw new ArgumentNullException("form"); if(form == null) throw new ArgumentNullException("form");
@ -185,8 +197,8 @@ namespace KeePassLib.Translation
try { RtlApplyToControls(form.Controls); } try { RtlApplyToControls(form.Controls); }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
}*/ }
/*
private static void RtlApplyToControls(Control.ControlCollection cc) private static void RtlApplyToControls(Control.ControlCollection cc)
{ {
foreach(Control c in cc) foreach(Control c in cc)
@ -212,9 +224,9 @@ namespace KeePassLib.Translation
if((c is GroupBox) || (c is Panel)) RtlMoveChildControls(c); 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; int nParentWidth = cParent.Size.Width;
@ -248,7 +260,7 @@ namespace KeePassLib.Translation
} }
if(kpst != null) kpst.ApplyTo(tsic); if(kpst != null) kpst.ApplyTo(tsic);
}*/ }
#endif #endif*/
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 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 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 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 it under the terms of the GNU General Public License as published by
@ -19,9 +19,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text;
#if !KeePassLibSD #if !KeePassLibSD
using System.IO.Compression; using System.IO.Compression;
@ -38,8 +38,7 @@ namespace KeePassLib.Utility
public static void Open(string strPrefix) 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(); AppLogEx.Close();

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -19,15 +19,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
#if !KeePassLibSD #if KeePassLibSD
using System.IO.Compression;
#else
using KeePassLibSD; using KeePassLibSD;
#else
using System.IO.Compression;
#endif #endif
namespace KeePassLib.Utility namespace KeePassLib.Utility
@ -37,6 +37,8 @@ namespace KeePassLib.Utility
/// </summary> /// </summary>
public static class MemUtil public static class MemUtil
{ {
internal static readonly byte[] EmptyByteArray = new byte[0];
private static readonly uint[] m_vSBox = new uint[256] { private static readonly uint[] m_vSBox = new uint[256] {
0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230, 0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230,
0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4, 0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4,
@ -187,75 +189,211 @@ namespace KeePassLib.Utility
return sb.ToString(); 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> /// <summary>
/// Set all bytes in a byte array to zero. /// Set all bytes in a byte array to zero.
/// </summary> /// </summary>
/// <param name="pbArray">Input array. All bytes of this array will be set /// <param name="pbArray">Input array. All bytes of this array
/// to zero.</param> /// will be set to zero.</param>
#if KeePassLibSD
[MethodImpl(MethodImplOptions.NoInlining)]
#else
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
#endif
public static void ZeroByteArray(byte[] pbArray) public static void ZeroByteArray(byte[] pbArray)
{ {
Debug.Assert(pbArray != null); if(pbArray == null) throw new ArgumentNullException("pbArray"); Debug.Assert(pbArray != null);
if(pbArray == null) throw new ArgumentNullException("pbArray");
// for(int i = 0; i < pbArray.Length; ++i)
// pbArray[i] = 0;
Array.Clear(pbArray, 0, pbArray.Length); Array.Clear(pbArray, 0, pbArray.Length);
} }
/// <summary> /// <summary>
/// Convert 2 bytes to a 16-bit unsigned integer using Little-Endian /// Set all elements of an array to the default value.
/// encoding. /// </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> /// </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) public static ushort BytesToUInt16(byte[] pb)
{ {
Debug.Assert((pb != null) && (pb.Length == 2)); Debug.Assert((pb != null) && (pb.Length == 2));
if(pb == null) throw new ArgumentNullException("pb"); 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)); return (ushort)((ushort)pb[0] | ((ushort)pb[1] << 8));
} }
/// <summary> /// <summary>
/// Convert 4 bytes to a 32-bit unsigned integer using Little-Endian /// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
/// encoding. /// </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> /// </summary>
/// <param name="pb">Input bytes.</param>
/// <returns>32-bit unsigned integer.</returns>
public static uint BytesToUInt32(byte[] pb) public static uint BytesToUInt32(byte[] pb)
{ {
Debug.Assert((pb != null) && (pb.Length == 4)); Debug.Assert((pb != null) && (pb.Length == 4));
if(pb == null) throw new ArgumentNullException("pb"); 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) | return ((uint)pb[0] | ((uint)pb[1] << 8) | ((uint)pb[2] << 16) |
((uint)pb[3] << 24); ((uint)pb[3] << 24));
} }
/// <summary> /// <summary>
/// Convert 8 bytes to a 64-bit unsigned integer using Little-Endian /// Convert 4 bytes to a 32-bit unsigned integer (little-endian).
/// encoding. /// </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> /// </summary>
/// <param name="pb">Input bytes.</param>
/// <returns>64-bit unsigned integer.</returns>
public static ulong BytesToUInt64(byte[] pb) public static ulong BytesToUInt64(byte[] pb)
{ {
Debug.Assert((pb != null) && (pb.Length == 8)); Debug.Assert((pb != null) && (pb.Length == 8));
if(pb == null) throw new ArgumentNullException("pb"); 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[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> /// <summary>
/// Convert a 16-bit unsigned integer to 2 bytes using Little-Endian /// Convert 8 bytes to a 64-bit unsigned integer (little-endian).
/// encoding. /// </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> /// </summary>
/// <param name="uValue">16-bit input word.</param>
/// <returns>Two bytes representing the 16-bit value.</returns>
public static byte[] UInt16ToBytes(ushort uValue) public static byte[] UInt16ToBytes(ushort uValue)
{ {
byte[] pb = new byte[2]; byte[] pb = new byte[2];
@ -270,11 +408,8 @@ namespace KeePassLib.Utility
} }
/// <summary> /// <summary>
/// Convert a 32-bit unsigned integer to 4 bytes using Little-Endian /// Convert a 32-bit unsigned integer to 4 bytes (little-endian).
/// encoding.
/// </summary> /// </summary>
/// <param name="uValue">32-bit input word.</param>
/// <returns>Four bytes representing the 32-bit value.</returns>
public static byte[] UInt32ToBytes(uint uValue) public static byte[] UInt32ToBytes(uint uValue)
{ {
byte[] pb = new byte[4]; byte[] pb = new byte[4];
@ -291,11 +426,29 @@ namespace KeePassLib.Utility
} }
/// <summary> /// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes using Little-Endian /// Convert a 32-bit unsigned integer to 4 bytes (little-endian).
/// encoding. /// </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> /// </summary>
/// <param name="uValue">64-bit input word.</param>
/// <returns>Eight bytes representing the 64-bit value.</returns>
public static byte[] UInt64ToBytes(ulong uValue) public static byte[] UInt64ToBytes(ulong uValue)
{ {
byte[] pb = new byte[8]; byte[] pb = new byte[8];
@ -315,6 +468,61 @@ namespace KeePassLib.Utility
return pb; 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) public static bool ArraysEqual(byte[] x, byte[] y)
{ {
// Return false if one of them is null (not comparable)! // Return false if one of them is null (not comparable)!
@ -330,19 +538,21 @@ namespace KeePassLib.Utility
return true; return true;
} }
public static void XorArray(byte[] pbSource, int nSourceOffset, public static void XorArray(byte[] pbSource, int iSourceOffset,
byte[] pbBuffer, int nBufferOffset, int nLength) byte[] pbBuffer, int iBufferOffset, int cb)
{ {
if(pbSource == null) throw new ArgumentNullException("pbSource"); 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(pbBuffer == null) throw new ArgumentNullException("pbBuffer");
if(nBufferOffset < 0) throw new ArgumentException(); if(iBufferOffset < 0) throw new ArgumentOutOfRangeException("iBufferOffset");
if(nLength < 0) throw new ArgumentException(); if(cb < 0) throw new ArgumentOutOfRangeException("cb");
if((nSourceOffset + nLength) > pbSource.Length) throw new ArgumentException(); if(iSourceOffset > (pbSource.Length - cb))
if((nBufferOffset + nLength) > pbBuffer.Length) throw new ArgumentException(); throw new ArgumentOutOfRangeException("cb");
if(iBufferOffset > (pbBuffer.Length - cb))
throw new ArgumentOutOfRangeException("cb");
for(int i = 0; i < nLength; ++i) for(int i = 0; i < cb; ++i)
pbBuffer[nBufferOffset + i] ^= pbSource[nSourceOffset + i]; pbBuffer[iBufferOffset + i] ^= pbSource[iSourceOffset + i];
} }
/// <summary> /// <summary>
@ -421,7 +631,8 @@ namespace KeePassLib.Utility
if(s == null) { Debug.Assert(false); return; } if(s == null) { Debug.Assert(false); return; }
if(pbData == 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) public static byte[] Compress(byte[] pbData)
@ -429,15 +640,21 @@ namespace KeePassLib.Utility
if(pbData == null) throw new ArgumentNullException("pbData"); if(pbData == null) throw new ArgumentNullException("pbData");
if(pbData.Length == 0) return pbData; if(pbData.Length == 0) return pbData;
MemoryStream msCompressed = new MemoryStream(); byte[] pbCompressed;
GZipStream gz = new GZipStream(msCompressed, CompressionMode.Compress); using(MemoryStream msSource = new MemoryStream(pbData, false))
MemoryStream msSource = new MemoryStream(pbData, false); {
MemUtil.CopyStream(msSource, gz); using(MemoryStream msCompressed = new MemoryStream())
gz.Close(); {
msSource.Close(); using(GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Compress))
{
MemUtil.CopyStream(msSource, gz);
}
pbCompressed = msCompressed.ToArray();
}
}
byte[] pbCompressed = msCompressed.ToArray();
msCompressed.Close();
return pbCompressed; return pbCompressed;
} }
@ -446,15 +663,21 @@ namespace KeePassLib.Utility
if(pbCompressed == null) throw new ArgumentNullException("pbCompressed"); if(pbCompressed == null) throw new ArgumentNullException("pbCompressed");
if(pbCompressed.Length == 0) return pbCompressed; if(pbCompressed.Length == 0) return pbCompressed;
MemoryStream msCompressed = new MemoryStream(pbCompressed, false); byte[] pbData;
GZipStream gz = new GZipStream(msCompressed, CompressionMode.Decompress); using(MemoryStream msData = new MemoryStream())
MemoryStream msData = new MemoryStream(); {
MemUtil.CopyStream(gz, msData); using(MemoryStream msCompressed = new MemoryStream(pbCompressed, false))
gz.Close(); {
msCompressed.Close(); using(GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Decompress))
{
MemUtil.CopyStream(gz, msData);
}
}
pbData = msData.ToArray();
}
byte[] pbData = msData.ToArray();
msData.Close();
return pbData; 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 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
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -22,19 +20,21 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; 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.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.Collections;
using KeePassLib.Cryptography.PasswordGenerator; using KeePassLib.Cryptography.PasswordGenerator;
using KeePassLib.Native; using KeePassLib.Native;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Resources;
namespace KeePassLib.Utility namespace KeePassLib.Utility
{ {
@ -221,8 +221,8 @@ namespace KeePassLib.Utility
List<StrEncodingInfo> l = new List<StrEncodingInfo>(); List<StrEncodingInfo> l = new List<StrEncodingInfo>();
l.Add(new StrEncodingInfo(StrEncodingType.Default, l.Add(new StrEncodingInfo(StrEncodingType.Default,
#if KeePassRT #if KeePassUAP
StrUtil.Utf8.WebName, StrUtil.Utf8, 1, null)); "Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
#else #else
#if !KeePassLibSD #if !KeePassLibSD
Encoding.Default.EncodingName, Encoding.Default.EncodingName,
@ -232,12 +232,11 @@ namespace KeePassLib.Utility
Encoding.Default, Encoding.Default,
(uint)Encoding.Default.GetBytes("a").Length, null)); (uint)Encoding.Default.GetBytes("a").Length, null));
#endif #endif
#if !KeePassRT
l.Add(new StrEncodingInfo(StrEncodingType.Ascii, l.Add(new StrEncodingInfo(StrEncodingType.Ascii,
"ASCII", Encoding.ASCII, 1, null)); "ASCII", Encoding.ASCII, 1, null));
l.Add(new StrEncodingInfo(StrEncodingType.Utf7, l.Add(new StrEncodingInfo(StrEncodingType.Utf7,
"Unicode (UTF-7)", Encoding.UTF7, 1, null)); "Unicode (UTF-7)", Encoding.UTF7, 1, null));
#endif
l.Add(new StrEncodingInfo(StrEncodingType.Utf8, l.Add(new StrEncodingInfo(StrEncodingType.Utf8,
"Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF })); "Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
l.Add(new StrEncodingInfo(StrEncodingType.Utf16LE, l.Add(new StrEncodingInfo(StrEncodingType.Utf16LE,
@ -246,7 +245,8 @@ namespace KeePassLib.Utility
l.Add(new StrEncodingInfo(StrEncodingType.Utf16BE, l.Add(new StrEncodingInfo(StrEncodingType.Utf16BE,
"Unicode (UTF-16 BE)", new UnicodeEncoding(true, false), "Unicode (UTF-16 BE)", new UnicodeEncoding(true, false),
2, new byte[] { 0xFE, 0xFF })); 2, new byte[] { 0xFE, 0xFF }));
#if (!KeePassLibSD && !KeePassRT)
#if !KeePassLibSD
l.Add(new StrEncodingInfo(StrEncodingType.Utf32LE, l.Add(new StrEncodingInfo(StrEncodingType.Utf32LE,
"Unicode (UTF-32 LE)", new UTF32Encoding(false, false), "Unicode (UTF-32 LE)", new UTF32Encoding(false, false),
4, new byte[] { 0xFF, 0xFE, 0x0, 0x0 })); 4, new byte[] { 0xFF, 0xFE, 0x0, 0x0 }));
@ -315,7 +315,7 @@ namespace KeePassLib.Utility
str = str.Replace("\'", @"&#39;"); str = str.Replace("\'", @"&#39;");
str = NormalizeNewLines(str, false); str = NormalizeNewLines(str, false);
str = str.Replace("\n", @"<br />"); str = str.Replace("\n", @"<br />\n" );
return str; return str;
} }
@ -358,9 +358,9 @@ namespace KeePassLib.Utility
} }
/// <summary> /// <summary>
/// Split up a command-line into application and argument. /// Split up a command line into application and argument.
/// </summary> /// </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="strApp">Application path.</param>
/// <param name="strArgs">Arguments.</param> /// <param name="strArgs">Arguments.</param>
public static void SplitCommandLine(string strCmdLine, out string strApp, out string strArgs) 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) public static string FormatException(Exception excp)
{ {
string strText = string.Empty; string strText = string.Empty;
string NewLine = "\n";
if(excp.Message != null) if(excp.Message != null)
strText += excp.Message + MessageService.NewLine; strText += excp.Message + NewLine;
#if !KeePassLibSD #if !KeePassLibSD
if(excp.Source != null) if(excp.Source != null)
strText += excp.Source + MessageService.NewLine; strText += excp.Source + NewLine;
#endif #endif
if(excp.StackTrace != null) if(excp.StackTrace != null)
strText += excp.StackTrace + MessageService.NewLine; strText += excp.StackTrace + NewLine;
#if !KeePassLibSD #if !KeePassLibSD
#if !KeePassRT #if !KeePassUAP
if(excp.TargetSite != null) if(excp.TargetSite != null)
strText += excp.TargetSite.ToString() + MessageService.NewLine; strText += excp.TargetSite.ToString() + NewLine;
#endif #endif
if(excp.Data != null) if(excp.Data != null)
{ {
strText += MessageService.NewLine; strText += NewLine;
foreach(DictionaryEntry de in excp.Data) foreach(DictionaryEntry de in excp.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" + strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
MessageService.NewLine; NewLine;
} }
#endif #endif
if(excp.InnerException != null) if(excp.InnerException != null)
{ {
strText += MessageService.NewLine + "Inner:" + MessageService.NewLine; strText += NewLine + "Inner:" + NewLine;
if(excp.InnerException.Message != null) if(excp.InnerException.Message != null)
strText += excp.InnerException.Message + MessageService.NewLine; strText += excp.InnerException.Message + NewLine;
#if !KeePassLibSD #if !KeePassLibSD
if(excp.InnerException.Source != null) if(excp.InnerException.Source != null)
strText += excp.InnerException.Source + MessageService.NewLine; strText += excp.InnerException.Source + NewLine;
#endif #endif
if(excp.InnerException.StackTrace != null) if(excp.InnerException.StackTrace != null)
strText += excp.InnerException.StackTrace + MessageService.NewLine; strText += excp.InnerException.StackTrace + NewLine;
#if !KeePassLibSD #if !KeePassLibSD
#if !KeePassRT #if !KeePassUAP
if(excp.InnerException.TargetSite != null) if(excp.InnerException.TargetSite != null)
strText += excp.InnerException.TargetSite.ToString(); strText += excp.InnerException.TargetSite.ToString();
#endif #endif
if(excp.InnerException.Data != null) if(excp.InnerException.Data != null)
{ {
strText += MessageService.NewLine; strText += NewLine;
foreach(DictionaryEntry de in excp.InnerException.Data) foreach(DictionaryEntry de in excp.InnerException.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" + strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
MessageService.NewLine; NewLine;
} }
#endif #endif
} }
@ -760,7 +760,7 @@ namespace KeePassLib.Utility
return sb.ToString(); return sb.ToString();
} }
private static Regex m_rxNaturalSplit = null; /* private static Regex g_rxNaturalSplit = null;
public static int CompareNaturally(string strX, string strY) public static int CompareNaturally(string strX, string strY)
{ {
Debug.Assert(strX != null); Debug.Assert(strX != null);
@ -771,39 +771,31 @@ namespace KeePassLib.Utility
if(NativeMethods.SupportsStrCmpNaturally) if(NativeMethods.SupportsStrCmpNaturally)
return NativeMethods.StrCmpNaturally(strX, strY); return NativeMethods.StrCmpNaturally(strX, strY);
strX = strX.ToLower(); // Case-insensitive comparison if(g_rxNaturalSplit == null)
strY = strY.ToLower(); g_rxNaturalSplit = new Regex(@"([0-9]+)", RegexOptions.Compiled);
if(m_rxNaturalSplit == null) string[] vPartsX = g_rxNaturalSplit.Split(strX);
m_rxNaturalSplit = new Regex(@"([0-9]+)", string[] vPartsY = g_rxNaturalSplit.Split(strY);
#if KeePassRT
RegexOptions.None);
#else
RegexOptions.Compiled);
#endif
string[] vPartsX = m_rxNaturalSplit.Split(strX); int n = Math.Min(vPartsX.Length, vPartsY.Length);
string[] vPartsY = m_rxNaturalSplit.Split(strY); for(int i = 0; i < n; ++i)
for(int i = 0; i < Math.Min(vPartsX.Length, vPartsY.Length); ++i)
{ {
string strPartX = vPartsX[i], strPartY = vPartsY[i]; string strPartX = vPartsX[i], strPartY = vPartsY[i];
int iPartCompare; int iPartCompare;
#if KeePassLibSD #if KeePassLibSD
ulong uX = 0, uY = 0;
try try
{ {
uX = ulong.Parse(strPartX); ulong uX = ulong.Parse(strPartX);
uY = ulong.Parse(strPartY); ulong uY = ulong.Parse(strPartY);
iPartCompare = uX.CompareTo(uY); iPartCompare = uX.CompareTo(uY);
} }
catch(Exception) { iPartCompare = strPartX.CompareTo(strPartY); } catch(Exception) { iPartCompare = string.Compare(strPartX, strPartY, true); }
#else #else
ulong uX, uY; ulong uX, uY;
if(ulong.TryParse(strPartX, out uX) && ulong.TryParse(strPartY, out uY)) if(ulong.TryParse(strPartX, out uX) && ulong.TryParse(strPartY, out uY))
iPartCompare = uX.CompareTo(uY); iPartCompare = uX.CompareTo(uY);
else iPartCompare = strPartX.CompareTo(strPartY); else iPartCompare = string.Compare(strPartX, strPartY, true);
#endif #endif
if(iPartCompare != 0) return iPartCompare; 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 0;
if(vPartsX.Length < vPartsY.Length) return -1; if(vPartsX.Length < vPartsY.Length) return -1;
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) public static string RemoveAccelerator(string strMenuText)
@ -835,6 +927,40 @@ namespace KeePassLib.Utility
return str; 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) public static string EncodeMenuText(string strText)
{ {
if(strText == null) throw new ArgumentNullException("strText"); if(strText == null) throw new ArgumentNullException("strText");
@ -852,13 +978,12 @@ namespace KeePassLib.Utility
public static bool IsHexString(string str, bool bStrict) public static bool IsHexString(string str, bool bStrict)
{ {
if(str == null) throw new ArgumentNullException("str"); if(str == null) throw new ArgumentNullException("str");
if(str.Length == 0) return true;
foreach(char ch in str) foreach(char ch in str)
{ {
if((ch >= '0') && (ch <= '9')) continue; if((ch >= '0') && (ch <= '9')) continue;
if((ch >= 'a') && (ch <= 'z')) continue; if((ch >= 'a') && (ch <= 'f')) continue;
if((ch >= 'A') && (ch <= 'Z')) continue; if((ch >= 'A') && (ch <= 'F')) continue;
if(bStrict) return false; if(bStrict) return false;
@ -871,8 +996,31 @@ namespace KeePassLib.Utility
return true; 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 #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, public static bool SimplePatternMatch(string strPattern, string strText,
StringComparison sc) 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) public static string AlphaNumericOnly(string str)
{ {
if(string.IsNullOrEmpty(str)) return str; if(string.IsNullOrEmpty(str)) return str;
@ -1074,37 +1252,44 @@ namespace KeePassLib.Utility
public static string VersionToString(ulong uVersion) public static string VersionToString(ulong uVersion)
{ {
return VersionToString(uVersion, false); return VersionToString(uVersion, 1U);
} }
[Obsolete]
public static string VersionToString(ulong uVersion, public static string VersionToString(ulong uVersion,
bool bEnsureAtLeastTwoComp) bool bEnsureAtLeastTwoComp)
{ {
string str = string.Empty; return VersionToString(uVersion, (bEnsureAtLeastTwoComp ? 2U : 1U));
bool bMultiComp = false; }
public static string VersionToString(ulong uVersion, uint uMinComp)
{
StringBuilder sb = new StringBuilder();
uint uComp = 0;
for(int i = 0; i < 4; ++i) for(int i = 0; i < 4; ++i)
{ {
ushort us = (ushort)(uVersion & 0xFFFFUL); if(uVersion == 0UL) break;
if((us != 0) || (str.Length > 0)) ushort us = (ushort)(uVersion >> 48);
{
if(str.Length > 0)
{
str = "." + str;
bMultiComp = true;
}
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)) while(uComp < uMinComp)
str += ".0"; {
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 }; private static readonly byte[] m_pbOptEnt = { 0xA5, 0x74, 0x2E, 0xEC };
@ -1169,7 +1354,7 @@ namespace KeePassLib.Utility
return v; 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) public static string TagsToString(List<string> vTags, bool bForDisplay)
{ {
if(vTags == null) throw new ArgumentNullException("vTags"); if(vTags == null) throw new ArgumentNullException("vTags");
@ -1222,7 +1407,7 @@ namespace KeePassLib.Utility
Array.Reverse(pb); Array.Reverse(pb);
for(int i = 0; i < pb.Length; ++i) pb[i] = (byte)(pb[i] ^ 0x65); 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); return Convert.ToBase64String(pb, Base64FormattingOptions.None);
#else #else
return Convert.ToBase64String(pb); return Convert.ToBase64String(pb);
@ -1382,7 +1567,7 @@ namespace KeePassLib.Utility
if(strMimeType == null) strMimeType = "application/octet-stream"; if(strMimeType == null) strMimeType = "application/octet-stream";
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !KeePassUAP)
return ("data:" + strMimeType + ";base64," + Convert.ToBase64String( return ("data:" + strMimeType + ";base64," + Convert.ToBase64String(
pbData, Base64FormattingOptions.None)); pbData, Base64FormattingOptions.None));
#else #else
@ -1412,12 +1597,7 @@ namespace KeePassLib.Utility
if(bBase64) return Convert.FromBase64String(strData); if(bBase64) return Convert.FromBase64String(strData);
MemoryStream ms = new MemoryStream(); MemoryStream ms = new MemoryStream();
#if KeePassRT
Encoding enc = StrUtil.Utf8;
#else
Encoding enc = Encoding.ASCII; Encoding enc = Encoding.ASCII;
#endif
string[] v = strData.Split('%'); string[] v = strData.Split('%');
byte[] pb = enc.GetBytes(v[0]); byte[] pb = enc.GetBytes(v[0]);

View File

@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager 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 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 it under the terms of the GNU General Public License as published by
@ -18,7 +18,10 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.Text;
using KeePassLib.Interfaces; using KeePassLib.Interfaces;
@ -35,9 +38,29 @@ namespace KeePassLib.Utility
/// </summary> /// </summary>
public const int PwTimeLength = 7; public const int PwTimeLength = 7;
#if !KeePassLibSD
private static string m_strDtfStd = null;
private static string m_strDtfDate = null;
#endif
/// <summary> /// <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, /// 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 /// 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. /// bits, second 6 bits.
/// </summary> /// </summary>
/// <param name="dt"></param> /// <param name="dt"></param>
@ -140,17 +163,118 @@ namespace KeePassLib.Utility
{ {
DateTime dt; DateTime dt;
#if !KeePassLibSD #if KeePassLibSD
if(DateTime.TryParse(strDisplay, out dt)) return dt;
#else
try { dt = DateTime.Parse(strDisplay); return dt; } try { dt = DateTime.Parse(strDisplay); return dt; }
catch(Exception) { } 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 #endif
Debug.Assert(false); Debug.Assert(false);
return DateTime.Now; 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) public static string SerializeUtc(DateTime dt)
{ {
string str = dt.ToUniversalTime().ToString("s"); string str = dt.ToUniversalTime().ToString("s");

View File

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