Keepass Orig KeePass_160826
This commit is contained in:
		| @@ -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,25 +106,25 @@ 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> | ||||||
| 		/// Delete a string. | 		/// Delete a string. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		/// <param name="strField">Name of the string field to delete.</param> | 		/// <param name="strField">Name of the string field to delete.</param> | ||||||
| 		/// <returns>Returns <c>true</c> if the field has been successfully | 		/// <returns>Returns <c>true</c>, if the field has been successfully | ||||||
| 		/// removed, otherwise the return value is <c>false</c>.</returns> | 		/// removed. Otherwise, the return value is <c>false</c>.</returns> | ||||||
| 		/// <exception cref="System.ArgumentNullException">Thrown if the input | 		/// <exception cref="System.ArgumentNullException">Thrown if the input | ||||||
| 		/// 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); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										415
									
								
								src/KeePassLib2Android/Collections/VariantDictionary.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										415
									
								
								src/KeePassLib2Android/Collections/VariantDictionary.cs
									
									
									
									
									
										Normal 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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										251
									
								
								src/KeePassLib2Android/Cryptography/Cipher/ChaCha20Cipher.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								src/KeePassLib2Android/Cryptography/Cipher/ChaCha20Cipher.cs
									
									
									
									
									
										Normal 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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										174
									
								
								src/KeePassLib2Android/Cryptography/Cipher/ChaCha20Engine.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/KeePassLib2Android/Cryptography/Cipher/ChaCha20Engine.cs
									
									
									
									
									
										Normal 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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -40,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; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								src/KeePassLib2Android/Cryptography/Cipher/CtrBlockCipher.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/KeePassLib2Android/Cryptography/Cipher/CtrBlockCipher.cs
									
									
									
									
									
										Normal 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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,182 +17,145 @@ | |||||||
|   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA |   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | ||||||
| */ | */ | ||||||
|  |  | ||||||
| // Implementation of the Salsa20 cipher, based on the eSTREAM submission. | // Implementation of the Salsa20 cipher, based on the eSTREAM | ||||||
|  | // submission by D. J. Bernstein. | ||||||
|  |  | ||||||
| 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 : IDisposable | 	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() | ||||||
| 		{ | 		{ | ||||||
| 			Dispose(false); | 			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"); | ||||||
|  |  | ||||||
|  | 			// Key setup | ||||||
|  | 			m_s[1] = MemUtil.BytesToUInt32(pbKey32, 0); | ||||||
|  | 			m_s[2] = MemUtil.BytesToUInt32(pbKey32, 4); | ||||||
|  | 			m_s[3] = MemUtil.BytesToUInt32(pbKey32, 8); | ||||||
|  | 			m_s[4] = MemUtil.BytesToUInt32(pbKey32, 12); | ||||||
|  | 			m_s[11] = MemUtil.BytesToUInt32(pbKey32, 16); | ||||||
|  | 			m_s[12] = MemUtil.BytesToUInt32(pbKey32, 20); | ||||||
|  | 			m_s[13] = MemUtil.BytesToUInt32(pbKey32, 24); | ||||||
|  | 			m_s[14] = MemUtil.BytesToUInt32(pbKey32, 28); | ||||||
|  | 			m_s[0] = g_sigma[0]; | ||||||
|  | 			m_s[5] = g_sigma[1]; | ||||||
|  | 			m_s[10] = g_sigma[2]; | ||||||
|  | 			m_s[15] = g_sigma[3]; | ||||||
|  |  | ||||||
|  | 			// IV setup | ||||||
|  | 			m_s[6] = MemUtil.BytesToUInt32(pbIV8, 0); | ||||||
|  | 			m_s[7] = MemUtil.BytesToUInt32(pbIV8, 4); | ||||||
|  | 			m_s[8] = 0; // Counter, low | ||||||
|  | 			m_s[9] = 0; // Counter, high | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		public void Dispose() | 		protected override void Dispose(bool bDisposing) | ||||||
| 		{ | 		{ | ||||||
| 			Dispose(true); | 			MemUtil.ZeroArray<uint>(m_s); | ||||||
| 			GC.SuppressFinalize(this); | 			MemUtil.ZeroArray<uint>(m_x); | ||||||
|  |  | ||||||
|  | 			base.Dispose(bDisposing); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private void Dispose(bool bDisposing) | 		protected override void NextBlock(byte[] pBlock) | ||||||
| 		{ | 		{ | ||||||
| 			// Clear sensitive data | 			if(pBlock == null) throw new ArgumentNullException("pBlock"); | ||||||
| 			Array.Clear(m_state, 0, m_state.Length); | 			if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock"); | ||||||
| 			Array.Clear(m_x, 0, m_x.Length); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void NextOutput() | 			// x is a local alias for the working buffer; with this, | ||||||
| 		{ | 			// the compiler/runtime might remove some checks | ||||||
| 			uint[] x = m_x; // Local alias for working buffer | 			uint[] x = m_x; | ||||||
|  | 			if(x == null) throw new InvalidOperationException(); | ||||||
| 			// Compiler/runtime might remove array bound checks after this |  | ||||||
| 			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 | ||||||
| 			{ | 			{ | ||||||
| 				for(int i = 0; i < 10; ++i) // (int i = 20; i > 0; i -= 2) | 				// 10 * 8 quarter rounds = 20 rounds | ||||||
|  | 				for(int i = 0; i < 10; ++i) | ||||||
| 				{ | 				{ | ||||||
| 					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) | 				for(int i = 0; i < 16; ++i) x[i] += s[i]; | ||||||
| 					x[i] += m_state[i]; |  | ||||||
|  |  | ||||||
| 				for(int i = 0; i < 16; ++i) | 				for(int i = 0; i < 16; ++i) | ||||||
| 				{ | 				{ | ||||||
| 					m_output[i << 2] = (byte)x[i]; | 					int i4 = i << 2; | ||||||
| 					m_output[(i << 2) + 1] = (byte)(x[i] >> 8); | 					uint xi = x[i]; | ||||||
| 					m_output[(i << 2) + 2] = (byte)(x[i] >> 16); |  | ||||||
| 					m_output[(i << 2) + 3] = (byte)(x[i] >> 24); | 					pBlock[i4] = (byte)xi; | ||||||
|  | 					pBlock[i4 + 1] = (byte)(xi >> 8); | ||||||
|  | 					pBlock[i4 + 2] = (byte)(xi >> 16); | ||||||
|  | 					pBlock[i4 + 3] = (byte)(xi >> 24); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				m_outputPos = 0; | 				++s[8]; | ||||||
| 				++m_state[8]; | 				if(s[8] == 0) ++s[9]; | ||||||
| 				if(m_state[8] == 0) ++m_state[9]; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private static uint Rotl32(uint x, int b) |  | ||||||
| 		{ |  | ||||||
| 			unchecked |  | ||||||
| 			{ |  | ||||||
| 				return ((x << b) | (x >> (32 - b))); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private static uint U8To32Little(byte[] pb, int iOffset) |  | ||||||
| 		{ |  | ||||||
| 			unchecked |  | ||||||
| 			{ |  | ||||||
| 				return ((uint)pb[iOffset] | ((uint)pb[iOffset + 1] << 8) | |  | ||||||
| 					((uint)pb[iOffset + 2] << 16) | ((uint)pb[iOffset + 3] << 24)); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void KeySetup(byte[] k) |  | ||||||
| 		{ |  | ||||||
| 			if(k == null) throw new ArgumentNullException("k"); |  | ||||||
| 			if(k.Length != 32) throw new ArgumentException(); |  | ||||||
|  |  | ||||||
| 			m_state[1] = U8To32Little(k, 0); |  | ||||||
| 			m_state[2] = U8To32Little(k, 4); |  | ||||||
| 			m_state[3] = U8To32Little(k, 8); |  | ||||||
| 			m_state[4] = U8To32Little(k, 12); |  | ||||||
| 			m_state[11] = U8To32Little(k, 16); |  | ||||||
| 			m_state[12] = U8To32Little(k, 20); |  | ||||||
| 			m_state[13] = U8To32Little(k, 24); |  | ||||||
| 			m_state[14] = U8To32Little(k, 28); |  | ||||||
| 			m_state[0] = m_sigma[0]; |  | ||||||
| 			m_state[5] = m_sigma[1]; |  | ||||||
| 			m_state[10] = m_sigma[2]; |  | ||||||
| 			m_state[15] = m_sigma[3]; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		private void IvSetup(byte[] pbIV) |  | ||||||
| 		{ |  | ||||||
| 			if(pbIV == null) throw new ArgumentNullException("pbIV"); |  | ||||||
| 			if(pbIV.Length != 8) throw new ArgumentException(); |  | ||||||
|  |  | ||||||
| 			m_state[6] = U8To32Little(pbIV, 0); |  | ||||||
| 			m_state[7] = U8To32Little(pbIV, 4); |  | ||||||
| 			m_state[8] = 0; |  | ||||||
| 			m_state[9] = 0; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		public void Encrypt(byte[] m, int nByteCount, bool bXor) |  | ||||||
| 		{ |  | ||||||
| 			if(m == null) throw new ArgumentNullException("m"); |  | ||||||
| 			if(nByteCount > m.Length) throw new ArgumentException(); |  | ||||||
|  |  | ||||||
| 			int nBytesRem = nByteCount, nOffset = 0; |  | ||||||
| 			while(nBytesRem > 0) |  | ||||||
| 			{ |  | ||||||
| 				Debug.Assert((m_outputPos >= 0) && (m_outputPos <= 64)); |  | ||||||
| 				if(m_outputPos == 64) NextOutput(); |  | ||||||
| 				Debug.Assert(m_outputPos < 64); |  | ||||||
|  |  | ||||||
| 				int nCopy = Math.Min(64 - m_outputPos, nBytesRem); |  | ||||||
|  |  | ||||||
| 				if(bXor) MemUtil.XorArray(m_output, m_outputPos, m, nOffset, nCopy); |  | ||||||
| 				else Array.Copy(m_output, m_outputPos, m, nOffset, nCopy); |  | ||||||
|  |  | ||||||
| 				m_outputPos += nCopy; |  | ||||||
| 				nBytesRem -= nCopy; |  | ||||||
| 				nOffset += nCopy; |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ namespace KeePassLib.Cryptography.Cipher | |||||||
| 		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 | ||||||
| @@ -52,12 +52,16 @@ namespace KeePassLib.Cryptography.Cipher | |||||||
| 		{ | 		{ | ||||||
| 			get | 			get | ||||||
| 			{ | 			{ | ||||||
| 				if(m_uuidAes == null) | 				PwUuid pu = g_uuidAes; | ||||||
| 					m_uuidAes = new PwUuid(new byte[]{ | 				if(pu == null) | ||||||
|  | 				{ | ||||||
|  | 					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; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -72,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) | ||||||
| 		{ | 		{ | ||||||
|   | |||||||
| @@ -40,14 +40,14 @@ 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 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 | ||||||
| @@ -55,11 +55,11 @@ namespace KeePassLib.Cryptography | |||||||
| 				CryptoRandom cr; | 				CryptoRandom cr; | ||||||
| 				lock(g_oSyncRoot) | 				lock(g_oSyncRoot) | ||||||
| 				{ | 				{ | ||||||
| 					cr = m_pInstance; | 					cr = g_pInstance; | ||||||
| 					if(cr == null) | 					if(cr == null) | ||||||
| 					{ | 					{ | ||||||
| 						cr = new CryptoRandom(); | 						cr = new CryptoRandom(); | ||||||
| 						m_pInstance = cr; | 						g_pInstance = cr; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| @@ -90,10 +90,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()); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -109,32 +111,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 | ||||||
| 				SHA256Managed shaNew = new SHA256Managed(); | 				using(SHA256Managed shaNew = new SHA256Managed()) | ||||||
| #else | #else | ||||||
| 				SHA512Managed shaNew = new SHA512Managed(); | 				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[] pbCmp = new byte[cbPool + cbNew]; | ||||||
|  | 				Array.Copy(m_pbEntropyPool, pbCmp, cbPool); | ||||||
|  | 				Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew); | ||||||
|  |  | ||||||
|  | 				MemUtil.ZeroByteArray(m_pbEntropyPool); | ||||||
|  |  | ||||||
| 				byte[] pbFinal = ms.ToArray(); |  | ||||||
| #if KeePassLibSD | #if KeePassLibSD | ||||||
| 				SHA256Managed shaPool = new SHA256Managed(); | 				using(SHA256Managed shaPool = new SHA256Managed()) | ||||||
| #else | #else | ||||||
| 				Debug.Assert(pbFinal.Length == (64 + pbNewData.Length)); | 				using(SHA512Managed shaPool = new SHA512Managed()) | ||||||
| 				SHA512Managed shaPool = new SHA512Managed(); |  | ||||||
| #endif | #endif | ||||||
| 				m_pbEntropyPool = shaPool.ComputeHash(pbFinal); | 				{ | ||||||
|  | 					m_pbEntropyPool = shaPool.ComputeHash(pbCmp); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				MemUtil.ZeroByteArray(pbCmp); | ||||||
| 			} | 			} | ||||||
| 			ms.Close(); |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private static byte[] GetSystemData(Random rWeak) | 		private static byte[] GetSystemData(Random rWeak) | ||||||
| @@ -142,11 +152,11 @@ 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 | #if !KeePassLibSD | ||||||
| 			// In try-catch for systems without GUI; | 			// In try-catch for systems without GUI; | ||||||
| @@ -154,79 +164,79 @@ namespace KeePassLib.Cryptography | |||||||
| 			try | 			try | ||||||
| 			{ | 			{ | ||||||
| 				Point pt = Cursor.Position; | 				Point pt = Cursor.Position; | ||||||
| 				pb = MemUtil.UInt32ToBytes((uint)pt.X); | 				pb = MemUtil.Int32ToBytes(pt.X); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt32ToBytes((uint)pt.Y); | 				pb = MemUtil.Int32ToBytes(pt.Y); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 			} | 			} | ||||||
| 			catch(Exception) { } | 			catch(Exception) { } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 			pb = MemUtil.UInt32ToBytes((uint)rWeak.Next()); | 			pb = MemUtil.Int32ToBytes(rWeak.Next()); | ||||||
| 			ms.Write(pb, 0, pb.Length); | 			MemUtil.Write(ms, pb); | ||||||
|  |  | ||||||
| 			pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID()); | 			pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID()); | ||||||
| 			ms.Write(pb, 0, pb.Length); | 			MemUtil.Write(ms, pb); | ||||||
|  |  | ||||||
| 			try | 			try | ||||||
| 			{ | 			{ | ||||||
| 				pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount); | 				pb = MemUtil.Int32ToBytes(Environment.ProcessorCount); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
|  |  | ||||||
| #if KeePassUAP | #if KeePassUAP | ||||||
| 				Version v = EnvironmentExt.OSVersion.Version; | 				Version v = EnvironmentExt.OSVersion.Version; | ||||||
| #else | #else | ||||||
| 				Version v = Environment.OSVersion.Version; | 				Version v = Environment.OSVersion.Version; | ||||||
| #endif | #endif | ||||||
| 				pb = MemUtil.UInt32ToBytes((uint)v.GetHashCode()); | 				pb = MemUtil.Int32ToBytes(v.GetHashCode()); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
|  |  | ||||||
| #if !KeePassUAP | #if !KeePassUAP | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)Environment.WorkingSet); | 				pb = MemUtil.Int64ToBytes(Environment.WorkingSet); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| #endif | #endif | ||||||
| 			} | 			} | ||||||
| 			catch(Exception) { Debug.Assert(false); } | 			catch(Exception) { Debug.Assert(false); } | ||||||
|  |  | ||||||
| #if KeePassUAP | #if KeePassUAP | ||||||
| 			pb = DiagnosticsExt.GetProcessEntropy(); | 			pb = DiagnosticsExt.GetProcessEntropy(); | ||||||
| 			ms.Write(pb, 0, pb.Length); | 			MemUtil.Write(ms, pb); | ||||||
| #elif !KeePassLibSD | #elif !KeePassLibSD | ||||||
| 			Process p = null; | 			Process p = null; | ||||||
| 			try | 			try | ||||||
| 			{ | 			{ | ||||||
| 				p = Process.GetCurrentProcess(); | 				p = Process.GetCurrentProcess(); | ||||||
|  |  | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.Handle.ToInt64()); | 				pb = MemUtil.Int64ToBytes(p.Handle.ToInt64()); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt32ToBytes((uint)p.HandleCount); | 				pb = MemUtil.Int32ToBytes(p.HandleCount); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt32ToBytes((uint)p.Id); | 				pb = MemUtil.Int32ToBytes(p.Id); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.NonpagedSystemMemorySize64); | 				pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.PagedMemorySize64); | 				pb = MemUtil.Int64ToBytes(p.PagedMemorySize64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.PagedSystemMemorySize64); | 				pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.PeakPagedMemorySize64); | 				pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.PeakVirtualMemorySize64); | 				pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.PeakWorkingSet64); | 				pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.PrivateMemorySize64); | 				pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.StartTime.ToBinary()); | 				pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary()); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.VirtualMemorySize64); | 				pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
| 				pb = MemUtil.UInt64ToBytes((ulong)p.WorkingSet64); | 				pb = MemUtil.Int64ToBytes(p.WorkingSet64); | ||||||
| 				ms.Write(pb, 0, pb.Length); | 				MemUtil.Write(ms, pb); | ||||||
|  |  | ||||||
| 				// Not supported in Mono 1.2.6: | 				// Not supported in Mono 1.2.6: | ||||||
| 				// pb = MemUtil.UInt32ToBytes((uint)p.SessionId); | 				// pb = MemUtil.UInt32ToBytes((uint)p.SessionId); | ||||||
| 				// ms.Write(pb, 0, pb.Length); | 				// MemUtil.Write(ms, pb); | ||||||
| 			} | 			} | ||||||
| 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); } | 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); } | ||||||
| 			finally | 			finally | ||||||
| @@ -237,7 +247,7 @@ namespace KeePassLib.Cryptography | |||||||
| #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(); | ||||||
| @@ -256,28 +266,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> | ||||||
| @@ -289,29 +302,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 && !KeePassUAP) | 				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; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ using System.Security.Cryptography; | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| using KeePassLib.Cryptography.Cipher; | using KeePassLib.Cryptography.Cipher; | ||||||
|  | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| namespace KeePassLib.Cryptography | namespace KeePassLib.Cryptography | ||||||
| { | { | ||||||
| @@ -40,6 +41,7 @@ namespace KeePassLib.Cryptography | |||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// A variant of the ARCFour algorithm (RC4 incompatible). | 		/// A variant of the ARCFour algorithm (RC4 incompatible). | ||||||
|  | 		/// Insecure; for backward compatibility only. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		ArcFourVariant = 1, | 		ArcFourVariant = 1, | ||||||
|  |  | ||||||
| @@ -48,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> | ||||||
| @@ -59,45 +66,68 @@ 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. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		/// <param name="genAlgorithm">Algorithm to use.</param> | 		/// <param name="a">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"); } | ||||||
|  |  | ||||||
| 			Debug.Assert(pbKey != null); if(pbKey == null) throw new ArgumentNullException("pbKey"); | 			int cbKey = pbKey.Length; | ||||||
|  | 			if(cbKey <= 0) | ||||||
|  | 			{ | ||||||
|  | 				Debug.Assert(false); // Need at least one byte | ||||||
|  | 				throw new ArgumentOutOfRangeException("pbKey"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			uint uKeyLen = (uint)pbKey.Length; | 			m_crsAlgorithm = a; | ||||||
| 			Debug.Assert(uKeyLen != 0); if(uKeyLen == 0) throw new ArgumentException(); |  | ||||||
|  |  | ||||||
| 			if(genAlgorithm == CrsAlgorithm.ArcFourVariant) | 			if(a == CrsAlgorithm.ChaCha20) | ||||||
|  | 			{ | ||||||
|  | 				byte[] pbKey32 = new byte[32]; | ||||||
|  | 				byte[] pbIV12 = new byte[12]; | ||||||
|  |  | ||||||
|  | 				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); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				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 | ||||||
|  |  | ||||||
|  | 				m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8); | ||||||
|  | 			} | ||||||
|  | 			else if(a == 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]); | ||||||
|  |  | ||||||
| @@ -106,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[8] { 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"); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -135,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]; | ||||||
| @@ -157,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; | ||||||
| @@ -167,14 +194,7 @@ 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 | ||||||
|   | |||||||
							
								
								
									
										126
									
								
								src/KeePassLib2Android/Cryptography/CryptoUtil.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/KeePassLib2Android/Cryptography/CryptoUtil.cs
									
									
									
									
									
										Normal 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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										229
									
								
								src/KeePassLib2Android/Cryptography/Hash/Blake2b.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/KeePassLib2Android/Cryptography/Hash/Blake2b.cs
									
									
									
									
									
										Normal 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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -34,7 +34,7 @@ namespace KeePassLib.Cryptography | |||||||
| 	public sealed class HashingStreamEx : Stream | 	public sealed class HashingStreamEx : Stream | ||||||
| 	{ | 	{ | ||||||
| 		private Stream m_sBaseStream; | 		private Stream m_sBaseStream; | ||||||
| 		private bool m_bWriting; | 		private readonly bool m_bWriting; | ||||||
| 		private HashAlgorithm m_hash; | 		private HashAlgorithm m_hash; | ||||||
|  |  | ||||||
| 		private byte[] m_pbFinalHash = null; | 		private byte[] m_pbFinalHash = null; | ||||||
| @@ -67,7 +67,7 @@ namespace KeePassLib.Cryptography | |||||||
| 		public override long Position | 		public override long Position | ||||||
| 		{ | 		{ | ||||||
| 			get { return m_sBaseStream.Position; } | 			get { return m_sBaseStream.Position; } | ||||||
| 			set { throw new NotSupportedException(); } | 			set { Debug.Assert(false); throw new NotSupportedException(); } | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm) | 		public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm) | ||||||
| @@ -114,7 +114,7 @@ namespace KeePassLib.Cryptography | |||||||
| 			{ | 			{ | ||||||
| 				try | 				try | ||||||
| 				{ | 				{ | ||||||
| 					m_hash.TransformFinalBlock(new byte[0], 0, 0); | 					m_hash.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0); | ||||||
|  |  | ||||||
| 					m_pbFinalHash = m_hash.Hash; | 					m_pbFinalHash = m_hash.Hash; | ||||||
| 				} | 				} | ||||||
|   | |||||||
							
								
								
									
										270
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/AesKdf.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/AesKdf.cs
									
									
									
									
									
										Normal 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; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		internal 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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										144
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/Argon2Kdf.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/Argon2Kdf.cs
									
									
									
									
									
										Normal 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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										142
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/KdfEngine.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/KdfEngine.cs
									
									
									
									
									
										Normal 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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										96
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/KdfPool.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/KeePassLib2Android/Cryptography/KeyDerivation/KdfPool.cs
									
									
									
									
									
										Normal 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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -19,10 +19,12 @@ | |||||||
|  |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Text; |  | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
|  | using System.Security.Cryptography; | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
| using KeePassLib.Security; | using KeePassLib.Security; | ||||||
|  | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| namespace KeePassLib.Cryptography.PasswordGenerator | namespace KeePassLib.Cryptography.PasswordGenerator | ||||||
| { | { | ||||||
| @@ -62,16 +64,20 @@ namespace KeePassLib.Cryptography.PasswordGenerator | |||||||
|  |  | ||||||
| 		private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy) | 		private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy) | ||||||
| 		{ | 		{ | ||||||
| 			byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(256); | 			byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(128); | ||||||
|  |  | ||||||
| 			// Mix in additional entropy | 			// Mix in additional entropy | ||||||
|  | 			Debug.Assert(pbKey.Length >= 64); | ||||||
| 			if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0)) | 			if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0)) | ||||||
| 			{ | 			{ | ||||||
| 				for(int nKeyPos = 0; nKeyPos < pbKey.Length; ++nKeyPos) | 				using(SHA512Managed h = new SHA512Managed()) | ||||||
| 					pbKey[nKeyPos] ^= pbAdditionalEntropy[nKeyPos % pbAdditionalEntropy.Length]; | 				{ | ||||||
|  | 					byte[] pbHash = h.ComputeHash(pbAdditionalEntropy); | ||||||
|  | 					MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			return new CryptoRandomStream(CrsAlgorithm.Salsa20, pbKey); | 			return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		internal static char GenerateCharacter(PwProfile pwProfile, | 		internal static char GenerateCharacter(PwProfile pwProfile, | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ using System; | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Globalization; | using System.Globalization; | ||||||
|  | using System.IO; | ||||||
| using System.Security; | using System.Security; | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| @@ -33,6 +34,8 @@ using System.Security.Cryptography; | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| using KeePassLib.Cryptography.Cipher; | using KeePassLib.Cryptography.Cipher; | ||||||
|  | using KeePassLib.Cryptography.Hash; | ||||||
|  | using KeePassLib.Cryptography.KeyDerivation; | ||||||
| using KeePassLib.Keys; | using KeePassLib.Keys; | ||||||
| using KeePassLib.Native; | using KeePassLib.Native; | ||||||
| using KeePassLib.Resources; | using KeePassLib.Resources; | ||||||
| @@ -45,17 +48,6 @@ using KeePassLib.Utility; | |||||||
|  |  | ||||||
| namespace KeePassLib.Cryptography | namespace KeePassLib.Cryptography | ||||||
| { | { | ||||||
| 	/* /// <summary> |  | ||||||
| 	/// Return values of the <c>SelfTest.Perform</c> method. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public enum SelfTestResult |  | ||||||
| 	{ |  | ||||||
| 		Success = 0, |  | ||||||
| 		RijndaelEcbError = 1, |  | ||||||
| 		Salsa20Error = 2, |  | ||||||
| 		NativeKeyTransformationError = 3 |  | ||||||
| 	} */ |  | ||||||
|  |  | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Class containing self-test methods. | 	/// Class containing self-test methods. | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| @@ -70,6 +62,10 @@ namespace KeePassLib.Cryptography | |||||||
|  |  | ||||||
| 			TestRijndael(); | 			TestRijndael(); | ||||||
| 			TestSalsa20(); | 			TestSalsa20(); | ||||||
|  | 			TestChaCha20(); | ||||||
|  | 			TestBlake2b(); | ||||||
|  | 			TestArgon2(); | ||||||
|  | 			TestHmac(); | ||||||
|  |  | ||||||
| 			TestNativeKeyTransform(); | 			TestNativeKeyTransform(); | ||||||
| 			 | 			 | ||||||
| @@ -92,14 +88,14 @@ namespace KeePassLib.Cryptography | |||||||
| 		internal static void TestFipsComplianceProblems() | 		internal static void TestFipsComplianceProblems() | ||||||
| 		{ | 		{ | ||||||
| #if !KeePassUAP | #if !KeePassUAP | ||||||
| 			try { new RijndaelManaged(); } | 			try { using(RijndaelManaged r = new RijndaelManaged()) { } } | ||||||
| 			catch(Exception exAes) | 			catch(Exception exAes) | ||||||
| 			{ | 			{ | ||||||
| 				throw new SecurityException("AES/Rijndael: " + exAes.Message); | 				throw new SecurityException("AES/Rijndael: " + exAes.Message); | ||||||
| 			} | 			} | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 			try { new SHA256Managed(); } | 			try { using(SHA256Managed h = new SHA256Managed()) { } } | ||||||
| 			catch(Exception exSha256) | 			catch(Exception exSha256) | ||||||
| 			{ | 			{ | ||||||
| 				throw new SecurityException("SHA-256: " + exSha256.Message); | 				throw new SecurityException("SHA-256: " + exSha256.Message); | ||||||
| @@ -126,7 +122,7 @@ namespace KeePassLib.Cryptography | |||||||
| 			AesEngine r = new AesEngine(); | 			AesEngine r = new AesEngine(); | ||||||
| 			r.Init(true, new KeyParameter(pbTestKey)); | 			r.Init(true, new KeyParameter(pbTestKey)); | ||||||
| 			if(r.GetBlockSize() != pbTestData.Length) | 			if(r.GetBlockSize() != pbTestData.Length) | ||||||
| 				throw new SecurityException(KLRes.EncAlgorithmAes + " (BS)."); | 				throw new SecurityException("AES (BC)"); | ||||||
| 			r.ProcessBlock(pbTestData, 0, pbTestData, 0); | 			r.ProcessBlock(pbTestData, 0, pbTestData, 0); | ||||||
| #else | #else | ||||||
| 			RijndaelManaged r = new RijndaelManaged(); | 			RijndaelManaged r = new RijndaelManaged(); | ||||||
| @@ -147,13 +143,14 @@ namespace KeePassLib.Cryptography | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 			if(!MemUtil.ArraysEqual(pbTestData, pbReferenceCT)) | 			if(!MemUtil.ArraysEqual(pbTestData, pbReferenceCT)) | ||||||
| 				throw new SecurityException(KLRes.EncAlgorithmAes + "."); | 				throw new SecurityException("AES"); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private static void TestSalsa20() | 		private static void TestSalsa20() | ||||||
| 		{ | 		{ | ||||||
|  | #if DEBUG | ||||||
| 			// Test values from official set 6, vector 3 | 			// Test values from official set 6, vector 3 | ||||||
| 			byte[] pbKey= new byte[32] { | 			byte[] pbKey = new byte[32] { | ||||||
| 				0x0F, 0x62, 0xB5, 0x08, 0x5B, 0xAE, 0x01, 0x54, | 				0x0F, 0x62, 0xB5, 0x08, 0x5B, 0xAE, 0x01, 0x54, | ||||||
| 				0xA7, 0xFA, 0x4D, 0xA0, 0xF3, 0x46, 0x99, 0xEC, | 				0xA7, 0xFA, 0x4D, 0xA0, 0xF3, 0x46, 0x99, 0xEC, | ||||||
| 				0x3F, 0x92, 0xE5, 0x38, 0x8B, 0xDE, 0x31, 0x84, | 				0x3F, 0x92, 0xE5, 0x38, 0x8B, 0xDE, 0x31, 0x84, | ||||||
| @@ -168,12 +165,11 @@ namespace KeePassLib.Cryptography | |||||||
|  |  | ||||||
| 			byte[] pb = new byte[16]; | 			byte[] pb = new byte[16]; | ||||||
| 			Salsa20Cipher c = new Salsa20Cipher(pbKey, pbIV); | 			Salsa20Cipher c = new Salsa20Cipher(pbKey, pbIV); | ||||||
| 			c.Encrypt(pb, pb.Length, false); | 			c.Encrypt(pb, 0, pb.Length); | ||||||
| 			if(!MemUtil.ArraysEqual(pb, pbExpected)) | 			if(!MemUtil.ArraysEqual(pb, pbExpected)) | ||||||
| 				throw new SecurityException("Salsa20-1"); | 				throw new SecurityException("Salsa20-1"); | ||||||
|  |  | ||||||
| #if DEBUG | 			// Extended test | ||||||
| 			// Extended test in debug mode |  | ||||||
| 			byte[] pbExpected2 = new byte[16] { | 			byte[] pbExpected2 = new byte[16] { | ||||||
| 				0xAB, 0xF3, 0x9A, 0x21, 0x0E, 0xEE, 0x89, 0x59, | 				0xAB, 0xF3, 0x9A, 0x21, 0x0E, 0xEE, 0x89, 0x59, | ||||||
| 				0x8B, 0x71, 0x33, 0x37, 0x70, 0x56, 0xC2, 0xFE | 				0x8B, 0x71, 0x33, 0x37, 0x70, 0x56, 0xC2, 0xFE | ||||||
| @@ -185,13 +181,14 @@ namespace KeePassLib.Cryptography | |||||||
|  |  | ||||||
| 			Random r = new Random(); | 			Random r = new Random(); | ||||||
| 			int nPos = Salsa20ToPos(c, r, pb.Length, 65536); | 			int nPos = Salsa20ToPos(c, r, pb.Length, 65536); | ||||||
| 			c.Encrypt(pb, pb.Length, false); | 			Array.Clear(pb, 0, pb.Length); | ||||||
|  | 			c.Encrypt(pb, 0, pb.Length); | ||||||
| 			if(!MemUtil.ArraysEqual(pb, pbExpected2)) | 			if(!MemUtil.ArraysEqual(pb, pbExpected2)) | ||||||
| 				throw new SecurityException("Salsa20-2"); | 				throw new SecurityException("Salsa20-2"); | ||||||
|  |  | ||||||
| 			nPos = Salsa20ToPos(c, r, nPos + pb.Length, 131008); | 			nPos = Salsa20ToPos(c, r, nPos + pb.Length, 131008); | ||||||
| 			Array.Clear(pb, 0, pb.Length); | 			Array.Clear(pb, 0, pb.Length); | ||||||
| 			c.Encrypt(pb, pb.Length, true); | 			c.Encrypt(pb, 0, pb.Length); | ||||||
| 			if(!MemUtil.ArraysEqual(pb, pbExpected3)) | 			if(!MemUtil.ArraysEqual(pb, pbExpected3)) | ||||||
| 				throw new SecurityException("Salsa20-3"); | 				throw new SecurityException("Salsa20-3"); | ||||||
|  |  | ||||||
| @@ -200,8 +197,8 @@ namespace KeePassLib.Cryptography | |||||||
| 			for(int i = 0; i < nRounds; ++i) | 			for(int i = 0; i < nRounds; ++i) | ||||||
| 			{ | 			{ | ||||||
| 				byte[] z = new byte[32]; | 				byte[] z = new byte[32]; | ||||||
| 				c = new Salsa20Cipher(z, BitConverter.GetBytes((long)i)); | 				c = new Salsa20Cipher(z, MemUtil.Int64ToBytes(i)); | ||||||
| 				c.Encrypt(z, z.Length, true); | 				c.Encrypt(z, 0, z.Length); | ||||||
| 				d[MemUtil.ByteArrayToHexString(z)] = true; | 				d[MemUtil.ByteArrayToHexString(z)] = true; | ||||||
| 			} | 			} | ||||||
| 			if(d.Count != nRounds) throw new SecurityException("Salsa20-4"); | 			if(d.Count != nRounds) throw new SecurityException("Salsa20-4"); | ||||||
| @@ -218,7 +215,7 @@ namespace KeePassLib.Cryptography | |||||||
| 			{ | 			{ | ||||||
| 				int x = r.Next(1, 513); | 				int x = r.Next(1, 513); | ||||||
| 				int nGen = Math.Min(nTargetPos - nPos, x); | 				int nGen = Math.Min(nTargetPos - nPos, x); | ||||||
| 				c.Encrypt(pb, nGen, r.Next(0, 2) == 0); | 				c.Encrypt(pb, 0, nGen); | ||||||
| 				nPos += nGen; | 				nPos += nGen; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -226,6 +223,475 @@ namespace KeePassLib.Cryptography | |||||||
| 		} | 		} | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | 		private static void TestChaCha20() | ||||||
|  | 		{ | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// Test vector from RFC 7539, section 2.3.2 | ||||||
|  |  | ||||||
|  | 			byte[] pbKey = new byte[32]; | ||||||
|  | 			for(int i = 0; i < 32; ++i) pbKey[i] = (byte)i; | ||||||
|  |  | ||||||
|  | 			byte[] pbIV = new byte[12]; | ||||||
|  | 			pbIV[3] = 0x09; | ||||||
|  | 			pbIV[7] = 0x4A; | ||||||
|  |  | ||||||
|  | 			byte[] pbExpc = new byte[64] { | ||||||
|  | 				0x10, 0xF1, 0xE7, 0xE4, 0xD1, 0x3B, 0x59, 0x15, | ||||||
|  | 				0x50, 0x0F, 0xDD, 0x1F, 0xA3, 0x20, 0x71, 0xC4, | ||||||
|  | 				0xC7, 0xD1, 0xF4, 0xC7, 0x33, 0xC0, 0x68, 0x03, | ||||||
|  | 				0x04, 0x22, 0xAA, 0x9A, 0xC3, 0xD4, 0x6C, 0x4E, | ||||||
|  | 				0xD2, 0x82, 0x64, 0x46, 0x07, 0x9F, 0xAA, 0x09, | ||||||
|  | 				0x14, 0xC2, 0xD7, 0x05, 0xD9, 0x8B, 0x02, 0xA2, | ||||||
|  | 				0xB5, 0x12, 0x9C, 0xD1, 0xDE, 0x16, 0x4E, 0xB9, | ||||||
|  | 				0xCB, 0xD0, 0x83, 0xE8, 0xA2, 0x50, 0x3C, 0x4E | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			byte[] pb = new byte[64]; | ||||||
|  |  | ||||||
|  | 			using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey, pbIV)) | ||||||
|  | 			{ | ||||||
|  | 				c.Seek(64, SeekOrigin.Begin); // Skip first block | ||||||
|  | 				c.Encrypt(pb, 0, pb.Length); | ||||||
|  |  | ||||||
|  | 				if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 					throw new SecurityException("ChaCha20-1"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | #if DEBUG | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// Test vector from RFC 7539, section 2.4.2 | ||||||
|  |  | ||||||
|  | 			pbIV[3] = 0; | ||||||
|  |  | ||||||
|  | 			pb = StrUtil.Utf8.GetBytes("Ladies and Gentlemen of the clas" + | ||||||
|  | 				@"s of '99: If I could offer you only one tip for " + | ||||||
|  | 				@"the future, sunscreen would be it."); | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[] { | ||||||
|  | 				0x6E, 0x2E, 0x35, 0x9A, 0x25, 0x68, 0xF9, 0x80, | ||||||
|  | 				0x41, 0xBA, 0x07, 0x28, 0xDD, 0x0D, 0x69, 0x81, | ||||||
|  | 				0xE9, 0x7E, 0x7A, 0xEC, 0x1D, 0x43, 0x60, 0xC2, | ||||||
|  | 				0x0A, 0x27, 0xAF, 0xCC, 0xFD, 0x9F, 0xAE, 0x0B, | ||||||
|  | 				0xF9, 0x1B, 0x65, 0xC5, 0x52, 0x47, 0x33, 0xAB, | ||||||
|  | 				0x8F, 0x59, 0x3D, 0xAB, 0xCD, 0x62, 0xB3, 0x57, | ||||||
|  | 				0x16, 0x39, 0xD6, 0x24, 0xE6, 0x51, 0x52, 0xAB, | ||||||
|  | 				0x8F, 0x53, 0x0C, 0x35, 0x9F, 0x08, 0x61, 0xD8, | ||||||
|  | 				0x07, 0xCA, 0x0D, 0xBF, 0x50, 0x0D, 0x6A, 0x61, | ||||||
|  | 				0x56, 0xA3, 0x8E, 0x08, 0x8A, 0x22, 0xB6, 0x5E, | ||||||
|  | 				0x52, 0xBC, 0x51, 0x4D, 0x16, 0xCC, 0xF8, 0x06, | ||||||
|  | 				0x81, 0x8C, 0xE9, 0x1A, 0xB7, 0x79, 0x37, 0x36, | ||||||
|  | 				0x5A, 0xF9, 0x0B, 0xBF, 0x74, 0xA3, 0x5B, 0xE6, | ||||||
|  | 				0xB4, 0x0B, 0x8E, 0xED, 0xF2, 0x78, 0x5E, 0x42, | ||||||
|  | 				0x87, 0x4D | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			byte[] pb64 = new byte[64]; | ||||||
|  |  | ||||||
|  | 			using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey, pbIV)) | ||||||
|  | 			{ | ||||||
|  | 				c.Encrypt(pb64, 0, pb64.Length); // Skip first block | ||||||
|  | 				c.Encrypt(pb, 0, pb.Length); | ||||||
|  |  | ||||||
|  | 				if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 					throw new SecurityException("ChaCha20-2"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// Test vector from RFC 7539, appendix A.2 #2 | ||||||
|  |  | ||||||
|  | 			Array.Clear(pbKey, 0, pbKey.Length); | ||||||
|  | 			pbKey[31] = 1; | ||||||
|  |  | ||||||
|  | 			Array.Clear(pbIV, 0, pbIV.Length); | ||||||
|  | 			pbIV[11] = 2; | ||||||
|  |  | ||||||
|  | 			pb = StrUtil.Utf8.GetBytes("Any submission to the IETF inten" + | ||||||
|  | 				"ded by the Contributor for publication as all or" + | ||||||
|  | 				" part of an IETF Internet-Draft or RFC and any s" + | ||||||
|  | 				"tatement made within the context of an IETF acti" + | ||||||
|  | 				"vity is considered an \"IETF Contribution\". Such " + | ||||||
|  | 				"statements include oral statements in IETF sessi" + | ||||||
|  | 				"ons, as well as written and electronic communica" + | ||||||
|  | 				"tions made at any time or place, which are addressed to"); | ||||||
|  |  | ||||||
|  | 			pbExpc = MemUtil.HexStringToByteArray( | ||||||
|  | 				"A3FBF07DF3FA2FDE4F376CA23E82737041605D9F4F4F57BD8CFF2C1D4B7955EC" + | ||||||
|  | 				"2A97948BD3722915C8F3D337F7D370050E9E96D647B7C39F56E031CA5EB6250D" + | ||||||
|  | 				"4042E02785ECECFA4B4BB5E8EAD0440E20B6E8DB09D881A7C6132F420E527950" + | ||||||
|  | 				"42BDFA7773D8A9051447B3291CE1411C680465552AA6C405B7764D5E87BEA85A" + | ||||||
|  | 				"D00F8449ED8F72D0D662AB052691CA66424BC86D2DF80EA41F43ABF937D3259D" + | ||||||
|  | 				"C4B2D0DFB48A6C9139DDD7F76966E928E635553BA76C5C879D7B35D49EB2E62B" + | ||||||
|  | 				"0871CDAC638939E25E8A1E0EF9D5280FA8CA328B351C3C765989CBCF3DAA8B6C" + | ||||||
|  | 				"CC3AAF9F3979C92B3720FC88DC95ED84A1BE059C6499B9FDA236E7E818B04B0B" + | ||||||
|  | 				"C39C1E876B193BFE5569753F88128CC08AAA9B63D1A16F80EF2554D7189C411F" + | ||||||
|  | 				"5869CA52C5B83FA36FF216B9C1D30062BEBCFD2DC5BCE0911934FDA79A86F6E6" + | ||||||
|  | 				"98CED759C3FF9B6477338F3DA4F9CD8514EA9982CCAFB341B2384DD902F3D1AB" + | ||||||
|  | 				"7AC61DD29C6F21BA5B862F3730E37CFDC4FD806C22F221"); | ||||||
|  |  | ||||||
|  | 			Random r = new Random(); | ||||||
|  | 			using(MemoryStream msEnc = new MemoryStream()) | ||||||
|  | 			{ | ||||||
|  | 				using(ChaCha20Stream c = new ChaCha20Stream(msEnc, true, pbKey, pbIV)) | ||||||
|  | 				{ | ||||||
|  | 					r.NextBytes(pb64); | ||||||
|  | 					c.Write(pb64, 0, pb64.Length); // Skip first block | ||||||
|  |  | ||||||
|  | 					int p = 0; | ||||||
|  | 					while(p < pb.Length) | ||||||
|  | 					{ | ||||||
|  | 						int cb = r.Next(1, pb.Length - p + 1); | ||||||
|  | 						c.Write(pb, p, cb); | ||||||
|  | 						p += cb; | ||||||
|  | 					} | ||||||
|  | 					Debug.Assert(p == pb.Length); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				byte[] pbEnc0 = msEnc.ToArray(); | ||||||
|  | 				byte[] pbEnc = MemUtil.Mid(pbEnc0, 64, pbEnc0.Length - 64); | ||||||
|  | 				if(!MemUtil.ArraysEqual(pbEnc, pbExpc)) | ||||||
|  | 					throw new SecurityException("ChaCha20-3"); | ||||||
|  |  | ||||||
|  | 				using(MemoryStream msCT = new MemoryStream(pbEnc0, false)) | ||||||
|  | 				{ | ||||||
|  | 					using(ChaCha20Stream cDec = new ChaCha20Stream(msCT, false, | ||||||
|  | 						pbKey, pbIV)) | ||||||
|  | 					{ | ||||||
|  | 						byte[] pbPT = MemUtil.Read(cDec, pbEnc0.Length); | ||||||
|  | 						if(cDec.ReadByte() >= 0) | ||||||
|  | 							throw new SecurityException("ChaCha20-4"); | ||||||
|  | 						if(!MemUtil.ArraysEqual(MemUtil.Mid(pbPT, 0, 64), pb64)) | ||||||
|  | 							throw new SecurityException("ChaCha20-5"); | ||||||
|  | 						if(!MemUtil.ArraysEqual(MemUtil.Mid(pbPT, 64, pbEnc.Length), pb)) | ||||||
|  | 							throw new SecurityException("ChaCha20-6"); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// Test vector TC8 from RFC draft by J. Strombergson: | ||||||
|  | 			// https://tools.ietf.org/html/draft-strombergson-chacha-test-vectors-01 | ||||||
|  |  | ||||||
|  | 			pbKey = new byte[32] { | ||||||
|  | 				0xC4, 0x6E, 0xC1, 0xB1, 0x8C, 0xE8, 0xA8, 0x78, | ||||||
|  | 				0x72, 0x5A, 0x37, 0xE7, 0x80, 0xDF, 0xB7, 0x35, | ||||||
|  | 				0x1F, 0x68, 0xED, 0x2E, 0x19, 0x4C, 0x79, 0xFB, | ||||||
|  | 				0xC6, 0xAE, 0xBE, 0xE1, 0xA6, 0x67, 0x97, 0x5D | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			// The first 4 bytes are set to zero and a large counter | ||||||
|  | 			// is used; this makes the RFC 7539 version of ChaCha20 | ||||||
|  | 			// compatible with the original specification by | ||||||
|  | 			// D. J. Bernstein. | ||||||
|  | 			pbIV = new byte[12] { 0x00, 0x00, 0x00, 0x00, | ||||||
|  | 				0x1A, 0xDA, 0x31, 0xD5, 0xCF, 0x68, 0x82, 0x21 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			pb = new byte[128]; | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[128] { | ||||||
|  | 				0xF6, 0x3A, 0x89, 0xB7, 0x5C, 0x22, 0x71, 0xF9, | ||||||
|  | 				0x36, 0x88, 0x16, 0x54, 0x2B, 0xA5, 0x2F, 0x06, | ||||||
|  | 				0xED, 0x49, 0x24, 0x17, 0x92, 0x30, 0x2B, 0x00, | ||||||
|  | 				0xB5, 0xE8, 0xF8, 0x0A, 0xE9, 0xA4, 0x73, 0xAF, | ||||||
|  | 				0xC2, 0x5B, 0x21, 0x8F, 0x51, 0x9A, 0xF0, 0xFD, | ||||||
|  | 				0xD4, 0x06, 0x36, 0x2E, 0x8D, 0x69, 0xDE, 0x7F, | ||||||
|  | 				0x54, 0xC6, 0x04, 0xA6, 0xE0, 0x0F, 0x35, 0x3F, | ||||||
|  | 				0x11, 0x0F, 0x77, 0x1B, 0xDC, 0xA8, 0xAB, 0x92, | ||||||
|  |  | ||||||
|  | 				0xE5, 0xFB, 0xC3, 0x4E, 0x60, 0xA1, 0xD9, 0xA9, | ||||||
|  | 				0xDB, 0x17, 0x34, 0x5B, 0x0A, 0x40, 0x27, 0x36, | ||||||
|  | 				0x85, 0x3B, 0xF9, 0x10, 0xB0, 0x60, 0xBD, 0xF1, | ||||||
|  | 				0xF8, 0x97, 0xB6, 0x29, 0x0F, 0x01, 0xD1, 0x38, | ||||||
|  | 				0xAE, 0x2C, 0x4C, 0x90, 0x22, 0x5B, 0xA9, 0xEA, | ||||||
|  | 				0x14, 0xD5, 0x18, 0xF5, 0x59, 0x29, 0xDE, 0xA0, | ||||||
|  | 				0x98, 0xCA, 0x7A, 0x6C, 0xCF, 0xE6, 0x12, 0x27, | ||||||
|  | 				0x05, 0x3C, 0x84, 0xE4, 0x9A, 0x4A, 0x33, 0x32 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey, pbIV, true)) | ||||||
|  | 			{ | ||||||
|  | 				c.Decrypt(pb, 0, pb.Length); | ||||||
|  |  | ||||||
|  | 				if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 					throw new SecurityException("ChaCha20-7"); | ||||||
|  | 			} | ||||||
|  | #endif | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		private static void TestBlake2b() | ||||||
|  | 		{ | ||||||
|  | #if DEBUG | ||||||
|  | 			Blake2b h = new Blake2b(); | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// From https://tools.ietf.org/html/rfc7693 | ||||||
|  |  | ||||||
|  | 			byte[] pbData = StrUtil.Utf8.GetBytes("abc"); | ||||||
|  | 			byte[] pbExpc = new byte[64] { | ||||||
|  | 				0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, | ||||||
|  | 				0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12, 0xF6, 0xE9, | ||||||
|  | 				0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, | ||||||
|  | 				0x4B, 0x12, 0xBB, 0x6F, 0xDB, 0xFF, 0xA2, 0xD1, | ||||||
|  | 				0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, | ||||||
|  | 				0xC2, 0x52, 0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, | ||||||
|  | 				0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A, | ||||||
|  | 				0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			byte[] pbC = h.ComputeHash(pbData); | ||||||
|  | 			if(!MemUtil.ArraysEqual(pbC, pbExpc)) | ||||||
|  | 				throw new SecurityException("Blake2b-1"); | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// Computed using the official b2sum tool | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[64] { | ||||||
|  | 				0x78, 0x6A, 0x02, 0xF7, 0x42, 0x01, 0x59, 0x03, | ||||||
|  | 				0xC6, 0xC6, 0xFD, 0x85, 0x25, 0x52, 0xD2, 0x72, | ||||||
|  | 				0x91, 0x2F, 0x47, 0x40, 0xE1, 0x58, 0x47, 0x61, | ||||||
|  | 				0x8A, 0x86, 0xE2, 0x17, 0xF7, 0x1F, 0x54, 0x19, | ||||||
|  | 				0xD2, 0x5E, 0x10, 0x31, 0xAF, 0xEE, 0x58, 0x53, | ||||||
|  | 				0x13, 0x89, 0x64, 0x44, 0x93, 0x4E, 0xB0, 0x4B, | ||||||
|  | 				0x90, 0x3A, 0x68, 0x5B, 0x14, 0x48, 0xB7, 0x55, | ||||||
|  | 				0xD5, 0x6F, 0x70, 0x1A, 0xFE, 0x9B, 0xE2, 0xCE | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			pbC = h.ComputeHash(MemUtil.EmptyByteArray); | ||||||
|  | 			if(!MemUtil.ArraysEqual(pbC, pbExpc)) | ||||||
|  | 				throw new SecurityException("Blake2b-2"); | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// Computed using the official b2sum tool | ||||||
|  |  | ||||||
|  | 			string strS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:,;_-\r\n"; | ||||||
|  | 			StringBuilder sb = new StringBuilder(); | ||||||
|  | 			for(int i = 0; i < 1000; ++i) sb.Append(strS); | ||||||
|  | 			pbData = StrUtil.Utf8.GetBytes(sb.ToString()); | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[64] { | ||||||
|  | 				0x59, 0x69, 0x8D, 0x3B, 0x83, 0xF4, 0x02, 0x4E, | ||||||
|  | 				0xD8, 0x99, 0x26, 0x0E, 0xF4, 0xE5, 0x9F, 0x20, | ||||||
|  | 				0xDC, 0x31, 0xEE, 0x5B, 0x45, 0xEA, 0xBB, 0xFC, | ||||||
|  | 				0x1C, 0x0A, 0x8E, 0xED, 0xAA, 0x7A, 0xFF, 0x50, | ||||||
|  | 				0x82, 0xA5, 0x8F, 0xBC, 0x4A, 0x46, 0xFC, 0xC5, | ||||||
|  | 				0xEF, 0x44, 0x4E, 0x89, 0x80, 0x7D, 0x3F, 0x1C, | ||||||
|  | 				0xC1, 0x94, 0x45, 0xBB, 0xC0, 0x2C, 0x95, 0xAA, | ||||||
|  | 				0x3F, 0x08, 0x8A, 0x93, 0xF8, 0x75, 0x91, 0xB0 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			Random r = new Random(); | ||||||
|  | 			int p = 0; | ||||||
|  | 			while(p < pbData.Length) | ||||||
|  | 			{ | ||||||
|  | 				int cb = r.Next(1, pbData.Length - p + 1); | ||||||
|  | 				h.TransformBlock(pbData, p, cb, pbData, p); | ||||||
|  | 				p += cb; | ||||||
|  | 			} | ||||||
|  | 			Debug.Assert(p == pbData.Length); | ||||||
|  |  | ||||||
|  | 			h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0); | ||||||
|  |  | ||||||
|  | 			if(!MemUtil.ArraysEqual(h.Hash, pbExpc)) | ||||||
|  | 				throw new SecurityException("Blake2b-3"); | ||||||
|  |  | ||||||
|  | 			h.Clear(); | ||||||
|  | #endif | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		private static void TestArgon2() | ||||||
|  | 		{ | ||||||
|  | #if DEBUG | ||||||
|  | 			Argon2Kdf kdf = new Argon2Kdf(); | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// From the official Argon2 1.3 reference code package | ||||||
|  | 			// (test vector for Argon2d 1.3); also on | ||||||
|  | 			// https://tools.ietf.org/html/draft-irtf-cfrg-argon2-00 | ||||||
|  |  | ||||||
|  | 			KdfParameters p = kdf.GetDefaultParameters(); | ||||||
|  | 			kdf.Randomize(p); | ||||||
|  |  | ||||||
|  | 			Debug.Assert(p.GetUInt32(Argon2Kdf.ParamVersion, 0) == 0x13U); | ||||||
|  |  | ||||||
|  | 			byte[] pbMsg = new byte[32]; | ||||||
|  | 			for(int i = 0; i < pbMsg.Length; ++i) pbMsg[i] = 1; | ||||||
|  |  | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamMemory, 32 * 1024); | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamIterations, 3); | ||||||
|  | 			p.SetUInt32(Argon2Kdf.ParamParallelism, 4); | ||||||
|  |  | ||||||
|  | 			byte[] pbSalt = new byte[16]; | ||||||
|  | 			for(int i = 0; i < pbSalt.Length; ++i) pbSalt[i] = 2; | ||||||
|  | 			p.SetByteArray(Argon2Kdf.ParamSalt, pbSalt); | ||||||
|  |  | ||||||
|  | 			byte[] pbKey = new byte[8]; | ||||||
|  | 			for(int i = 0; i < pbKey.Length; ++i) pbKey[i] = 3; | ||||||
|  | 			p.SetByteArray(Argon2Kdf.ParamSecretKey, pbKey); | ||||||
|  |  | ||||||
|  | 			byte[] pbAssoc = new byte[12]; | ||||||
|  | 			for(int i = 0; i < pbAssoc.Length; ++i) pbAssoc[i] = 4; | ||||||
|  | 			p.SetByteArray(Argon2Kdf.ParamAssocData, pbAssoc); | ||||||
|  |  | ||||||
|  | 			byte[] pbExpc = new byte[32] { | ||||||
|  | 				0x51, 0x2B, 0x39, 0x1B, 0x6F, 0x11, 0x62, 0x97, | ||||||
|  | 				0x53, 0x71, 0xD3, 0x09, 0x19, 0x73, 0x42, 0x94, | ||||||
|  | 				0xF8, 0x68, 0xE3, 0xBE, 0x39, 0x84, 0xF3, 0xC1, | ||||||
|  | 				0xA1, 0x3A, 0x4D, 0xB9, 0xFA, 0xBE, 0x4A, 0xCB | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			byte[] pb = kdf.Transform(pbMsg, p); | ||||||
|  |  | ||||||
|  | 			if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 				throw new SecurityException("Argon2-1"); | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// From the official Argon2 1.3 reference code package | ||||||
|  | 			// (test vector for Argon2d 1.0) | ||||||
|  |  | ||||||
|  | 			p.SetUInt32(Argon2Kdf.ParamVersion, 0x10); | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[32] { | ||||||
|  | 				0x96, 0xA9, 0xD4, 0xE5, 0xA1, 0x73, 0x40, 0x92, | ||||||
|  | 				0xC8, 0x5E, 0x29, 0xF4, 0x10, 0xA4, 0x59, 0x14, | ||||||
|  | 				0xA5, 0xDD, 0x1F, 0x5C, 0xBF, 0x08, 0xB2, 0x67, | ||||||
|  | 				0x0D, 0xA6, 0x8A, 0x02, 0x85, 0xAB, 0xF3, 0x2B | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			pb = kdf.Transform(pbMsg, p); | ||||||
|  |  | ||||||
|  | 			if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 				throw new SecurityException("Argon2-2"); | ||||||
|  |  | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// From the official 'phc-winner-argon2-20151206.zip' | ||||||
|  | 			// (test vector for Argon2d 1.0) | ||||||
|  |  | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamMemory, 16 * 1024); | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[32] { | ||||||
|  | 				0x57, 0xB0, 0x61, 0x3B, 0xFD, 0xD4, 0x13, 0x1A, | ||||||
|  | 				0x0C, 0x34, 0x88, 0x34, 0xC6, 0x72, 0x9C, 0x2C, | ||||||
|  | 				0x72, 0x29, 0x92, 0x1E, 0x6B, 0xBA, 0x37, 0x66, | ||||||
|  | 				0x5D, 0x97, 0x8C, 0x4F, 0xE7, 0x17, 0x5E, 0xD2 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			pb = kdf.Transform(pbMsg, p); | ||||||
|  |  | ||||||
|  | 			if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 				throw new SecurityException("Argon2-3"); | ||||||
|  |  | ||||||
|  | #if SELFTEST_ARGON2_LONG | ||||||
|  | 			// ====================================================== | ||||||
|  | 			// Computed using the official 'argon2' application | ||||||
|  | 			// (test vectors for Argon2d 1.3) | ||||||
|  |  | ||||||
|  | 			p = kdf.GetDefaultParameters(); | ||||||
|  |  | ||||||
|  | 			pbMsg = StrUtil.Utf8.GetBytes("ABC1234"); | ||||||
|  |  | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamMemory, (1 << 11) * 1024); // 2 MB | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamIterations, 2); | ||||||
|  | 			p.SetUInt32(Argon2Kdf.ParamParallelism, 2); | ||||||
|  |  | ||||||
|  | 			pbSalt = StrUtil.Utf8.GetBytes("somesalt"); | ||||||
|  | 			p.SetByteArray(Argon2Kdf.ParamSalt, pbSalt); | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[32] { | ||||||
|  | 				0x29, 0xCB, 0xD3, 0xA1, 0x93, 0x76, 0xF7, 0xA2, | ||||||
|  | 				0xFC, 0xDF, 0xB0, 0x68, 0xAC, 0x0B, 0x99, 0xBA, | ||||||
|  | 				0x40, 0xAC, 0x09, 0x01, 0x73, 0x42, 0xCE, 0xF1, | ||||||
|  | 				0x29, 0xCC, 0xA1, 0x4F, 0xE1, 0xC1, 0xB7, 0xA3 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			pb = kdf.Transform(pbMsg, p); | ||||||
|  |  | ||||||
|  | 			if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 				throw new SecurityException("Argon2-4"); | ||||||
|  |  | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamMemory, (1 << 10) * 1024); // 1 MB | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamIterations, 3); | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[32] { | ||||||
|  | 				0x7A, 0xBE, 0x1C, 0x1C, 0x8D, 0x7F, 0xD6, 0xDC, | ||||||
|  | 				0x7C, 0x94, 0x06, 0x3E, 0xD8, 0xBC, 0xD8, 0x1C, | ||||||
|  | 				0x2F, 0x87, 0x84, 0x99, 0x12, 0x83, 0xFE, 0x76, | ||||||
|  | 				0x00, 0x64, 0xC4, 0x58, 0xA4, 0xDA, 0x35, 0x70 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			pb = kdf.Transform(pbMsg, p); | ||||||
|  |  | ||||||
|  | 			if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 				throw new SecurityException("Argon2-5"); | ||||||
|  |  | ||||||
|  | #if SELFTEST_ARGON2_LONGER | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamMemory, (1 << 20) * 1024); // 1 GB | ||||||
|  | 			p.SetUInt64(Argon2Kdf.ParamIterations, 2); | ||||||
|  | 			p.SetUInt32(Argon2Kdf.ParamParallelism, 3); | ||||||
|  |  | ||||||
|  | 			pbExpc = new byte[32] { | ||||||
|  | 				0xE6, 0xE7, 0xCB, 0xF5, 0x5A, 0x06, 0x93, 0x05, | ||||||
|  | 				0x32, 0xBA, 0x86, 0xC6, 0x1F, 0x45, 0x17, 0x99, | ||||||
|  | 				0x65, 0x41, 0x77, 0xF9, 0x30, 0x55, 0x9A, 0xE8, | ||||||
|  | 				0x3D, 0x21, 0x48, 0xC6, 0x2D, 0x0C, 0x49, 0x11 | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			pb = kdf.Transform(pbMsg, p); | ||||||
|  |  | ||||||
|  | 			if(!MemUtil.ArraysEqual(pb, pbExpc)) | ||||||
|  | 				throw new SecurityException("Argon2-6"); | ||||||
|  | #endif // SELFTEST_ARGON2_LONGER | ||||||
|  | #endif // SELFTEST_ARGON2_LONG | ||||||
|  | #endif // DEBUG | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		private static void TestHmac() | ||||||
|  | 		{ | ||||||
|  | #if DEBUG | ||||||
|  | 			// Test vectors from RFC 4231 | ||||||
|  |  | ||||||
|  | 			byte[] pbKey = new byte[20]; | ||||||
|  | 			for(int i = 0; i < pbKey.Length; ++i) pbKey[i] = 0x0B; | ||||||
|  | 			byte[] pbMsg = StrUtil.Utf8.GetBytes("Hi There"); | ||||||
|  | 			byte[] pbExpc = new byte[32] { | ||||||
|  | 				0xB0, 0x34, 0x4C, 0x61, 0xD8, 0xDB, 0x38, 0x53, | ||||||
|  | 				0x5C, 0xA8, 0xAF, 0xCE, 0xAF, 0x0B, 0xF1, 0x2B, | ||||||
|  | 				0x88, 0x1D, 0xC2, 0x00, 0xC9, 0x83, 0x3D, 0xA7, | ||||||
|  | 				0x26, 0xE9, 0x37, 0x6C, 0x2E, 0x32, 0xCF, 0xF7 | ||||||
|  | 			}; | ||||||
|  | 			HmacEval(pbKey, pbMsg, pbExpc, "1"); | ||||||
|  |  | ||||||
|  | 			pbKey = new byte[131]; | ||||||
|  | 			for(int i = 0; i < pbKey.Length; ++i) pbKey[i] = 0xAA; | ||||||
|  | 			pbMsg = StrUtil.Utf8.GetBytes( | ||||||
|  | 				"This is a test using a larger than block-size key and " + | ||||||
|  | 				"a larger than block-size data. The key needs to be " + | ||||||
|  | 				"hashed before being used by the HMAC algorithm."); | ||||||
|  | 			pbExpc = new byte[32] { | ||||||
|  | 				0x9B, 0x09, 0xFF, 0xA7, 0x1B, 0x94, 0x2F, 0xCB, | ||||||
|  | 				0x27, 0x63, 0x5F, 0xBC, 0xD5, 0xB0, 0xE9, 0x44, | ||||||
|  | 				0xBF, 0xDC, 0x63, 0x64, 0x4F, 0x07, 0x13, 0x93, | ||||||
|  | 				0x8A, 0x7F, 0x51, 0x53, 0x5C, 0x3A, 0x35, 0xE2 | ||||||
|  | 			}; | ||||||
|  | 			HmacEval(pbKey, pbMsg, pbExpc, "2"); | ||||||
|  | #endif | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | #if DEBUG | ||||||
|  | 		private static void HmacEval(byte[] pbKey, byte[] pbMsg, | ||||||
|  | 			byte[] pbExpc, string strID) | ||||||
|  | 		{ | ||||||
|  | 			using(HMACSHA256 h = new HMACSHA256(pbKey)) | ||||||
|  | 			{ | ||||||
|  | 				h.TransformBlock(pbMsg, 0, pbMsg.Length, pbMsg, 0); | ||||||
|  | 				h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0); | ||||||
|  |  | ||||||
|  | 				byte[] pbHash = h.Hash; | ||||||
|  | 				if(!MemUtil.ArraysEqual(pbHash, pbExpc)) | ||||||
|  | 					throw new SecurityException("HMAC-SHA-256-" + strID); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  |  | ||||||
| 		private static void TestNativeKeyTransform() | 		private static void TestNativeKeyTransform() | ||||||
| 		{ | 		{ | ||||||
| #if DEBUG | #if DEBUG | ||||||
| @@ -235,8 +701,8 @@ namespace KeePassLib.Cryptography | |||||||
|  |  | ||||||
| 			byte[] pbManaged = new byte[32]; | 			byte[] pbManaged = new byte[32]; | ||||||
| 			Array.Copy(pbOrgKey, pbManaged, 32); | 			Array.Copy(pbOrgKey, pbManaged, 32); | ||||||
| 			if(!CompositeKey.TransformKeyManaged(pbManaged, pbSeed, uRounds)) | 			if(!AesKdf.TransformKeyManaged(pbManaged, pbSeed, uRounds)) | ||||||
| 				throw new SecurityException("Managed transform."); | 				throw new SecurityException("AES-KDF-1"); | ||||||
|  |  | ||||||
| 			byte[] pbNative = new byte[32]; | 			byte[] pbNative = new byte[32]; | ||||||
| 			Array.Copy(pbOrgKey, pbNative, 32); | 			Array.Copy(pbOrgKey, pbNative, 32); | ||||||
| @@ -244,7 +710,7 @@ namespace KeePassLib.Cryptography | |||||||
| 				return; // Native library not available ("success") | 				return; // Native library not available ("success") | ||||||
|  |  | ||||||
| 			if(!MemUtil.ArraysEqual(pbManaged, pbNative)) | 			if(!MemUtil.ArraysEqual(pbManaged, pbNative)) | ||||||
| 				throw new SecurityException("Native transform."); | 				throw new SecurityException("AES-KDF-2"); | ||||||
| #endif | #endif | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -310,6 +776,15 @@ namespace KeePassLib.Cryptography | |||||||
| 			pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======"); | 			pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======"); | ||||||
| 			pbExp = Encoding.ASCII.GetBytes("Key provider based on one-time passwords."); | 			pbExp = Encoding.ASCII.GetBytes("Key provider based on one-time passwords."); | ||||||
| 			if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-7"); | 			if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-7"); | ||||||
|  |  | ||||||
|  | 			int i = 0 - 0x10203040; | ||||||
|  | 			pbRes = MemUtil.Int32ToBytes(i); | ||||||
|  | 			if(MemUtil.ByteArrayToHexString(pbRes) != "C0CFDFEF") | ||||||
|  | 				throw new Exception("MemUtil-8"); // Must be little-endian | ||||||
|  | 			if(MemUtil.BytesToUInt32(pbRes) != (uint)i) | ||||||
|  | 				throw new Exception("MemUtil-9"); | ||||||
|  | 			if(MemUtil.BytesToInt32(pbRes) != i) | ||||||
|  | 				throw new Exception("MemUtil-10"); | ||||||
| #endif | #endif | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,14 +22,8 @@ using System.Collections.Generic; | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| #if KeePassUAP | using KeePassLib.Cryptography; | ||||||
| using Org.BouncyCastle.Crypto; | using KeePassLib.Cryptography.KeyDerivation; | ||||||
| using Org.BouncyCastle.Crypto.Engines; |  | ||||||
| using Org.BouncyCastle.Crypto.Parameters; |  | ||||||
| #else |  | ||||||
| using System.Security.Cryptography; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| using KeePassLib.Native; | using KeePassLib.Native; | ||||||
| using KeePassLib.Resources; | using KeePassLib.Resources; | ||||||
| using KeePassLib.Security; | using KeePassLib.Security; | ||||||
| @@ -197,8 +191,7 @@ namespace KeePassLib.Keys | |||||||
| 			} | 			} | ||||||
| 			Debug.Assert(p == cbData); | 			Debug.Assert(p == cbData); | ||||||
|  |  | ||||||
| 			SHA256Managed sha256 = new SHA256Managed(); | 			byte[] pbHash = CryptoUtil.HashSha256(pbAllData); | ||||||
| 			byte[] pbHash = sha256.ComputeHash(pbAllData); |  | ||||||
| 			MemUtil.ZeroByteArray(pbAllData); | 			MemUtil.ZeroByteArray(pbAllData); | ||||||
| 			return pbHash; | 			return pbHash; | ||||||
| 		} | 		} | ||||||
| @@ -216,15 +209,7 @@ namespace KeePassLib.Keys | |||||||
| 			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); | ||||||
| @@ -232,18 +217,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; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -263,182 +273,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 |  | ||||||
| 			{ |  | ||||||
| 				// Try to use the native library first |  | ||||||
| 				if(NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds)) |  | ||||||
| 					return (new SHA256Managed()).ComputeHash(pbNewKey); |  | ||||||
|  |  | ||||||
| 				if(!TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds)) |  | ||||||
| 					return null; |  | ||||||
|  |  | ||||||
| 				return (new SHA256Managed()).ComputeHash(pbNewKey); |  | ||||||
| 			} |  | ||||||
| 			finally { MemUtil.ZeroByteArray(pbNewKey); } |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		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; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		/// <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 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!"); |  | ||||||
| 				return PwDefs.DefaultKeyEncryptionRounds; |  | ||||||
| 			} |  | ||||||
| #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; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return uRounds; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public sealed class InvalidCompositeKeyException : Exception | 	public sealed class InvalidCompositeKeyException : Exception | ||||||
|   | |||||||
| @@ -22,10 +22,7 @@ using System.Collections.Generic; | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| #if !KeePassUAP | using KeePassLib.Cryptography; | ||||||
| using System.Security.Cryptography; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| using KeePassLib.Security; | using KeePassLib.Security; | ||||||
|  |  | ||||||
| namespace KeePassLib.Keys | namespace KeePassLib.Keys | ||||||
| @@ -57,8 +54,7 @@ namespace KeePassLib.Keys | |||||||
|  |  | ||||||
| 			if(bPerformHash) | 			if(bPerformHash) | ||||||
| 			{ | 			{ | ||||||
| 				SHA256Managed sha256 = new SHA256Managed(); | 				byte[] pbRaw = CryptoUtil.HashSha256(pbKeyData); | ||||||
| 				byte[] pbRaw = sha256.ComputeHash(pbKeyData); |  | ||||||
| 				m_pbKey = new ProtectedBinary(true, pbRaw); | 				m_pbKey = new ProtectedBinary(true, pbRaw); | ||||||
| 			} | 			} | ||||||
| 			else m_pbKey = new ProtectedBinary(true, pbKeyData); | 			else m_pbKey = new ProtectedBinary(true, pbKeyData); | ||||||
|   | |||||||
| @@ -133,10 +133,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; | ||||||
| 		} | 		} | ||||||
| @@ -156,12 +153,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; | ||||||
| 			} | 			} | ||||||
| @@ -189,13 +189,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); | ||||||
|   | |||||||
| @@ -21,10 +21,7 @@ using System; | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| #if !KeePassUAP | using KeePassLib.Cryptography; | ||||||
| using System.Security.Cryptography; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| using KeePassLib.Security; | using KeePassLib.Security; | ||||||
| using KeePassLib.Utility; | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| @@ -75,8 +72,7 @@ namespace KeePassLib.Keys | |||||||
| 			Debug.Assert(ValidatePassword(pbPasswordUtf8)); | 			Debug.Assert(ValidatePassword(pbPasswordUtf8)); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 			SHA256Managed sha256 = new SHA256Managed(); | 			byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8); | ||||||
| 			byte[] pbRaw = sha256.ComputeHash(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); | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ using System.Drawing; | |||||||
| 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; | ||||||
| @@ -50,13 +51,14 @@ namespace KeePassLib | |||||||
|  |  | ||||||
| 		private static bool m_bPrimaryCreated = false; | 		private static bool m_bPrimaryCreated = false; | ||||||
|  |  | ||||||
| 		// Initializations see Clear() | 		// Initializations: see Clear() | ||||||
| 		private PwGroup m_pgRootGroup = null; | 		private PwGroup m_pgRootGroup = null; | ||||||
| 		private PwObjectList<PwDeletedObject> m_vDeletedObjects = new PwObjectList<PwDeletedObject>(); | 		private PwObjectList<PwDeletedObject> m_vDeletedObjects = new PwObjectList<PwDeletedObject>(); | ||||||
|  |  | ||||||
| 		private PwUuid m_uuidDataCipher = StandardAesEngine.AesUuid; | 		private PwUuid m_uuidDataCipher = StandardAesEngine.AesUuid; | ||||||
| 		private PwCompressionAlgorithm m_caCompression = PwCompressionAlgorithm.GZip; | 		private PwCompressionAlgorithm m_caCompression = PwCompressionAlgorithm.GZip; | ||||||
| 		private ulong m_uKeyEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds; | 		// private ulong m_uKeyEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds; | ||||||
|  | 		private KdfParameters m_kdfParams = KdfPool.GetDefaultParameters(); | ||||||
|  |  | ||||||
| 		private CompositeKey m_pwUserKey = null; | 		private CompositeKey m_pwUserKey = null; | ||||||
| 		private MemoryProtectionConfig m_memProtConfig = new MemoryProtectionConfig(); | 		private MemoryProtectionConfig m_memProtConfig = new MemoryProtectionConfig(); | ||||||
| @@ -64,6 +66,7 @@ namespace KeePassLib | |||||||
| 		private List<PwCustomIcon> m_vCustomIcons = new List<PwCustomIcon>(); | 		private List<PwCustomIcon> m_vCustomIcons = new List<PwCustomIcon>(); | ||||||
| 		private bool m_bUINeedsIconUpdate = true; | 		private bool m_bUINeedsIconUpdate = true; | ||||||
|  |  | ||||||
|  | 		private DateTime m_dtSettingsChanged = PwDefs.DtDefaultNow; | ||||||
| 		private string m_strName = string.Empty; | 		private string m_strName = string.Empty; | ||||||
| 		private DateTime m_dtNameChanged = PwDefs.DtDefaultNow; | 		private DateTime m_dtNameChanged = PwDefs.DtDefaultNow; | ||||||
| 		private string m_strDesc = string.Empty; | 		private string m_strDesc = string.Empty; | ||||||
| @@ -93,7 +96,8 @@ namespace KeePassLib | |||||||
| 		private int m_nHistoryMaxItems = DefaultHistoryMaxItems; | 		private int m_nHistoryMaxItems = DefaultHistoryMaxItems; | ||||||
| 		private long m_lHistoryMaxSize = DefaultHistoryMaxSize; // In bytes | 		private long m_lHistoryMaxSize = DefaultHistoryMaxSize; // In bytes | ||||||
|  |  | ||||||
| 		private StringDictionaryEx m_vCustomData = new StringDictionaryEx(); | 		private StringDictionaryEx m_dCustomData = new StringDictionaryEx(); | ||||||
|  | 		private VariantDictionary m_dPublicCustomData = new VariantDictionary(); | ||||||
|  |  | ||||||
| 		private byte[] m_pbHashOfFileOnDisk = null; | 		private byte[] m_pbHashOfFileOnDisk = null; | ||||||
| 		private byte[] m_pbHashOfLastIO = null; | 		private byte[] m_pbHashOfLastIO = null; | ||||||
| @@ -168,6 +172,12 @@ namespace KeePassLib | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		public DateTime SettingsChanged | ||||||
|  | 		{ | ||||||
|  | 			get { return m_dtSettingsChanged; } | ||||||
|  | 			set { m_dtSettingsChanged = value; } | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Name of the database. | 		/// Name of the database. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| @@ -281,14 +291,23 @@ namespace KeePassLib | |||||||
| 			set { m_caCompression = value; } | 			set { m_caCompression = value; } | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		// /// <summary> | ||||||
| 		/// Number of key transformation rounds (in order to make dictionary | 		// /// Number of key transformation rounds (KDF parameter). | ||||||
| 		/// attacks harder). | 		// /// </summary> | ||||||
| 		/// </summary> | 		// public ulong KeyEncryptionRounds | ||||||
| 		public ulong KeyEncryptionRounds | 		// { | ||||||
|  | 		//	get { return m_uKeyEncryptionRounds; } | ||||||
|  | 		//	set { m_uKeyEncryptionRounds = value; } | ||||||
|  | 		// } | ||||||
|  |  | ||||||
|  | 		public KdfParameters KdfParameters | ||||||
| 		{ | 		{ | ||||||
| 			get { return m_uKeyEncryptionRounds; } | 			get { return m_kdfParams; } | ||||||
| 			set { m_uKeyEncryptionRounds = value; } | 			set | ||||||
|  | 			{ | ||||||
|  | 				if(value == null) throw new ArgumentNullException("value"); | ||||||
|  | 				m_kdfParams = value; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| @@ -408,14 +427,37 @@ namespace KeePassLib | |||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Custom data container that can be used by plugins to store | 		/// Custom data container that can be used by plugins to store | ||||||
| 		/// own data in KeePass databases. | 		/// own data in KeePass databases. | ||||||
|  | 		/// The data is stored in the encrypted part of encrypted | ||||||
|  | 		/// database files. | ||||||
|  | 		/// Use unique names for your items, e.g. "PluginName_ItemName". | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public StringDictionaryEx CustomData | 		public StringDictionaryEx CustomData | ||||||
| 		{ | 		{ | ||||||
| 			get { return m_vCustomData; } | 			get { return m_dCustomData; } | ||||||
| 			set | 			internal set | ||||||
| 			{ | 			{ | ||||||
| 				Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value"); | 				if(value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); } | ||||||
| 				m_vCustomData = value; | 				m_dCustomData = value; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Custom data container that can be used by plugins to store | ||||||
|  | 		/// own data in KeePass databases. | ||||||
|  | 		/// The data is stored in the *unencrypted* part of database files, | ||||||
|  | 		/// and it is not supported by all file formats (e.g. supported by KDBX, | ||||||
|  | 		/// unsupported by XML). | ||||||
|  | 		/// It is highly recommended to use <c>CustomData</c> instead, | ||||||
|  | 		/// if possible. | ||||||
|  | 		/// Use unique names for your items, e.g. "PluginName_ItemName". | ||||||
|  | 		/// </summary> | ||||||
|  | 		public VariantDictionary PublicCustomData | ||||||
|  | 		{ | ||||||
|  | 			get { return m_dPublicCustomData; } | ||||||
|  | 			internal set | ||||||
|  | 			{ | ||||||
|  | 				if(value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); } | ||||||
|  | 				m_dPublicCustomData = value; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -484,7 +526,8 @@ namespace KeePassLib | |||||||
|  |  | ||||||
| 			m_uuidDataCipher = StandardAesEngine.AesUuid; | 			m_uuidDataCipher = StandardAesEngine.AesUuid; | ||||||
| 			m_caCompression = PwCompressionAlgorithm.GZip; | 			m_caCompression = PwCompressionAlgorithm.GZip; | ||||||
| 			m_uKeyEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds; | 			// m_uKeyEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds; | ||||||
|  | 			m_kdfParams = KdfPool.GetDefaultParameters(); | ||||||
|  |  | ||||||
| 			m_pwUserKey = null; | 			m_pwUserKey = null; | ||||||
| 			m_memProtConfig = new MemoryProtectionConfig(); | 			m_memProtConfig = new MemoryProtectionConfig(); | ||||||
| @@ -494,6 +537,7 @@ namespace KeePassLib | |||||||
|  |  | ||||||
| 			DateTime dtNow = DateTime.Now; | 			DateTime dtNow = DateTime.Now; | ||||||
|  |  | ||||||
|  | 			m_dtSettingsChanged = dtNow; | ||||||
| 			m_strName = string.Empty; | 			m_strName = string.Empty; | ||||||
| 			m_dtNameChanged = dtNow; | 			m_dtNameChanged = dtNow; | ||||||
| 			m_strDesc = string.Empty; | 			m_strDesc = string.Empty; | ||||||
| @@ -523,7 +567,8 @@ namespace KeePassLib | |||||||
| 			m_nHistoryMaxItems = DefaultHistoryMaxItems; | 			m_nHistoryMaxItems = DefaultHistoryMaxItems; | ||||||
| 			m_lHistoryMaxSize = DefaultHistoryMaxSize; | 			m_lHistoryMaxSize = DefaultHistoryMaxSize; | ||||||
|  |  | ||||||
| 			m_vCustomData = new StringDictionaryEx(); | 			m_dCustomData = new StringDictionaryEx(); | ||||||
|  | 			m_dPublicCustomData = new VariantDictionary(); | ||||||
|  |  | ||||||
| 			m_pbHashOfFileOnDisk = null; | 			m_pbHashOfFileOnDisk = null; | ||||||
| 			m_pbHashOfLastIO = null; | 			m_pbHashOfLastIO = null; | ||||||
| @@ -1393,6 +1438,14 @@ namespace KeePassLib | |||||||
| 				return; | 				return; | ||||||
|  |  | ||||||
| 			bool bForce = (mm == PwMergeMethod.OverwriteExisting); | 			bool bForce = (mm == PwMergeMethod.OverwriteExisting); | ||||||
|  | 			bool bSourceNewer = (pdSource.m_dtSettingsChanged > m_dtSettingsChanged); | ||||||
|  |  | ||||||
|  | 			if(bForce || bSourceNewer) | ||||||
|  | 			{ | ||||||
|  | 				m_dtSettingsChanged = pdSource.m_dtSettingsChanged; | ||||||
|  |  | ||||||
|  | 				m_clr = pdSource.m_clr; | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			if(bForce || (pdSource.m_dtNameChanged > m_dtNameChanged)) | 			if(bForce || (pdSource.m_dtNameChanged > m_dtNameChanged)) | ||||||
| 			{ | 			{ | ||||||
| @@ -1412,8 +1465,6 @@ namespace KeePassLib | |||||||
| 				m_dtDefaultUserChanged = pdSource.m_dtDefaultUserChanged; | 				m_dtDefaultUserChanged = pdSource.m_dtDefaultUserChanged; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if(bForce) m_clr = pdSource.m_clr; |  | ||||||
|  |  | ||||||
| 			PwUuid pwPrefBin = m_pwRecycleBin, pwAltBin = pdSource.m_pwRecycleBin; | 			PwUuid pwPrefBin = m_pwRecycleBin, pwAltBin = pdSource.m_pwRecycleBin; | ||||||
| 			if(bForce || (pdSource.m_dtRecycleBinChanged > m_dtRecycleBinChanged)) | 			if(bForce || (pdSource.m_dtRecycleBinChanged > m_dtRecycleBinChanged)) | ||||||
| 			{ | 			{ | ||||||
| @@ -1440,6 +1491,16 @@ namespace KeePassLib | |||||||
| 			else if(m_pgRootGroup.FindGroup(pwAltTmp, true) != null) | 			else if(m_pgRootGroup.FindGroup(pwAltTmp, true) != null) | ||||||
| 				m_pwEntryTemplatesGroup = pwAltTmp; | 				m_pwEntryTemplatesGroup = pwAltTmp; | ||||||
| 			else m_pwEntryTemplatesGroup = PwUuid.Zero; // Debug.Assert(false); | 			else m_pwEntryTemplatesGroup = PwUuid.Zero; // Debug.Assert(false); | ||||||
|  |  | ||||||
|  | 			foreach(KeyValuePair<string, string> kvp in pdSource.m_dCustomData) | ||||||
|  | 			{ | ||||||
|  | 				if(bSourceNewer || !m_dCustomData.Exists(kvp.Key)) | ||||||
|  | 					m_dCustomData.Set(kvp.Key, kvp.Value); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			VariantDictionary vdLocal = m_dPublicCustomData; // Backup | ||||||
|  | 			m_dPublicCustomData = (VariantDictionary)pdSource.m_dPublicCustomData.Clone(); | ||||||
|  | 			if(!bSourceNewer) vdLocal.CopyTo(m_dPublicCustomData); // Merge | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private void MergeEntryHistory(PwEntry pe, PwEntry peSource, | 		private void MergeEntryHistory(PwEntry pe, PwEntry peSource, | ||||||
|   | |||||||
| @@ -65,6 +65,8 @@ 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> | ||||||
| @@ -274,6 +276,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; | ||||||
|  |  | ||||||
| @@ -366,6 +385,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; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -486,6 +507,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; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -502,10 +525,10 @@ 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.m_tLastMod, m_tLastMod, | 			if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.m_tLastMod, | ||||||
| 				true) < 0)) | 				m_tLastMod, true) < 0)) | ||||||
| 				return; | 				return; | ||||||
|  |  | ||||||
| 			// Template UUID should be the same as the current one | 			// Template UUID should be the same as the current one | ||||||
| @@ -515,10 +538,11 @@ namespace KeePassLib | |||||||
| 			if(bAssignLocationChanged) | 			if(bAssignLocationChanged) | ||||||
| 				m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod; | 				m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod; | ||||||
|  |  | ||||||
| 			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 | ||||||
| @@ -536,6 +560,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> | ||||||
| @@ -787,6 +813,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; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -67,6 +67,8 @@ namespace KeePassLib | |||||||
|  |  | ||||||
| 		private PwUuid m_pwLastTopVisibleEntry = PwUuid.Zero; | 		private PwUuid m_pwLastTopVisibleEntry = PwUuid.Zero; | ||||||
|  |  | ||||||
|  | 		private StringDictionaryEx m_dCustomData = new StringDictionaryEx(); | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// UUID of this group. | 		/// UUID of this group. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| @@ -281,6 +283,23 @@ namespace KeePassLib | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Custom data container that can be used by plugins to store | ||||||
|  | 		/// own data in KeePass groups. | ||||||
|  | 		/// 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> GroupTouched; | 		public static EventHandler<ObjectTouchedEventArgs> GroupTouched; | ||||||
| 		public EventHandler<ObjectTouchedEventArgs> Touched; | 		public EventHandler<ObjectTouchedEventArgs> Touched; | ||||||
|  |  | ||||||
| @@ -376,6 +395,8 @@ namespace KeePassLib | |||||||
|  |  | ||||||
| 			pg.m_pwLastTopVisibleEntry = m_pwLastTopVisibleEntry; | 			pg.m_pwLastTopVisibleEntry = m_pwLastTopVisibleEntry; | ||||||
|  |  | ||||||
|  | 			pg.m_dCustomData = m_dCustomData.CloneDeep(); | ||||||
|  |  | ||||||
| 			return pg; | 			return pg; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -444,6 +465,8 @@ namespace KeePassLib | |||||||
|  |  | ||||||
| 			if(!m_pwLastTopVisibleEntry.Equals(pg.m_pwLastTopVisibleEntry)) return false; | 			if(!m_pwLastTopVisibleEntry.Equals(pg.m_pwLastTopVisibleEntry)) return false; | ||||||
|  |  | ||||||
|  | 			if(!m_dCustomData.Equals(pg.m_dCustomData)) return false; | ||||||
|  |  | ||||||
| 			if((pwOpt & PwCompareOptions.PropertiesOnly) == PwCompareOptions.None) | 			if((pwOpt & PwCompareOptions.PropertiesOnly) == PwCompareOptions.None) | ||||||
| 			{ | 			{ | ||||||
| 				if(m_listEntries.UCount != pg.m_listEntries.UCount) return false; | 				if(m_listEntries.UCount != pg.m_listEntries.UCount) return false; | ||||||
| @@ -509,6 +532,8 @@ namespace KeePassLib | |||||||
| 			m_bEnableSearching = pgTemplate.m_bEnableSearching; | 			m_bEnableSearching = pgTemplate.m_bEnableSearching; | ||||||
|  |  | ||||||
| 			m_pwLastTopVisibleEntry = pgTemplate.m_pwLastTopVisibleEntry; | 			m_pwLastTopVisibleEntry = pgTemplate.m_pwLastTopVisibleEntry; | ||||||
|  |  | ||||||
|  | 			m_dCustomData = pgTemplate.m_dCustomData.CloneDeep(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ 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_strExpect100Continue = TryGetEx(dictNew, "Expect100Continue", m_strExpect100Continue); | ||||||
| 			m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError); | 			m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError); | ||||||
| @@ -36,6 +36,7 @@ namespace KeePassLib.Resources | |||||||
| 			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); | ||||||
| @@ -50,6 +51,7 @@ namespace KeePassLib.Resources | |||||||
| 			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); | ||||||
| @@ -58,13 +60,14 @@ namespace KeePassLib.Resources | |||||||
| 			m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout); | 			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); | 			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", | 			"Expect100Continue", | ||||||
| 			"FatalError", | 			"FatalError", | ||||||
| @@ -73,6 +76,7 @@ namespace KeePassLib.Resources | |||||||
| 			"FileHeaderEndEarly", | 			"FileHeaderEndEarly", | ||||||
| 			"FileLoadFailed", | 			"FileLoadFailed", | ||||||
| 			"FileLockedWrite", | 			"FileLockedWrite", | ||||||
|  | 			"FileNewVerOrPlgReq", | ||||||
| 			"FileNewVerReq", | 			"FileNewVerReq", | ||||||
| 			"FileSaveCorruptionWarning", | 			"FileSaveCorruptionWarning", | ||||||
| 			"FileSaveFailed", | 			"FileSaveFailed", | ||||||
| @@ -87,6 +91,7 @@ namespace KeePassLib.Resources | |||||||
| 			"InvalidCompositeKeyHint", | 			"InvalidCompositeKeyHint", | ||||||
| 			"InvalidDataWhileDecoding", | 			"InvalidDataWhileDecoding", | ||||||
| 			"KeePass1xHint", | 			"KeePass1xHint", | ||||||
|  | 			"KeyBits", | ||||||
| 			"KeyFileDbSel", | 			"KeyFileDbSel", | ||||||
| 			"MasterSeedLengthInvalid", | 			"MasterSeedLengthInvalid", | ||||||
| 			"OldFormat", | 			"OldFormat", | ||||||
| @@ -95,6 +100,7 @@ namespace KeePassLib.Resources | |||||||
| 			"Timeout", | 			"Timeout", | ||||||
| 			"TryAgainSecs", | 			"TryAgainSecs", | ||||||
| 			"UnknownHeaderId", | 			"UnknownHeaderId", | ||||||
|  | 			"UnknownKdf", | ||||||
| 			"UserAccountKeyError", | 			"UserAccountKeyError", | ||||||
| 			"UserAgent" | 			"UserAgent" | ||||||
| 		}; | 		}; | ||||||
| @@ -115,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 = | ||||||
| @@ -214,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> | ||||||
| @@ -368,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> | ||||||
| @@ -456,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> | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ namespace KeePassLib.Security | |||||||
| 		{ | 		{ | ||||||
| 			None = 0, | 			None = 0, | ||||||
| 			ProtectedMemory, | 			ProtectedMemory, | ||||||
| 			Salsa20, | 			ChaCha20, | ||||||
| 			ExtCrypt | 			ExtCrypt | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -166,7 +166,7 @@ namespace KeePassLib.Security | |||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public ProtectedBinary() | 		public ProtectedBinary() | ||||||
| 		{ | 		{ | ||||||
| 			Init(false, new byte[0]); | 			Init(false, MemUtil.EmptyByteArray); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| @@ -263,11 +263,13 @@ namespace KeePassLib.Security | |||||||
| 				if(pbUpd != null) pbKey32 = pbUpd; | 				if(pbUpd != null) pbKey32 = pbUpd; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			Salsa20Cipher s = new Salsa20Cipher(pbKey32, | 			byte[] pbIV = new byte[12]; | ||||||
| 				BitConverter.GetBytes(m_lID)); | 			MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4); | ||||||
| 			s.Encrypt(m_pbData, m_pbData.Length, true); | 			using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey32, pbIV, true)) | ||||||
| 			s.Dispose(); | 			{ | ||||||
| 			m_mp = PbMemProt.Salsa20; | 				c.Encrypt(m_pbData, 0, m_pbData.Length); | ||||||
|  | 			} | ||||||
|  | 			m_mp = PbMemProt.ChaCha20; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		private void Decrypt() | 		private void Decrypt() | ||||||
| @@ -276,12 +278,14 @@ namespace KeePassLib.Security | |||||||
|  |  | ||||||
| 			if(m_mp == PbMemProt.ProtectedMemory) | 			if(m_mp == PbMemProt.ProtectedMemory) | ||||||
| 				ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess); | 				ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess); | ||||||
| 			else if(m_mp == PbMemProt.Salsa20) | 			else if(m_mp == PbMemProt.ChaCha20) | ||||||
| 			{ | 			{ | ||||||
| 				Salsa20Cipher s = new Salsa20Cipher(g_pbKey32, | 				byte[] pbIV = new byte[12]; | ||||||
| 					BitConverter.GetBytes(m_lID)); | 				MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4); | ||||||
| 				s.Encrypt(m_pbData, m_pbData.Length, true); | 				using(ChaCha20Cipher c = new ChaCha20Cipher(g_pbKey32, pbIV, true)) | ||||||
| 				s.Dispose(); | 				{ | ||||||
|  | 					c.Decrypt(m_pbData, 0, m_pbData.Length); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			else if(m_mp == PbMemProt.ExtCrypt) | 			else if(m_mp == PbMemProt.ExtCrypt) | ||||||
| 				m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID); | 				m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID); | ||||||
| @@ -300,7 +304,7 @@ 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]; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -152,7 +152,7 @@ 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); | ||||||
|  |  | ||||||
| 					lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime, | 					lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime, | ||||||
| #if KeePassUAP | #if KeePassUAP | ||||||
|   | |||||||
| @@ -22,10 +22,7 @@ using System.Diagnostics; | |||||||
| using System.IO; | using System.IO; | ||||||
| using System.Text; | using System.Text; | ||||||
|  |  | ||||||
| #if !KeePassUAP | using KeePassLib.Cryptography; | ||||||
| using System.Security.Cryptography; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| using KeePassLib.Native; | using KeePassLib.Native; | ||||||
| using KeePassLib.Utility; | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| @@ -37,7 +34,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; | ||||||
| @@ -50,7 +47,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 | ||||||
| 		{ | 		{ | ||||||
| @@ -69,13 +66,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) | ||||||
| @@ -100,7 +97,7 @@ 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; | ||||||
| @@ -114,7 +111,7 @@ namespace KeePassLib.Serialization | |||||||
|  |  | ||||||
| 				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 | ||||||
| 			{ | 			{ | ||||||
| @@ -142,7 +139,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; | ||||||
| @@ -209,9 +206,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)) | ||||||
| @@ -236,7 +233,7 @@ namespace KeePassLib.Serialization | |||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				m_bEos = true; | 				m_bEos = true; | ||||||
| 				m_pbBuffer = new byte[0]; | 				m_pbBuffer = MemUtil.EmptyByteArray; | ||||||
| 				return false; | 				return false; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -246,16 +243,12 @@ 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)) | ||||||
| 				{ | 					throw new InvalidDataException(); | ||||||
| 					if(pbStoredHash[iHashPos] != pbComputedHash[iHashPos]) |  | ||||||
| 						throw new InvalidDataException(); |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			return true; | 			return true; | ||||||
| @@ -283,26 +276,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 | 				// For 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); | ||||||
| 			} | 			} | ||||||
|   | |||||||
							
								
								
									
										325
									
								
								src/KeePassLib2Android/Serialization/HmacBlockStream.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								src/KeePassLib2Android/Serialization/HmacBlockStream.cs
									
									
									
									
									
										Normal 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; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -58,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; | ||||||
| @@ -84,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() | ||||||
| @@ -215,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) | ||||||
| @@ -383,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); | ||||||
| @@ -403,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); | ||||||
| @@ -434,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); | ||||||
| @@ -508,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) | ||||||
| 					{ | 					{ | ||||||
| @@ -609,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 | ||||||
| @@ -659,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; | ||||||
| @@ -883,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); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,8 +35,10 @@ using System.IO.Compression; | |||||||
| 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; | ||||||
| @@ -50,74 +52,108 @@ 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) | ||||||
| 				Stream readerStream = null; |  | ||||||
|  |  | ||||||
| 				if(kdbFormat == KdbxFormat.Default) |  | ||||||
| 				{ | 				{ | ||||||
| 					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); |  | ||||||
|  |  | ||||||
| 					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) | ||||||
|  |  | ||||||
| 					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); | ||||||
|  | 						lStreams.Add(sDecrypted); | ||||||
|  |  | ||||||
| 					Stream sHashed = new HashedBlockStream(sDecrypted, false, 0, | 						BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted, | ||||||
| 						!m_bRepairMode); | 							encNoBom, KLRes.FileCorrupted); | ||||||
|  | 						byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32); | ||||||
|  |  | ||||||
|  | 						if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32)) | ||||||
|  | 							throw new InvalidDataException(); | ||||||
|  | 						if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes)) | ||||||
|  | 							throw new InvalidCompositeKeyException(); | ||||||
|  |  | ||||||
|  | 						sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode); | ||||||
|  | 					} | ||||||
|  | 					else // KDBX >= 4 | ||||||
|  | 					{ | ||||||
|  | 						byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64); | ||||||
|  | 						byte[] pbStoredHmac = MemUtil.Read(sHashing, 32); | ||||||
|  | 						if((pbStoredHmac == null) || (pbStoredHmac.Length != 32)) | ||||||
|  | 							throw new InvalidDataException(); | ||||||
|  | 						if(!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac)) | ||||||
|  | 							throw new InvalidCompositeKeyException(); | ||||||
|  |  | ||||||
|  | 						HmacBlockStream sBlocks = new HmacBlockStream(sHashing, | ||||||
|  | 							false, !m_bRepairMode, pbHmacKey64); | ||||||
|  | 						lStreams.Add(sBlocks); | ||||||
|  |  | ||||||
|  | 						sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey, | ||||||
|  | 							cbEncIV, false); | ||||||
|  | 						if((sPlain == null) || (sPlain == sBlocks)) | ||||||
|  | 							throw new SecurityException(KLRes.CryptoStreamFailed); | ||||||
|  | 					} | ||||||
|  | 					lStreams.Add(sPlain); | ||||||
|  |  | ||||||
| 					if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip) | 					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) | ||||||
| 					{ | 					{ | ||||||
| @@ -137,7 +173,7 @@ namespace KeePassLib.Serialization | |||||||
| 				// { | 				// { | ||||||
| 				//	while(true) | 				//	while(true) | ||||||
| 				//	{ | 				//	{ | ||||||
| 				//		int b = readerStream.ReadByte(); | 				//		int b = sXml.ReadByte(); | ||||||
| 				//		if(b == -1) break; | 				//		if(b == -1) break; | ||||||
| 				//		fsOut.WriteByte((byte)b); | 				//		fsOut.WriteByte((byte)b); | ||||||
| 				//	} | 				//	} | ||||||
| @@ -146,26 +182,29 @@ namespace KeePassLib.Serialization | |||||||
| 				// fsOut.Close(); | 				// fsOut.Close(); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 				ReadXmlStreamed(readerStream, hashedStream); | 				ReadXmlStreamed(sXml, sHashing); | ||||||
| 				// ReadXmlDom(readerStream); | 				// 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) | ||||||
| @@ -187,7 +226,7 @@ namespace KeePassLib.Serialization | |||||||
| 			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); | ||||||
| @@ -212,18 +251,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) | ||||||
| @@ -232,15 +272,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; | ||||||
| 			} | 			} | ||||||
| @@ -266,13 +312,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: | ||||||
| @@ -285,6 +345,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; | ||||||
|  |  | ||||||
| @@ -292,6 +353,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) | ||||||
| @@ -305,7 +375,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); | ||||||
| @@ -329,35 +399,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); |  | ||||||
|  |  | ||||||
| 			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) | ||||||
| 		{ | 		{ | ||||||
|   | |||||||
| @@ -40,6 +40,7 @@ using System.IO.Compression; | |||||||
| 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; | ||||||
| @@ -54,7 +55,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,56 +73,117 @@ 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) | 				if(m_format == KdbxFormat.Default) | ||||||
| 				{ | 				{ | ||||||
| 					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"); | ||||||
|  | 				} | ||||||
|  |  | ||||||
| #if KeePassUAP | #if KeePassUAP | ||||||
| 				XmlWriterSettings xws = new XmlWriterSettings(); | 				XmlWriterSettings xws = new XmlWriterSettings(); | ||||||
| @@ -130,122 +192,125 @@ namespace KeePassLib.Serialization | |||||||
| 				xws.IndentChars = "\t"; | 				xws.IndentChars = "\t"; | ||||||
| 				xws.NewLineOnAttributes = false; | 				xws.NewLineOnAttributes = false; | ||||||
|  |  | ||||||
| 				XmlWriter xw = XmlWriter.Create(writerStream, xws); | 				XmlWriter xw = XmlWriter.Create(sXml, xws); | ||||||
| #else | #else | ||||||
| 				XmlTextWriter xw = new XmlTextWriter(writerStream, encNoBom); | 				XmlTextWriter xw = new XmlTextWriter(sXml, encNoBom); | ||||||
|  |  | ||||||
| 				xw.Formatting = Formatting.Indented; | 				xw.Formatting = Formatting.Indented; | ||||||
| 				xw.IndentChar = '\t'; | 				xw.IndentChar = '\t'; | ||||||
| 				xw.Indentation = 1; | 				xw.Indentation = 1; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 				m_xmlWriter = xw; | 				m_xmlWriter = xw; | ||||||
|  |  | ||||||
| 				WriteDocument(pgDataSource); | 				WriteDocument(pgDataSource); | ||||||
|  |  | ||||||
| 				m_xmlWriter.Flush(); | 				m_xmlWriter.Flush(); | ||||||
| 				m_xmlWriter.Close(); | 				m_xmlWriter.Close(); | ||||||
| 				writerStream.Close(); |  | ||||||
| 			} | 			} | ||||||
| 			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) | ||||||
| @@ -331,12 +396,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); | ||||||
| @@ -387,6 +455,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() | ||||||
| @@ -417,6 +488,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); } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,13 +22,21 @@ using System.Collections.Generic; | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using System.Globalization; | using System.Globalization; | ||||||
| using System.IO; | using System.IO; | ||||||
|  | using System.Security; | ||||||
| using System.Text; | using System.Text; | ||||||
| using System.Xml; | using System.Xml; | ||||||
|  |  | ||||||
|  | #if !KeePassUAP | ||||||
|  | using System.Security.Cryptography; | ||||||
|  | #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; | ||||||
|  |  | ||||||
| @@ -73,7 +81,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; | ||||||
|  |  | ||||||
| @@ -92,6 +101,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,14 +202,15 @@ namespace KeePassLib.Serialization | |||||||
| 		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 = | ||||||
| @@ -222,12 +233,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 | ||||||
| @@ -286,6 +299,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>(); | ||||||
|   | |||||||
| @@ -27,10 +27,10 @@ using System.Xml.Serialization; | |||||||
|  |  | ||||||
| #if !KeePassUAP | #if !KeePassUAP | ||||||
| using System.Drawing; | using System.Drawing; | ||||||
| using System.Security.Cryptography; |  | ||||||
| using System.Windows.Forms; | using System.Windows.Forms; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | using KeePassLib.Cryptography; | ||||||
| using KeePassLib.Utility; | using KeePassLib.Utility; | ||||||
|  |  | ||||||
| namespace KeePassLib.Translation | namespace KeePassLib.Translation | ||||||
| @@ -333,9 +333,7 @@ namespace KeePassLib.Translation | |||||||
| 			WriteControlDependentParams(sb, c); | 			WriteControlDependentParams(sb, c); | ||||||
|  |  | ||||||
| 			byte[] pb = StrUtil.Utf8.GetBytes(sb.ToString()); | 			byte[] pb = StrUtil.Utf8.GetBytes(sb.ToString()); | ||||||
|  | 			byte[] pbSha = CryptoUtil.HashSha256(pb); | ||||||
| 			SHA256Managed sha256 = new SHA256Managed(); |  | ||||||
| 			byte[] pbSha = sha256.ComputeHash(pb); |  | ||||||
|  |  | ||||||
| 			// Also see MatchHash | 			// Also see MatchHash | ||||||
| 			return "v1:" + Convert.ToBase64String(pbSha, 0, 3, | 			return "v1:" + Convert.ToBase64String(pbSha, 0, 3, | ||||||
|   | |||||||
| @@ -214,14 +214,11 @@ namespace KeePassLib.Utility | |||||||
| 			const int SizeICONDIR = 6; | 			const int SizeICONDIR = 6; | ||||||
| 			const int SizeICONDIRENTRY = 16; | 			const int SizeICONDIRENTRY = 16; | ||||||
|  |  | ||||||
| 			Debug.Assert(BitConverter.ToInt32(new byte[] { 1, 2, 3, 4 }, |  | ||||||
| 				0) == 0x04030201); // Little-endian |  | ||||||
|  |  | ||||||
| 			if(pb.Length < SizeICONDIR) return null; | 			if(pb.Length < SizeICONDIR) return null; | ||||||
| 			if(BitConverter.ToUInt16(pb, 0) != 0) return null; // Reserved, 0 | 			if(MemUtil.BytesToUInt16(pb, 0) != 0) return null; // Reserved, 0 | ||||||
| 			if(BitConverter.ToUInt16(pb, 2) != 1) return null; // ICO type, 1 | 			if(MemUtil.BytesToUInt16(pb, 2) != 1) return null; // ICO type, 1 | ||||||
|  |  | ||||||
| 			int n = BitConverter.ToUInt16(pb, 4); | 			int n = MemUtil.BytesToUInt16(pb, 4); | ||||||
| 			if(n < 0) { Debug.Assert(false); return null; } | 			if(n < 0) { Debug.Assert(false); return null; } | ||||||
|  |  | ||||||
| 			int cbDir = SizeICONDIR + (n * SizeICONDIRENTRY); | 			int cbDir = SizeICONDIR + (n * SizeICONDIRENTRY); | ||||||
| @@ -235,10 +232,10 @@ namespace KeePassLib.Utility | |||||||
| 				int h = pb[iOffset + 1]; | 				int h = pb[iOffset + 1]; | ||||||
| 				if((w < 0) || (h < 0)) { Debug.Assert(false); return null; } | 				if((w < 0) || (h < 0)) { Debug.Assert(false); return null; } | ||||||
|  |  | ||||||
| 				int cb = BitConverter.ToInt32(pb, iOffset + 8); | 				int cb = MemUtil.BytesToInt32(pb, iOffset + 8); | ||||||
| 				if(cb <= 0) return null; // Data must have header (even BMP) | 				if(cb <= 0) return null; // Data must have header (even BMP) | ||||||
|  |  | ||||||
| 				int p = BitConverter.ToInt32(pb, iOffset + 12); | 				int p = MemUtil.BytesToInt32(pb, iOffset + 12); | ||||||
| 				if(p < cbDir) return null; | 				if(p < cbDir) return null; | ||||||
| 				if((p + cb) > pb.Length) return null; | 				if((p + cb) > pb.Length) return null; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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, | ||||||
| @@ -262,6 +264,22 @@ namespace KeePassLib.Utility | |||||||
| 			Array.Clear(pbArray, 0, pbArray.Length); | 			Array.Clear(pbArray, 0, pbArray.Length); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Set all elements of an array to the default value. | ||||||
|  | 		/// </summary> | ||||||
|  | 		/// <param name="v">Input array.</param> | ||||||
|  | #if KeePassLibSD | ||||||
|  | 		[MethodImpl(MethodImplOptions.NoInlining)] | ||||||
|  | #else | ||||||
|  | 		[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] | ||||||
|  | #endif | ||||||
|  | 		public static void ZeroArray<T>(T[] v) | ||||||
|  | 		{ | ||||||
|  | 			if(v == null) { Debug.Assert(false); throw new ArgumentNullException("v"); } | ||||||
|  |  | ||||||
|  | 			Array.Clear(v, 0, v.Length); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Convert 2 bytes to a 16-bit unsigned integer (little-endian). | 		/// Convert 2 bytes to a 16-bit unsigned integer (little-endian). | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| @@ -269,11 +287,26 @@ namespace KeePassLib.Utility | |||||||
| 		{ | 		{ | ||||||
| 			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> | ||||||
|  | 		/// Convert 2 bytes to a 16-bit unsigned integer (little-endian). | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static ushort BytesToUInt16(byte[] pb, int iOffset) | ||||||
|  | 		{ | ||||||
|  | 			if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } | ||||||
|  | 			if((iOffset < 0) || ((iOffset + 1) >= pb.Length)) | ||||||
|  | 			{ | ||||||
|  | 				Debug.Assert(false); | ||||||
|  | 				throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return (ushort)((ushort)pb[iOffset] | ((ushort)pb[iOffset + 1] << 8)); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Convert 4 bytes to a 32-bit unsigned integer (little-endian). | 		/// Convert 4 bytes to a 32-bit unsigned integer (little-endian). | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| @@ -281,12 +314,28 @@ namespace KeePassLib.Utility | |||||||
| 		{ | 		{ | ||||||
| 			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(); | 			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> | ||||||
|  | 		/// Convert 4 bytes to a 32-bit unsigned integer (little-endian). | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static uint BytesToUInt32(byte[] pb, int iOffset) | ||||||
|  | 		{ | ||||||
|  | 			if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } | ||||||
|  | 			if((iOffset < 0) || ((iOffset + 3) >= pb.Length)) | ||||||
|  | 			{ | ||||||
|  | 				Debug.Assert(false); | ||||||
|  | 				throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return ((uint)pb[iOffset] | ((uint)pb[iOffset + 1] << 8) | | ||||||
|  | 				((uint)pb[iOffset + 2] << 16) | ((uint)pb[iOffset + 3] << 24)); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Convert 8 bytes to a 64-bit unsigned integer (little-endian). | 		/// Convert 8 bytes to a 64-bit unsigned integer (little-endian). | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| @@ -294,13 +343,54 @@ namespace KeePassLib.Utility | |||||||
| 		{ | 		{ | ||||||
| 			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> | ||||||
|  | 		/// Convert 8 bytes to a 64-bit unsigned integer (little-endian). | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static ulong BytesToUInt64(byte[] pb, int iOffset) | ||||||
|  | 		{ | ||||||
|  | 			if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } | ||||||
|  | 			if((iOffset < 0) || ((iOffset + 7) >= pb.Length)) | ||||||
|  | 			{ | ||||||
|  | 				Debug.Assert(false); | ||||||
|  | 				throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// if(BitConverter.IsLittleEndian) | ||||||
|  | 			//	return BitConverter.ToUInt64(pb, iOffset); | ||||||
|  |  | ||||||
|  | 			return ((ulong)pb[iOffset] | ((ulong)pb[iOffset + 1] << 8) | | ||||||
|  | 				((ulong)pb[iOffset + 2] << 16) | ((ulong)pb[iOffset + 3] << 24) | | ||||||
|  | 				((ulong)pb[iOffset + 4] << 32) | ((ulong)pb[iOffset + 5] << 40) | | ||||||
|  | 				((ulong)pb[iOffset + 6] << 48) | ((ulong)pb[iOffset + 7] << 56)); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public static int BytesToInt32(byte[] pb) | ||||||
|  | 		{ | ||||||
|  | 			return (int)BytesToUInt32(pb); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public static int BytesToInt32(byte[] pb, int iOffset) | ||||||
|  | 		{ | ||||||
|  | 			return (int)BytesToUInt32(pb, iOffset); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public static long BytesToInt64(byte[] pb) | ||||||
|  | 		{ | ||||||
|  | 			return (long)BytesToUInt64(pb); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		public static long BytesToInt64(byte[] pb, int iOffset) | ||||||
|  | 		{ | ||||||
|  | 			return (long)BytesToUInt64(pb, iOffset); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Convert a 16-bit unsigned integer to 2 bytes (little-endian). | 		/// Convert a 16-bit unsigned integer to 2 bytes (little-endian). | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| @@ -335,6 +425,27 @@ namespace KeePassLib.Utility | |||||||
| 			return pb; | 			return pb; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Convert a 32-bit unsigned integer to 4 bytes (little-endian). | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static void UInt32ToBytesEx(uint uValue, byte[] pb, int iOffset) | ||||||
|  | 		{ | ||||||
|  | 			if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } | ||||||
|  | 			if((iOffset < 0) || ((iOffset + 3) >= pb.Length)) | ||||||
|  | 			{ | ||||||
|  | 				Debug.Assert(false); | ||||||
|  | 				throw new ArgumentOutOfRangeException("iOffset"); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			unchecked | ||||||
|  | 			{ | ||||||
|  | 				pb[iOffset] = (byte)uValue; | ||||||
|  | 				pb[iOffset + 1] = (byte)(uValue >> 8); | ||||||
|  | 				pb[iOffset + 2] = (byte)(uValue >> 16); | ||||||
|  | 				pb[iOffset + 3] = (byte)(uValue >> 24); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Convert a 64-bit unsigned integer to 8 bytes (little-endian). | 		/// Convert a 64-bit unsigned integer to 8 bytes (little-endian). | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| @@ -357,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)! | ||||||
| @@ -372,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> | ||||||
| @@ -463,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) | ||||||
|   | |||||||
| @@ -70,6 +70,12 @@ namespace KeePassLib.Utility | |||||||
| 		// 1418: | 		// 1418: | ||||||
| 		//   Minimizing a form while loading it doesn't work. | 		//   Minimizing a form while loading it doesn't work. | ||||||
| 		//   https://sourceforge.net/p/keepass/bugs/1418/ | 		//   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: | 		// 5795: | ||||||
| 		//   Text in input field is incomplete. | 		//   Text in input field is incomplete. | ||||||
| 		//   https://bugzilla.xamarin.com/show_bug.cgi?id=5795 | 		//   https://bugzilla.xamarin.com/show_bug.cgi?id=5795 | ||||||
|   | |||||||
| @@ -978,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; | ||||||
|  |  | ||||||
| @@ -997,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) | ||||||
| 		{ | 		{ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Philipp Crocoll
					Philipp Crocoll