Keepass Orig KeePass_160826
This commit is contained in:
		@@ -32,14 +32,14 @@ using KeePassLibSD;
 | 
			
		||||
namespace KeePassLib.Collections
 | 
			
		||||
{
 | 
			
		||||
	public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
 | 
			
		||||
		IEnumerable<KeyValuePair<string, string>>
 | 
			
		||||
		IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
 | 
			
		||||
	{
 | 
			
		||||
		private SortedDictionary<string, string> m_vDict =
 | 
			
		||||
		private SortedDictionary<string, string> m_dict =
 | 
			
		||||
			new SortedDictionary<string, string>();
 | 
			
		||||
 | 
			
		||||
		public int Count
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_vDict.Count; }
 | 
			
		||||
			get { return m_dict.Count; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public StringDictionaryEx()
 | 
			
		||||
@@ -48,39 +48,53 @@ namespace KeePassLib.Collections
 | 
			
		||||
 | 
			
		||||
		IEnumerator IEnumerable.GetEnumerator()
 | 
			
		||||
		{
 | 
			
		||||
			return m_vDict.GetEnumerator();
 | 
			
		||||
			return m_dict.GetEnumerator();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
 | 
			
		||||
		{
 | 
			
		||||
			return m_vDict.GetEnumerator();
 | 
			
		||||
			return m_dict.GetEnumerator();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public StringDictionaryEx CloneDeep()
 | 
			
		||||
		{
 | 
			
		||||
			StringDictionaryEx plNew = new StringDictionaryEx();
 | 
			
		||||
			StringDictionaryEx sdNew = new StringDictionaryEx();
 | 
			
		||||
 | 
			
		||||
			foreach(KeyValuePair<string, string> kvpStr in m_vDict)
 | 
			
		||||
				plNew.Set(kvpStr.Key, kvpStr.Value);
 | 
			
		||||
			foreach(KeyValuePair<string, string> kvp in m_dict)
 | 
			
		||||
				sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
 | 
			
		||||
 | 
			
		||||
			return plNew;
 | 
			
		||||
			return sdNew;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public bool Equals(StringDictionaryEx sdOther)
 | 
			
		||||
		{
 | 
			
		||||
			if(sdOther == null) { Debug.Assert(false); return false; }
 | 
			
		||||
 | 
			
		||||
			if(m_dict.Count != sdOther.m_dict.Count) return false;
 | 
			
		||||
 | 
			
		||||
			foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
 | 
			
		||||
			{
 | 
			
		||||
				string str = Get(kvp.Key);
 | 
			
		||||
				if((str == null) || (str != kvp.Value)) return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public string Get(string strName)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
 | 
			
		||||
			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
 | 
			
		||||
 | 
			
		||||
			string s;
 | 
			
		||||
			if(m_vDict.TryGetValue(strName, out s)) return s;
 | 
			
		||||
 | 
			
		||||
			if(m_dict.TryGetValue(strName, out s)) return s;
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public bool Exists(string strName)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
 | 
			
		||||
			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
 | 
			
		||||
 | 
			
		||||
			return m_vDict.ContainsKey(strName);
 | 
			
		||||
			return m_dict.ContainsKey(strName);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -92,25 +106,25 @@ namespace KeePassLib.Collections
 | 
			
		||||
		/// parameters is <c>null</c>.</exception>
 | 
			
		||||
		public void Set(string strField, string strNewValue)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
 | 
			
		||||
			Debug.Assert(strNewValue != null); if(strNewValue == null) throw new ArgumentNullException("strNewValue");
 | 
			
		||||
			if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
 | 
			
		||||
			if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
 | 
			
		||||
 | 
			
		||||
			m_vDict[strField] = strNewValue;
 | 
			
		||||
			m_dict[strField] = strNewValue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Delete a string.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="strField">Name of the string field to delete.</param>
 | 
			
		||||
		/// <returns>Returns <c>true</c> if the field has been successfully
 | 
			
		||||
		/// removed, otherwise the return value is <c>false</c>.</returns>
 | 
			
		||||
		/// <returns>Returns <c>true</c>, if the field has been successfully
 | 
			
		||||
		/// removed. Otherwise, the return value is <c>false</c>.</returns>
 | 
			
		||||
		/// <exception cref="System.ArgumentNullException">Thrown if the input
 | 
			
		||||
		/// parameter is <c>null</c>.</exception>
 | 
			
		||||
		public bool Remove(string strField)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
 | 
			
		||||
			if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
 | 
			
		||||
 | 
			
		||||
			return m_vDict.Remove(strField);
 | 
			
		||||
			return m_dict.Remove(strField);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
			{
 | 
			
		||||
				if(m_poolGlobal != null) return m_poolGlobal;
 | 
			
		||||
				CipherPool cp = m_poolGlobal;
 | 
			
		||||
				if(cp == null)
 | 
			
		||||
				{
 | 
			
		||||
					cp = new CipherPool();
 | 
			
		||||
					cp.AddCipher(new StandardAesEngine());
 | 
			
		||||
					cp.AddCipher(new ChaCha20Engine());
 | 
			
		||||
 | 
			
		||||
				m_poolGlobal = new CipherPool();
 | 
			
		||||
				m_poolGlobal.AddCipher(new StandardAesEngine());
 | 
			
		||||
					m_poolGlobal = cp;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return m_poolGlobal;
 | 
			
		||||
				return cp;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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>
 | 
			
		||||
		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
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// 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.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
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 byte[] m_output = new byte[64];
 | 
			
		||||
		private int m_outputPos = 64;
 | 
			
		||||
 | 
			
		||||
		private static readonly uint[] m_sigma = new uint[4] {
 | 
			
		||||
		private static readonly uint[] g_sigma = new uint[4] {
 | 
			
		||||
			0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8)
 | 
			
		||||
		public override int BlockSize
 | 
			
		||||
		{
 | 
			
		||||
			KeySetup(pbKey32);
 | 
			
		||||
			IvSetup(pbIV8);
 | 
			
		||||
			get { return 64; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~Salsa20Cipher()
 | 
			
		||||
		public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) : base()
 | 
			
		||||
		{
 | 
			
		||||
			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);
 | 
			
		||||
			GC.SuppressFinalize(this);
 | 
			
		||||
			MemUtil.ZeroArray<uint>(m_s);
 | 
			
		||||
			MemUtil.ZeroArray<uint>(m_x);
 | 
			
		||||
 | 
			
		||||
			base.Dispose(bDisposing);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void Dispose(bool bDisposing)
 | 
			
		||||
		protected override void NextBlock(byte[] pBlock)
 | 
			
		||||
		{
 | 
			
		||||
			// Clear sensitive data
 | 
			
		||||
			Array.Clear(m_state, 0, m_state.Length);
 | 
			
		||||
			Array.Clear(m_x, 0, m_x.Length);
 | 
			
		||||
		}
 | 
			
		||||
			if(pBlock == null) throw new ArgumentNullException("pBlock");
 | 
			
		||||
			if(pBlock.Length != 64) throw new ArgumentOutOfRangeException("pBlock");
 | 
			
		||||
 | 
			
		||||
		private void NextOutput()
 | 
			
		||||
		{
 | 
			
		||||
			uint[] x = m_x; // Local alias for working buffer
 | 
			
		||||
 | 
			
		||||
			// Compiler/runtime might remove array bound checks after this
 | 
			
		||||
			// x is a local alias for the working buffer; with this,
 | 
			
		||||
			// the compiler/runtime might remove some checks
 | 
			
		||||
			uint[] x = m_x;
 | 
			
		||||
			if(x == null) throw new InvalidOperationException();
 | 
			
		||||
			if(x.Length < 16) throw new InvalidOperationException();
 | 
			
		||||
 | 
			
		||||
			Array.Copy(m_state, x, 16);
 | 
			
		||||
			uint[] s = m_s;
 | 
			
		||||
			if(s == null) throw new InvalidOperationException();
 | 
			
		||||
			if(s.Length < 16) throw new InvalidOperationException();
 | 
			
		||||
 | 
			
		||||
			Array.Copy(s, x, 16);
 | 
			
		||||
 | 
			
		||||
			unchecked
 | 
			
		||||
			{
 | 
			
		||||
				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[ 8] ^= Rotl32(x[ 4] + x[ 0],  9);
 | 
			
		||||
					x[12] ^= Rotl32(x[ 8] + x[ 4], 13);
 | 
			
		||||
					x[ 0] ^= Rotl32(x[12] + x[ 8], 18);
 | 
			
		||||
					x[ 9] ^= Rotl32(x[ 5] + x[ 1],  7);
 | 
			
		||||
					x[13] ^= Rotl32(x[ 9] + x[ 5],  9);
 | 
			
		||||
					x[ 1] ^= Rotl32(x[13] + x[ 9], 13);
 | 
			
		||||
					x[ 5] ^= Rotl32(x[ 1] + x[13], 18);
 | 
			
		||||
					x[14] ^= Rotl32(x[10] + x[ 6],  7);
 | 
			
		||||
					x[ 2] ^= Rotl32(x[14] + x[10],  9);
 | 
			
		||||
					x[ 6] ^= Rotl32(x[ 2] + x[14], 13);
 | 
			
		||||
					x[10] ^= Rotl32(x[ 6] + x[ 2], 18);
 | 
			
		||||
					x[ 3] ^= Rotl32(x[15] + x[11],  7);
 | 
			
		||||
					x[ 7] ^= Rotl32(x[ 3] + x[15],  9);
 | 
			
		||||
					x[11] ^= Rotl32(x[ 7] + x[ 3], 13);
 | 
			
		||||
					x[15] ^= Rotl32(x[11] + x[ 7], 18);
 | 
			
		||||
					x[ 1] ^= Rotl32(x[ 0] + x[ 3],  7);
 | 
			
		||||
					x[ 2] ^= Rotl32(x[ 1] + x[ 0],  9);
 | 
			
		||||
					x[ 3] ^= Rotl32(x[ 2] + x[ 1], 13);
 | 
			
		||||
					x[ 0] ^= Rotl32(x[ 3] + x[ 2], 18);
 | 
			
		||||
					x[ 6] ^= Rotl32(x[ 5] + x[ 4],  7);
 | 
			
		||||
					x[ 7] ^= Rotl32(x[ 6] + x[ 5],  9);
 | 
			
		||||
					x[ 4] ^= Rotl32(x[ 7] + x[ 6], 13);
 | 
			
		||||
					x[ 5] ^= Rotl32(x[ 4] + x[ 7], 18);
 | 
			
		||||
					x[11] ^= Rotl32(x[10] + x[ 9],  7);
 | 
			
		||||
					x[ 8] ^= Rotl32(x[11] + x[10],  9);
 | 
			
		||||
					x[ 9] ^= Rotl32(x[ 8] + x[11], 13);
 | 
			
		||||
					x[10] ^= Rotl32(x[ 9] + x[ 8], 18);
 | 
			
		||||
					x[12] ^= Rotl32(x[15] + x[14],  7);
 | 
			
		||||
					x[13] ^= Rotl32(x[12] + x[15],  9);
 | 
			
		||||
					x[14] ^= Rotl32(x[13] + x[12], 13);
 | 
			
		||||
					x[15] ^= Rotl32(x[14] + x[13], 18);
 | 
			
		||||
					x[ 4] ^= MemUtil.RotateLeft32(x[ 0] + x[12],  7);
 | 
			
		||||
					x[ 8] ^= MemUtil.RotateLeft32(x[ 4] + x[ 0],  9);
 | 
			
		||||
					x[12] ^= MemUtil.RotateLeft32(x[ 8] + x[ 4], 13);
 | 
			
		||||
					x[ 0] ^= MemUtil.RotateLeft32(x[12] + x[ 8], 18);
 | 
			
		||||
 | 
			
		||||
					x[ 9] ^= MemUtil.RotateLeft32(x[ 5] + x[ 1],  7);
 | 
			
		||||
					x[13] ^= MemUtil.RotateLeft32(x[ 9] + x[ 5],  9);
 | 
			
		||||
					x[ 1] ^= MemUtil.RotateLeft32(x[13] + x[ 9], 13);
 | 
			
		||||
					x[ 5] ^= MemUtil.RotateLeft32(x[ 1] + x[13], 18);
 | 
			
		||||
 | 
			
		||||
					x[14] ^= MemUtil.RotateLeft32(x[10] + x[ 6],  7);
 | 
			
		||||
					x[ 2] ^= MemUtil.RotateLeft32(x[14] + x[10],  9);
 | 
			
		||||
					x[ 6] ^= MemUtil.RotateLeft32(x[ 2] + x[14], 13);
 | 
			
		||||
					x[10] ^= MemUtil.RotateLeft32(x[ 6] + x[ 2], 18);
 | 
			
		||||
 | 
			
		||||
					x[ 3] ^= MemUtil.RotateLeft32(x[15] + x[11],  7);
 | 
			
		||||
					x[ 7] ^= MemUtil.RotateLeft32(x[ 3] + x[15],  9);
 | 
			
		||||
					x[11] ^= MemUtil.RotateLeft32(x[ 7] + x[ 3], 13);
 | 
			
		||||
					x[15] ^= MemUtil.RotateLeft32(x[11] + x[ 7], 18);
 | 
			
		||||
 | 
			
		||||
					x[ 1] ^= MemUtil.RotateLeft32(x[ 0] + x[ 3],  7);
 | 
			
		||||
					x[ 2] ^= MemUtil.RotateLeft32(x[ 1] + x[ 0],  9);
 | 
			
		||||
					x[ 3] ^= MemUtil.RotateLeft32(x[ 2] + x[ 1], 13);
 | 
			
		||||
					x[ 0] ^= MemUtil.RotateLeft32(x[ 3] + x[ 2], 18);
 | 
			
		||||
 | 
			
		||||
					x[ 6] ^= MemUtil.RotateLeft32(x[ 5] + x[ 4],  7);
 | 
			
		||||
					x[ 7] ^= MemUtil.RotateLeft32(x[ 6] + x[ 5],  9);
 | 
			
		||||
					x[ 4] ^= MemUtil.RotateLeft32(x[ 7] + x[ 6], 13);
 | 
			
		||||
					x[ 5] ^= MemUtil.RotateLeft32(x[ 4] + x[ 7], 18);
 | 
			
		||||
 | 
			
		||||
					x[11] ^= MemUtil.RotateLeft32(x[10] + x[ 9],  7);
 | 
			
		||||
					x[ 8] ^= MemUtil.RotateLeft32(x[11] + x[10],  9);
 | 
			
		||||
					x[ 9] ^= MemUtil.RotateLeft32(x[ 8] + x[11], 13);
 | 
			
		||||
					x[10] ^= MemUtil.RotateLeft32(x[ 9] + x[ 8], 18);
 | 
			
		||||
 | 
			
		||||
					x[12] ^= MemUtil.RotateLeft32(x[15] + x[14],  7);
 | 
			
		||||
					x[13] ^= MemUtil.RotateLeft32(x[12] + x[15],  9);
 | 
			
		||||
					x[14] ^= MemUtil.RotateLeft32(x[13] + x[12], 13);
 | 
			
		||||
					x[15] ^= MemUtil.RotateLeft32(x[14] + x[13], 18);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				for(int i = 0; i < 16; ++i)
 | 
			
		||||
					x[i] += m_state[i];
 | 
			
		||||
				for(int i = 0; i < 16; ++i) x[i] += s[i];
 | 
			
		||||
 | 
			
		||||
				for(int i = 0; i < 16; ++i)
 | 
			
		||||
				{
 | 
			
		||||
					m_output[i << 2] = (byte)x[i];
 | 
			
		||||
					m_output[(i << 2) + 1] = (byte)(x[i] >> 8);
 | 
			
		||||
					m_output[(i << 2) + 2] = (byte)(x[i] >> 16);
 | 
			
		||||
					m_output[(i << 2) + 3] = (byte)(x[i] >> 24);
 | 
			
		||||
					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);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				m_outputPos = 0;
 | 
			
		||||
				++m_state[8];
 | 
			
		||||
				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;
 | 
			
		||||
				++s[8];
 | 
			
		||||
				if(s[8] == 0) ++s[9];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ namespace KeePassLib.Cryptography.Cipher
 | 
			
		||||
		private const PaddingMode m_rCipherPadding = PaddingMode.PKCS7;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		private static PwUuid m_uuidAes = null;
 | 
			
		||||
		private static PwUuid g_uuidAes = null;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// UUID of the cipher engine. This ID uniquely identifies the
 | 
			
		||||
@@ -52,12 +52,16 @@ namespace KeePassLib.Cryptography.Cipher
 | 
			
		||||
		{
 | 
			
		||||
			get
 | 
			
		||||
			{
 | 
			
		||||
				if(m_uuidAes == null)
 | 
			
		||||
					m_uuidAes = new PwUuid(new byte[]{
 | 
			
		||||
				PwUuid pu = g_uuidAes;
 | 
			
		||||
				if(pu == null)
 | 
			
		||||
				{
 | 
			
		||||
					pu = new PwUuid(new byte[] {
 | 
			
		||||
						0x31, 0xC1, 0xF2, 0xE6, 0xBF, 0x71, 0x43, 0x50,
 | 
			
		||||
						0xBE, 0x58, 0x05, 0x21, 0x6A, 0xFC, 0x5A, 0xFF });
 | 
			
		||||
					g_uuidAes = pu;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return m_uuidAes;
 | 
			
		||||
				return pu;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -72,7 +76,14 @@ namespace KeePassLib.Cryptography.Cipher
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Get a displayable name describing this cipher engine.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public string DisplayName { get { return KLRes.EncAlgorithmAes; } }
 | 
			
		||||
		public string DisplayName
 | 
			
		||||
		{
 | 
			
		||||
			get
 | 
			
		||||
			{
 | 
			
		||||
				return ("AES/Rijndael (" + KLRes.KeyBits.Replace(@"{PARAM}",
 | 
			
		||||
					"256") + ", FIPS 197)");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static void ValidateArguments(Stream stream, bool bEncrypt, byte[] pbKey, byte[] pbIV)
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
@@ -40,14 +40,14 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
	public sealed class CryptoRandom
 | 
			
		||||
	{
 | 
			
		||||
		private byte[] m_pbEntropyPool = new byte[64];
 | 
			
		||||
		private uint m_uCounter;
 | 
			
		||||
		private ulong m_uCounter;
 | 
			
		||||
		private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
 | 
			
		||||
		private ulong m_uGeneratedBytesCount = 0;
 | 
			
		||||
 | 
			
		||||
		private static object g_oSyncRoot = new object();
 | 
			
		||||
		private object m_oSyncRoot = new object();
 | 
			
		||||
 | 
			
		||||
		private static CryptoRandom m_pInstance = null;
 | 
			
		||||
		private static CryptoRandom g_pInstance = null;
 | 
			
		||||
		public static CryptoRandom Instance
 | 
			
		||||
		{
 | 
			
		||||
			get
 | 
			
		||||
@@ -55,11 +55,11 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
				CryptoRandom cr;
 | 
			
		||||
				lock(g_oSyncRoot)
 | 
			
		||||
				{
 | 
			
		||||
					cr = m_pInstance;
 | 
			
		||||
					cr = g_pInstance;
 | 
			
		||||
					if(cr == null)
 | 
			
		||||
					{
 | 
			
		||||
						cr = new CryptoRandom();
 | 
			
		||||
						m_pInstance = cr;
 | 
			
		||||
						g_pInstance = cr;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
@@ -90,10 +90,12 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
 | 
			
		||||
		private CryptoRandom()
 | 
			
		||||
		{
 | 
			
		||||
			Random r = new Random();
 | 
			
		||||
			m_uCounter = (uint)r.Next();
 | 
			
		||||
			Random rWeak = new Random();
 | 
			
		||||
			byte[] pb = new byte[8];
 | 
			
		||||
			rWeak.NextBytes(pb);
 | 
			
		||||
			m_uCounter = MemUtil.BytesToUInt64(pb);
 | 
			
		||||
 | 
			
		||||
			AddEntropy(GetSystemData(r));
 | 
			
		||||
			AddEntropy(GetSystemData(rWeak));
 | 
			
		||||
			AddEntropy(GetCspData());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -109,32 +111,40 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
 | 
			
		||||
 | 
			
		||||
			byte[] pbNewData = pbEntropy;
 | 
			
		||||
			if(pbEntropy.Length >= 64)
 | 
			
		||||
			if(pbEntropy.Length > 64)
 | 
			
		||||
			{
 | 
			
		||||
#if KeePassLibSD
 | 
			
		||||
				SHA256Managed shaNew = new SHA256Managed();
 | 
			
		||||
				using(SHA256Managed shaNew = new SHA256Managed())
 | 
			
		||||
#else
 | 
			
		||||
				SHA512Managed shaNew = new SHA512Managed();
 | 
			
		||||
				using(SHA512Managed shaNew = new SHA512Managed())
 | 
			
		||||
#endif
 | 
			
		||||
				{
 | 
			
		||||
					pbNewData = shaNew.ComputeHash(pbEntropy);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			MemoryStream ms = new MemoryStream();
 | 
			
		||||
			lock(m_oSyncRoot)
 | 
			
		||||
			{
 | 
			
		||||
				ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
 | 
			
		||||
				ms.Write(pbNewData, 0, pbNewData.Length);
 | 
			
		||||
				int cbPool = m_pbEntropyPool.Length;
 | 
			
		||||
				int cbNew = pbNewData.Length;
 | 
			
		||||
 | 
			
		||||
				byte[] 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
 | 
			
		||||
				SHA256Managed shaPool = new SHA256Managed();
 | 
			
		||||
				using(SHA256Managed shaPool = new SHA256Managed())
 | 
			
		||||
#else
 | 
			
		||||
				Debug.Assert(pbFinal.Length == (64 + pbNewData.Length));
 | 
			
		||||
				SHA512Managed shaPool = new SHA512Managed();
 | 
			
		||||
				using(SHA512Managed shaPool = new SHA512Managed())
 | 
			
		||||
#endif
 | 
			
		||||
				m_pbEntropyPool = shaPool.ComputeHash(pbFinal);
 | 
			
		||||
				{
 | 
			
		||||
					m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				MemUtil.ZeroByteArray(pbCmp);
 | 
			
		||||
			}
 | 
			
		||||
			ms.Close();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static byte[] GetSystemData(Random rWeak)
 | 
			
		||||
@@ -142,11 +152,11 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			MemoryStream ms = new MemoryStream();
 | 
			
		||||
			byte[] pb;
 | 
			
		||||
 | 
			
		||||
			pb = MemUtil.UInt32ToBytes((uint)Environment.TickCount);
 | 
			
		||||
			ms.Write(pb, 0, pb.Length);
 | 
			
		||||
			pb = MemUtil.Int32ToBytes(Environment.TickCount);
 | 
			
		||||
			MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
			pb = TimeUtil.PackTime(DateTime.Now);
 | 
			
		||||
			ms.Write(pb, 0, pb.Length);
 | 
			
		||||
			pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
 | 
			
		||||
			MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
#if !KeePassLibSD
 | 
			
		||||
			// In try-catch for systems without GUI;
 | 
			
		||||
@@ -154,79 +164,79 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				Point pt = Cursor.Position;
 | 
			
		||||
				pb = MemUtil.UInt32ToBytes((uint)pt.X);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt32ToBytes((uint)pt.Y);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.Int32ToBytes(pt.X);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int32ToBytes(pt.Y);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
			}
 | 
			
		||||
			catch(Exception) { }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
			pb = MemUtil.UInt32ToBytes((uint)rWeak.Next());
 | 
			
		||||
			ms.Write(pb, 0, pb.Length);
 | 
			
		||||
			pb = MemUtil.Int32ToBytes(rWeak.Next());
 | 
			
		||||
			MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
			pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
 | 
			
		||||
			ms.Write(pb, 0, pb.Length);
 | 
			
		||||
			MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				pb = MemUtil.UInt32ToBytes((uint)Environment.ProcessorCount);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.Int32ToBytes(Environment.ProcessorCount);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
#if KeePassUAP
 | 
			
		||||
				Version v = EnvironmentExt.OSVersion.Version;
 | 
			
		||||
#else
 | 
			
		||||
				Version v = Environment.OSVersion.Version;
 | 
			
		||||
#endif
 | 
			
		||||
				pb = MemUtil.UInt32ToBytes((uint)v.GetHashCode());
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.Int32ToBytes(v.GetHashCode());
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
#if !KeePassUAP
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)Environment.WorkingSet);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(Environment.WorkingSet);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
#endif
 | 
			
		||||
			}
 | 
			
		||||
			catch(Exception) { Debug.Assert(false); }
 | 
			
		||||
 | 
			
		||||
#if KeePassUAP
 | 
			
		||||
			pb = DiagnosticsExt.GetProcessEntropy();
 | 
			
		||||
			ms.Write(pb, 0, pb.Length);
 | 
			
		||||
			MemUtil.Write(ms, pb);
 | 
			
		||||
#elif !KeePassLibSD
 | 
			
		||||
			Process p = null;
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				p = Process.GetCurrentProcess();
 | 
			
		||||
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.Handle.ToInt64());
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt32ToBytes((uint)p.HandleCount);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt32ToBytes((uint)p.Id);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.NonpagedSystemMemorySize64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.PagedMemorySize64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.PagedSystemMemorySize64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.PeakPagedMemorySize64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.PeakVirtualMemorySize64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.PeakWorkingSet64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.PrivateMemorySize64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.StartTime.ToBinary());
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.VirtualMemorySize64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.UInt64ToBytes((ulong)p.WorkingSet64);
 | 
			
		||||
				ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int32ToBytes(p.HandleCount);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int32ToBytes(p.Id);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
				pb = MemUtil.Int64ToBytes(p.WorkingSet64);
 | 
			
		||||
				MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
				// Not supported in Mono 1.2.6:
 | 
			
		||||
				// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
 | 
			
		||||
				// ms.Write(pb, 0, pb.Length);
 | 
			
		||||
				// MemUtil.Write(ms, pb);
 | 
			
		||||
			}
 | 
			
		||||
			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
 | 
			
		||||
			finally
 | 
			
		||||
@@ -237,7 +247,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
			pb = Guid.NewGuid().ToByteArray();
 | 
			
		||||
			ms.Write(pb, 0, pb.Length);
 | 
			
		||||
			MemUtil.Write(ms, pb);
 | 
			
		||||
 | 
			
		||||
			byte[] pbAll = ms.ToArray();
 | 
			
		||||
			ms.Close();
 | 
			
		||||
@@ -256,28 +266,31 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			if(this.GenerateRandom256Pre != null)
 | 
			
		||||
				this.GenerateRandom256Pre(this, EventArgs.Empty);
 | 
			
		||||
 | 
			
		||||
			byte[] pbFinal;
 | 
			
		||||
			byte[] pbCmp;
 | 
			
		||||
			lock(m_oSyncRoot)
 | 
			
		||||
			{
 | 
			
		||||
				unchecked { m_uCounter += 386047; } // Prime number
 | 
			
		||||
				byte[] pbCounter = MemUtil.UInt32ToBytes(m_uCounter);
 | 
			
		||||
				m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
 | 
			
		||||
				byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
 | 
			
		||||
 | 
			
		||||
				byte[] pbCspRandom = GetCspData();
 | 
			
		||||
 | 
			
		||||
				MemoryStream ms = new MemoryStream();
 | 
			
		||||
				ms.Write(m_pbEntropyPool, 0, m_pbEntropyPool.Length);
 | 
			
		||||
				ms.Write(pbCounter, 0, pbCounter.Length);
 | 
			
		||||
				ms.Write(pbCspRandom, 0, pbCspRandom.Length);
 | 
			
		||||
				pbFinal = ms.ToArray();
 | 
			
		||||
				Debug.Assert(pbFinal.Length == (m_pbEntropyPool.Length +
 | 
			
		||||
					pbCounter.Length + pbCspRandom.Length));
 | 
			
		||||
				ms.Close();
 | 
			
		||||
				int cbPool = m_pbEntropyPool.Length;
 | 
			
		||||
				int cbCtr = pbCounter.Length;
 | 
			
		||||
				int cbCsp = pbCspRandom.Length;
 | 
			
		||||
 | 
			
		||||
				pbCmp = new byte[cbPool + cbCtr + cbCsp];
 | 
			
		||||
				Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
 | 
			
		||||
				Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
 | 
			
		||||
				Array.Copy(pbCspRandom, 0, pbCmp, cbPool + cbCtr, cbCsp);
 | 
			
		||||
 | 
			
		||||
				MemUtil.ZeroByteArray(pbCspRandom);
 | 
			
		||||
 | 
			
		||||
				m_uGeneratedBytesCount += 32;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			return sha256.ComputeHash(pbFinal);
 | 
			
		||||
			byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
 | 
			
		||||
			MemUtil.ZeroByteArray(pbCmp);
 | 
			
		||||
			return pbRet;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -289,29 +302,32 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
		/// random bytes.</returns>
 | 
			
		||||
		public byte[] GetRandomBytes(uint uRequestedBytes)
 | 
			
		||||
		{
 | 
			
		||||
			if(uRequestedBytes == 0) return new byte[0]; // Allow zero-length array
 | 
			
		||||
			if(uRequestedBytes == 0) return MemUtil.EmptyByteArray;
 | 
			
		||||
			if(uRequestedBytes > (uint)int.MaxValue)
 | 
			
		||||
			{
 | 
			
		||||
				Debug.Assert(false);
 | 
			
		||||
				throw new ArgumentOutOfRangeException("uRequestedBytes");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			byte[] pbRes = new byte[uRequestedBytes];
 | 
			
		||||
			long lPos = 0;
 | 
			
		||||
			int cbRem = (int)uRequestedBytes;
 | 
			
		||||
			byte[] pbRes = new byte[cbRem];
 | 
			
		||||
			int iPos = 0;
 | 
			
		||||
 | 
			
		||||
			while(uRequestedBytes != 0)
 | 
			
		||||
			while(cbRem != 0)
 | 
			
		||||
			{
 | 
			
		||||
				byte[] pbRandom256 = GenerateRandom256();
 | 
			
		||||
				Debug.Assert(pbRandom256.Length == 32);
 | 
			
		||||
 | 
			
		||||
				long lCopy = (long)((uRequestedBytes < 32) ? uRequestedBytes : 32);
 | 
			
		||||
				int cbCopy = Math.Min(cbRem, 32);
 | 
			
		||||
				Array.Copy(pbRandom256, 0, pbRes, iPos, cbCopy);
 | 
			
		||||
 | 
			
		||||
#if (!KeePassLibSD && !KeePassUAP)
 | 
			
		||||
				Array.Copy(pbRandom256, 0, pbRes, lPos, lCopy);
 | 
			
		||||
#else
 | 
			
		||||
				Array.Copy(pbRandom256, 0, pbRes, (int)lPos, (int)lCopy);
 | 
			
		||||
#endif
 | 
			
		||||
				MemUtil.ZeroByteArray(pbRandom256);
 | 
			
		||||
 | 
			
		||||
				lPos += lCopy;
 | 
			
		||||
				uRequestedBytes -= (uint)lCopy;
 | 
			
		||||
				iPos += cbCopy;
 | 
			
		||||
				cbRem -= cbCopy;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Debug.Assert((int)lPos == pbRes.Length);
 | 
			
		||||
			Debug.Assert(iPos == pbRes.Length);
 | 
			
		||||
			return pbRes;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ using System.Security.Cryptography;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Cryptography.Cipher;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
namespace KeePassLib.Cryptography
 | 
			
		||||
{
 | 
			
		||||
@@ -40,6 +41,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// A variant of the ARCFour algorithm (RC4 incompatible).
 | 
			
		||||
		/// Insecure; for backward compatibility only.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		ArcFourVariant = 1,
 | 
			
		||||
 | 
			
		||||
@@ -48,7 +50,12 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		Salsa20 = 2,
 | 
			
		||||
 | 
			
		||||
		Count = 3
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// ChaCha20 stream cipher algorithm.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		ChaCha20 = 3,
 | 
			
		||||
 | 
			
		||||
		Count = 4
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// <summary>
 | 
			
		||||
@@ -59,45 +66,68 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public sealed class CryptoRandomStream
 | 
			
		||||
	{
 | 
			
		||||
		private CrsAlgorithm m_crsAlgorithm;
 | 
			
		||||
		private readonly CrsAlgorithm m_crsAlgorithm;
 | 
			
		||||
 | 
			
		||||
		private byte[] m_pbState = null;
 | 
			
		||||
		private byte m_i = 0;
 | 
			
		||||
		private byte m_j = 0;
 | 
			
		||||
 | 
			
		||||
		private Salsa20Cipher m_salsa20 = null;
 | 
			
		||||
		private ChaCha20Cipher m_chacha20 = null;
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Construct a new cryptographically secure random stream object.
 | 
			
		||||
		/// </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
 | 
			
		||||
		/// must contain at least 1 byte.</param>
 | 
			
		||||
		/// <exception cref="System.ArgumentNullException">Thrown if the
 | 
			
		||||
		/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
 | 
			
		||||
		/// <exception cref="System.ArgumentException">Thrown if the
 | 
			
		||||
		/// <paramref name="pbKey" /> parameter contains no bytes or the
 | 
			
		||||
		/// algorithm is unknown.</exception>
 | 
			
		||||
		public CryptoRandomStream(CrsAlgorithm genAlgorithm, byte[] pbKey)
 | 
			
		||||
		public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
 | 
			
		||||
		{
 | 
			
		||||
			m_crsAlgorithm = genAlgorithm;
 | 
			
		||||
			if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
 | 
			
		||||
 | 
			
		||||
			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;
 | 
			
		||||
			Debug.Assert(uKeyLen != 0); if(uKeyLen == 0) throw new ArgumentException();
 | 
			
		||||
			m_crsAlgorithm = a;
 | 
			
		||||
 | 
			
		||||
			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
 | 
			
		||||
				m_pbState = new byte[256];
 | 
			
		||||
				for(uint w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
 | 
			
		||||
				for(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
 | 
			
		||||
 | 
			
		||||
				unchecked
 | 
			
		||||
				{
 | 
			
		||||
					byte j = 0, t;
 | 
			
		||||
					uint inxKey = 0;
 | 
			
		||||
					for(uint w = 0; w < 256; ++w) // Key setup
 | 
			
		||||
					int inxKey = 0;
 | 
			
		||||
					for(int w = 0; w < 256; ++w) // Key setup
 | 
			
		||||
					{
 | 
			
		||||
						j += (byte)(m_pbState[w] + pbKey[inxKey]);
 | 
			
		||||
 | 
			
		||||
@@ -106,25 +136,16 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
						m_pbState[j] = t;
 | 
			
		||||
 | 
			
		||||
						++inxKey;
 | 
			
		||||
						if(inxKey >= uKeyLen) inxKey = 0;
 | 
			
		||||
						if(inxKey >= cbKey) inxKey = 0;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				GetRandomBytes(512); // Increases security, see cryptanalysis
 | 
			
		||||
			}
 | 
			
		||||
			else if(genAlgorithm == CrsAlgorithm.Salsa20)
 | 
			
		||||
			{
 | 
			
		||||
				SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
				byte[] pbKey32 = sha256.ComputeHash(pbKey);
 | 
			
		||||
				byte[] pbIV = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
 | 
			
		||||
					0x97, 0x20, 0x5D, 0x2A }; // Unique constant
 | 
			
		||||
 | 
			
		||||
				m_salsa20 = new Salsa20Cipher(pbKey32, pbIV);
 | 
			
		||||
			}
 | 
			
		||||
			else // Unknown algorithm
 | 
			
		||||
			{
 | 
			
		||||
				Debug.Assert(false);
 | 
			
		||||
				throw new ArgumentException();
 | 
			
		||||
				throw new ArgumentOutOfRangeException("a");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -135,15 +156,23 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
		/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
 | 
			
		||||
		public byte[] GetRandomBytes(uint uRequestedCount)
 | 
			
		||||
		{
 | 
			
		||||
			if(uRequestedCount == 0) return new byte[0];
 | 
			
		||||
			if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
 | 
			
		||||
 | 
			
		||||
			byte[] pbRet = new byte[uRequestedCount];
 | 
			
		||||
			if(uRequestedCount > (uint)int.MaxValue)
 | 
			
		||||
				throw new ArgumentOutOfRangeException("uRequestedCount");
 | 
			
		||||
			int cb = (int)uRequestedCount;
 | 
			
		||||
 | 
			
		||||
			if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
 | 
			
		||||
			byte[] pbRet = new byte[cb];
 | 
			
		||||
 | 
			
		||||
			if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
 | 
			
		||||
				m_chacha20.Encrypt(pbRet, 0, cb);
 | 
			
		||||
			else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
 | 
			
		||||
				m_salsa20.Encrypt(pbRet, 0, cb);
 | 
			
		||||
			else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
 | 
			
		||||
			{
 | 
			
		||||
				unchecked
 | 
			
		||||
				{
 | 
			
		||||
					for(uint w = 0; w < uRequestedCount; ++w)
 | 
			
		||||
					for(int w = 0; w < cb; ++w)
 | 
			
		||||
					{
 | 
			
		||||
						++m_i;
 | 
			
		||||
						m_j += m_pbState[m_i];
 | 
			
		||||
@@ -157,8 +186,6 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
 | 
			
		||||
				m_salsa20.Encrypt(pbRet, pbRet.Length, false);
 | 
			
		||||
			else { Debug.Assert(false); }
 | 
			
		||||
 | 
			
		||||
			return pbRet;
 | 
			
		||||
@@ -167,14 +194,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
		public ulong GetRandomUInt64()
 | 
			
		||||
		{
 | 
			
		||||
			byte[] pb = GetRandomBytes(8);
 | 
			
		||||
 | 
			
		||||
			unchecked
 | 
			
		||||
			{
 | 
			
		||||
				return ((ulong)pb[0]) | ((ulong)pb[1] << 8) |
 | 
			
		||||
					((ulong)pb[2] << 16) | ((ulong)pb[3] << 24) |
 | 
			
		||||
					((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) |
 | 
			
		||||
					((ulong)pb[6] << 48) | ((ulong)pb[7] << 56);
 | 
			
		||||
			}
 | 
			
		||||
			return MemUtil.BytesToUInt64(pb);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
#if CRSBENCHMARK
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
	{
 | 
			
		||||
		private Stream m_sBaseStream;
 | 
			
		||||
		private bool m_bWriting;
 | 
			
		||||
		private readonly bool m_bWriting;
 | 
			
		||||
		private HashAlgorithm m_hash;
 | 
			
		||||
 | 
			
		||||
		private byte[] m_pbFinalHash = null;
 | 
			
		||||
@@ -67,7 +67,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
		public override long 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)
 | 
			
		||||
@@ -114,7 +114,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			{
 | 
			
		||||
				try
 | 
			
		||||
				{
 | 
			
		||||
					m_hash.TransformFinalBlock(new byte[0], 0, 0);
 | 
			
		||||
					m_hash.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
 | 
			
		||||
 | 
			
		||||
					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.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
namespace KeePassLib.Cryptography.PasswordGenerator
 | 
			
		||||
{
 | 
			
		||||
@@ -62,16 +64,20 @@ namespace KeePassLib.Cryptography.PasswordGenerator
 | 
			
		||||
 | 
			
		||||
		private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy)
 | 
			
		||||
		{
 | 
			
		||||
			byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(256);
 | 
			
		||||
			byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(128);
 | 
			
		||||
 | 
			
		||||
			// Mix in additional entropy
 | 
			
		||||
			Debug.Assert(pbKey.Length >= 64);
 | 
			
		||||
			if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
 | 
			
		||||
			{
 | 
			
		||||
				for(int nKeyPos = 0; nKeyPos < pbKey.Length; ++nKeyPos)
 | 
			
		||||
					pbKey[nKeyPos] ^= pbAdditionalEntropy[nKeyPos % pbAdditionalEntropy.Length];
 | 
			
		||||
				using(SHA512Managed h = new SHA512Managed())
 | 
			
		||||
				{
 | 
			
		||||
					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,
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Security;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
@@ -33,6 +34,8 @@ using System.Security.Cryptography;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Cryptography.Cipher;
 | 
			
		||||
using KeePassLib.Cryptography.Hash;
 | 
			
		||||
using KeePassLib.Cryptography.KeyDerivation;
 | 
			
		||||
using KeePassLib.Keys;
 | 
			
		||||
using KeePassLib.Native;
 | 
			
		||||
using KeePassLib.Resources;
 | 
			
		||||
@@ -45,17 +48,6 @@ using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
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>
 | 
			
		||||
	/// Class containing self-test methods.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
@@ -70,6 +62,10 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
 | 
			
		||||
			TestRijndael();
 | 
			
		||||
			TestSalsa20();
 | 
			
		||||
			TestChaCha20();
 | 
			
		||||
			TestBlake2b();
 | 
			
		||||
			TestArgon2();
 | 
			
		||||
			TestHmac();
 | 
			
		||||
 | 
			
		||||
			TestNativeKeyTransform();
 | 
			
		||||
			
 | 
			
		||||
@@ -92,14 +88,14 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
		internal static void TestFipsComplianceProblems()
 | 
			
		||||
		{
 | 
			
		||||
#if !KeePassUAP
 | 
			
		||||
			try { new RijndaelManaged(); }
 | 
			
		||||
			try { using(RijndaelManaged r = new RijndaelManaged()) { } }
 | 
			
		||||
			catch(Exception exAes)
 | 
			
		||||
			{
 | 
			
		||||
				throw new SecurityException("AES/Rijndael: " + exAes.Message);
 | 
			
		||||
			}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
			try { new SHA256Managed(); }
 | 
			
		||||
			try { using(SHA256Managed h = new SHA256Managed()) { } }
 | 
			
		||||
			catch(Exception exSha256)
 | 
			
		||||
			{
 | 
			
		||||
				throw new SecurityException("SHA-256: " + exSha256.Message);
 | 
			
		||||
@@ -126,7 +122,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			AesEngine r = new AesEngine();
 | 
			
		||||
			r.Init(true, new KeyParameter(pbTestKey));
 | 
			
		||||
			if(r.GetBlockSize() != pbTestData.Length)
 | 
			
		||||
				throw new SecurityException(KLRes.EncAlgorithmAes + " (BS).");
 | 
			
		||||
				throw new SecurityException("AES (BC)");
 | 
			
		||||
			r.ProcessBlock(pbTestData, 0, pbTestData, 0);
 | 
			
		||||
#else
 | 
			
		||||
			RijndaelManaged r = new RijndaelManaged();
 | 
			
		||||
@@ -147,11 +143,12 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
			if(!MemUtil.ArraysEqual(pbTestData, pbReferenceCT))
 | 
			
		||||
				throw new SecurityException(KLRes.EncAlgorithmAes + ".");
 | 
			
		||||
				throw new SecurityException("AES");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static void TestSalsa20()
 | 
			
		||||
		{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
			// Test values from official set 6, vector 3
 | 
			
		||||
			byte[] pbKey = new byte[32] {
 | 
			
		||||
				0x0F, 0x62, 0xB5, 0x08, 0x5B, 0xAE, 0x01, 0x54,
 | 
			
		||||
@@ -168,12 +165,11 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
 | 
			
		||||
			byte[] pb = new byte[16];
 | 
			
		||||
			Salsa20Cipher c = new Salsa20Cipher(pbKey, pbIV);
 | 
			
		||||
			c.Encrypt(pb, pb.Length, false);
 | 
			
		||||
			c.Encrypt(pb, 0, pb.Length);
 | 
			
		||||
			if(!MemUtil.ArraysEqual(pb, pbExpected))
 | 
			
		||||
				throw new SecurityException("Salsa20-1");
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
			// Extended test in debug mode
 | 
			
		||||
			// Extended test
 | 
			
		||||
			byte[] pbExpected2 = new byte[16] {
 | 
			
		||||
				0xAB, 0xF3, 0x9A, 0x21, 0x0E, 0xEE, 0x89, 0x59,
 | 
			
		||||
				0x8B, 0x71, 0x33, 0x37, 0x70, 0x56, 0xC2, 0xFE
 | 
			
		||||
@@ -185,13 +181,14 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
 | 
			
		||||
			Random r = new Random();
 | 
			
		||||
			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))
 | 
			
		||||
				throw new SecurityException("Salsa20-2");
 | 
			
		||||
 | 
			
		||||
			nPos = Salsa20ToPos(c, r, nPos + pb.Length, 131008);
 | 
			
		||||
			Array.Clear(pb, 0, pb.Length);
 | 
			
		||||
			c.Encrypt(pb, pb.Length, true);
 | 
			
		||||
			c.Encrypt(pb, 0, pb.Length);
 | 
			
		||||
			if(!MemUtil.ArraysEqual(pb, pbExpected3))
 | 
			
		||||
				throw new SecurityException("Salsa20-3");
 | 
			
		||||
 | 
			
		||||
@@ -200,8 +197,8 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			for(int i = 0; i < nRounds; ++i)
 | 
			
		||||
			{
 | 
			
		||||
				byte[] z = new byte[32];
 | 
			
		||||
				c = new Salsa20Cipher(z, BitConverter.GetBytes((long)i));
 | 
			
		||||
				c.Encrypt(z, z.Length, true);
 | 
			
		||||
				c = new Salsa20Cipher(z, MemUtil.Int64ToBytes(i));
 | 
			
		||||
				c.Encrypt(z, 0, z.Length);
 | 
			
		||||
				d[MemUtil.ByteArrayToHexString(z)] = true;
 | 
			
		||||
			}
 | 
			
		||||
			if(d.Count != nRounds) throw new SecurityException("Salsa20-4");
 | 
			
		||||
@@ -218,7 +215,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			{
 | 
			
		||||
				int x = r.Next(1, 513);
 | 
			
		||||
				int nGen = Math.Min(nTargetPos - nPos, x);
 | 
			
		||||
				c.Encrypt(pb, nGen, r.Next(0, 2) == 0);
 | 
			
		||||
				c.Encrypt(pb, 0, nGen);
 | 
			
		||||
				nPos += nGen;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -226,6 +223,475 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
		}
 | 
			
		||||
#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()
 | 
			
		||||
		{
 | 
			
		||||
#if DEBUG
 | 
			
		||||
@@ -235,8 +701,8 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
 | 
			
		||||
			byte[] pbManaged = new byte[32];
 | 
			
		||||
			Array.Copy(pbOrgKey, pbManaged, 32);
 | 
			
		||||
			if(!CompositeKey.TransformKeyManaged(pbManaged, pbSeed, uRounds))
 | 
			
		||||
				throw new SecurityException("Managed transform.");
 | 
			
		||||
			if(!AesKdf.TransformKeyManaged(pbManaged, pbSeed, uRounds))
 | 
			
		||||
				throw new SecurityException("AES-KDF-1");
 | 
			
		||||
 | 
			
		||||
			byte[] pbNative = new byte[32];
 | 
			
		||||
			Array.Copy(pbOrgKey, pbNative, 32);
 | 
			
		||||
@@ -244,7 +710,7 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
				return; // Native library not available ("success")
 | 
			
		||||
 | 
			
		||||
			if(!MemUtil.ArraysEqual(pbManaged, pbNative))
 | 
			
		||||
				throw new SecurityException("Native transform.");
 | 
			
		||||
				throw new SecurityException("AES-KDF-2");
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -310,6 +776,15 @@ namespace KeePassLib.Cryptography
 | 
			
		||||
			pbRes = MemUtil.ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======");
 | 
			
		||||
			pbExp = Encoding.ASCII.GetBytes("Key provider based on one-time passwords.");
 | 
			
		||||
			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
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,14 +22,8 @@ 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.Cryptography.KeyDerivation;
 | 
			
		||||
using KeePassLib.Native;
 | 
			
		||||
using KeePassLib.Resources;
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
@@ -197,8 +191,7 @@ namespace KeePassLib.Keys
 | 
			
		||||
			}
 | 
			
		||||
			Debug.Assert(p == cbData);
 | 
			
		||||
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			byte[] pbHash = sha256.ComputeHash(pbAllData);
 | 
			
		||||
			byte[] pbHash = CryptoUtil.HashSha256(pbAllData);
 | 
			
		||||
			MemUtil.ZeroByteArray(pbAllData);
 | 
			
		||||
			return pbHash;
 | 
			
		||||
		}
 | 
			
		||||
@@ -216,15 +209,7 @@ namespace KeePassLib.Keys
 | 
			
		||||
			return bResult;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Generate a 32-bit wide key out of the composite key.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="pbKeySeed32">Seed used in the key transformation
 | 
			
		||||
		/// rounds. Must be a byte array containing exactly 32 bytes; must
 | 
			
		||||
		/// not be null.</param>
 | 
			
		||||
		/// <param name="uNumRounds">Number of key transformation rounds.</param>
 | 
			
		||||
		/// <returns>Returns a protected binary object that contains the
 | 
			
		||||
		/// resulting 32-bit wide key.</returns>
 | 
			
		||||
		[Obsolete]
 | 
			
		||||
		public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(pbKeySeed32 != null);
 | 
			
		||||
@@ -232,18 +217,43 @@ namespace KeePassLib.Keys
 | 
			
		||||
			Debug.Assert(pbKeySeed32.Length == 32);
 | 
			
		||||
			if(pbKeySeed32.Length != 32) throw new ArgumentException("pbKeySeed32");
 | 
			
		||||
 | 
			
		||||
			AesKdf kdf = new AesKdf();
 | 
			
		||||
			KdfParameters p = kdf.GetDefaultParameters();
 | 
			
		||||
			p.SetUInt64(AesKdf.ParamRounds, uNumRounds);
 | 
			
		||||
			p.SetByteArray(AesKdf.ParamSeed, pbKeySeed32);
 | 
			
		||||
 | 
			
		||||
			return GenerateKey32(p);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Generate a 32-byte (256-bit) key from the composite key.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public ProtectedBinary GenerateKey32(KdfParameters p)
 | 
			
		||||
		{
 | 
			
		||||
			if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
 | 
			
		||||
 | 
			
		||||
			byte[] pbRaw32 = CreateRawCompositeKey32();
 | 
			
		||||
			if((pbRaw32 == null) || (pbRaw32.Length != 32))
 | 
			
		||||
				{ Debug.Assert(false); return null; }
 | 
			
		||||
 | 
			
		||||
			byte[] pbTrf32 = TransformKey(pbRaw32, pbKeySeed32, uNumRounds);
 | 
			
		||||
			if((pbTrf32 == null) || (pbTrf32.Length != 32))
 | 
			
		||||
				{ Debug.Assert(false); return null; }
 | 
			
		||||
			KdfEngine kdf = KdfPool.Get(p.KdfUuid);
 | 
			
		||||
			if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
 | 
			
		||||
				throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
 | 
			
		||||
					KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
 | 
			
		||||
					"UUID: " + p.KdfUuid.ToHexString() + ".");
 | 
			
		||||
 | 
			
		||||
			byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
 | 
			
		||||
			if(pbTrf32 == null) { Debug.Assert(false); return null; }
 | 
			
		||||
 | 
			
		||||
			if(pbTrf32.Length != 32)
 | 
			
		||||
			{
 | 
			
		||||
				Debug.Assert(false);
 | 
			
		||||
				pbTrf32 = CryptoUtil.HashSha256(pbTrf32);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ProtectedBinary pbRet = new ProtectedBinary(true, pbTrf32);
 | 
			
		||||
			MemUtil.ZeroByteArray(pbTrf32);
 | 
			
		||||
			MemUtil.ZeroByteArray(pbRaw32);
 | 
			
		||||
 | 
			
		||||
			return pbRet;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -263,182 +273,6 @@ namespace KeePassLib.Keys
 | 
			
		||||
				throw new InvalidOperationException();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Transform the current key <c>uNumRounds</c> times.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="pbOriginalKey32">The original key which will be transformed.
 | 
			
		||||
		/// This parameter won't be modified.</param>
 | 
			
		||||
		/// <param name="pbKeySeed32">Seed used for key transformations. Must not
 | 
			
		||||
		/// be <c>null</c>. This parameter won't be modified.</param>
 | 
			
		||||
		/// <param name="uNumRounds">Transformation count.</param>
 | 
			
		||||
		/// <returns>256-bit transformed key.</returns>
 | 
			
		||||
		private static byte[] TransformKey(byte[] pbOriginalKey32, byte[] pbKeySeed32,
 | 
			
		||||
			ulong uNumRounds)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert((pbOriginalKey32 != null) && (pbOriginalKey32.Length == 32));
 | 
			
		||||
			if(pbOriginalKey32 == null) throw new ArgumentNullException("pbOriginalKey32");
 | 
			
		||||
			if(pbOriginalKey32.Length != 32) throw new ArgumentException();
 | 
			
		||||
 | 
			
		||||
			Debug.Assert((pbKeySeed32 != null) && (pbKeySeed32.Length == 32));
 | 
			
		||||
			if(pbKeySeed32 == null) throw new ArgumentNullException("pbKeySeed32");
 | 
			
		||||
			if(pbKeySeed32.Length != 32) throw new ArgumentException();
 | 
			
		||||
 | 
			
		||||
			byte[] pbNewKey = new byte[32];
 | 
			
		||||
			Array.Copy(pbOriginalKey32, pbNewKey, pbNewKey.Length);
 | 
			
		||||
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				// 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
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,7 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
#if !KeePassUAP
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
 | 
			
		||||
namespace KeePassLib.Keys
 | 
			
		||||
@@ -57,8 +54,7 @@ namespace KeePassLib.Keys
 | 
			
		||||
 | 
			
		||||
			if(bPerformHash)
 | 
			
		||||
			{
 | 
			
		||||
				SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
				byte[] pbRaw = sha256.ComputeHash(pbKeyData);
 | 
			
		||||
				byte[] pbRaw = CryptoUtil.HashSha256(pbKeyData);
 | 
			
		||||
				m_pbKey = new ProtectedBinary(true, pbRaw);
 | 
			
		||||
			}
 | 
			
		||||
			else m_pbKey = new ProtectedBinary(true, pbKeyData);
 | 
			
		||||
 
 | 
			
		||||
@@ -133,10 +133,7 @@ namespace KeePassLib.Keys
 | 
			
		||||
			else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
 | 
			
		||||
 | 
			
		||||
			if(pbKey == null)
 | 
			
		||||
			{
 | 
			
		||||
				SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
				pbKey = sha256.ComputeHash(pbFileData);
 | 
			
		||||
			}
 | 
			
		||||
				pbKey = CryptoUtil.HashSha256(pbFileData);
 | 
			
		||||
 | 
			
		||||
			return pbKey;
 | 
			
		||||
		}
 | 
			
		||||
@@ -156,12 +153,15 @@ namespace KeePassLib.Keys
 | 
			
		||||
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				string strHex = StrUtil.Utf8.GetString(pbFileData, 0, 64);
 | 
			
		||||
				if(!StrUtil.IsHexString(strHex, true)) return null;
 | 
			
		||||
				if(!StrUtil.IsHexString(pbFileData, true)) return null;
 | 
			
		||||
 | 
			
		||||
				string strHex = StrUtil.Utf8.GetString(pbFileData);
 | 
			
		||||
				byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
 | 
			
		||||
				if((pbKey == null) || (pbKey.Length != 32))
 | 
			
		||||
				{
 | 
			
		||||
					Debug.Assert(false);
 | 
			
		||||
					return null;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return pbKey;
 | 
			
		||||
			}
 | 
			
		||||
@@ -189,13 +189,13 @@ namespace KeePassLib.Keys
 | 
			
		||||
				pbFinalKey32 = pbKey32;
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				MemoryStream ms = new MemoryStream();
 | 
			
		||||
				ms.Write(pbAdditionalEntropy, 0, pbAdditionalEntropy.Length);
 | 
			
		||||
				ms.Write(pbKey32, 0, 32);
 | 
			
		||||
				using(MemoryStream ms = new MemoryStream())
 | 
			
		||||
				{
 | 
			
		||||
					MemUtil.Write(ms, pbAdditionalEntropy);
 | 
			
		||||
					MemUtil.Write(ms, pbKey32);
 | 
			
		||||
 | 
			
		||||
				SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
				pbFinalKey32 = sha256.ComputeHash(ms.ToArray());
 | 
			
		||||
				ms.Close();
 | 
			
		||||
					pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			CreateXmlKeyFile(strFilePath, pbFinalKey32);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,7 @@ using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
#if !KeePassUAP
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
@@ -75,8 +72,7 @@ namespace KeePassLib.Keys
 | 
			
		||||
			Debug.Assert(ValidatePassword(pbPasswordUtf8));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			byte[] pbRaw = sha256.ComputeHash(pbPasswordUtf8);
 | 
			
		||||
			byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
 | 
			
		||||
 | 
			
		||||
			m_psPassword = new ProtectedString(true, pbPasswordUtf8);
 | 
			
		||||
			m_pbKeyData = new ProtectedBinary(true, pbRaw);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ using System.Drawing;
 | 
			
		||||
using KeePassLib.Collections;
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Cryptography.Cipher;
 | 
			
		||||
using KeePassLib.Cryptography.KeyDerivation;
 | 
			
		||||
using KeePassLib.Delegates;
 | 
			
		||||
using KeePassLib.Interfaces;
 | 
			
		||||
using KeePassLib.Keys;
 | 
			
		||||
@@ -50,13 +51,14 @@ namespace KeePassLib
 | 
			
		||||
 | 
			
		||||
		private static bool m_bPrimaryCreated = false;
 | 
			
		||||
 | 
			
		||||
		// Initializations see Clear()
 | 
			
		||||
		// Initializations: see Clear()
 | 
			
		||||
		private PwGroup m_pgRootGroup = null;
 | 
			
		||||
		private PwObjectList<PwDeletedObject> m_vDeletedObjects = new PwObjectList<PwDeletedObject>();
 | 
			
		||||
 | 
			
		||||
		private PwUuid m_uuidDataCipher = StandardAesEngine.AesUuid;
 | 
			
		||||
		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 MemoryProtectionConfig m_memProtConfig = new MemoryProtectionConfig();
 | 
			
		||||
@@ -64,6 +66,7 @@ namespace KeePassLib
 | 
			
		||||
		private List<PwCustomIcon> m_vCustomIcons = new List<PwCustomIcon>();
 | 
			
		||||
		private bool m_bUINeedsIconUpdate = true;
 | 
			
		||||
 | 
			
		||||
		private DateTime m_dtSettingsChanged = PwDefs.DtDefaultNow;
 | 
			
		||||
		private string m_strName = string.Empty;
 | 
			
		||||
		private DateTime m_dtNameChanged = PwDefs.DtDefaultNow;
 | 
			
		||||
		private string m_strDesc = string.Empty;
 | 
			
		||||
@@ -93,7 +96,8 @@ namespace KeePassLib
 | 
			
		||||
		private int m_nHistoryMaxItems = DefaultHistoryMaxItems;
 | 
			
		||||
		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_pbHashOfLastIO = null;
 | 
			
		||||
@@ -168,6 +172,12 @@ namespace KeePassLib
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public DateTime SettingsChanged
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_dtSettingsChanged; }
 | 
			
		||||
			set { m_dtSettingsChanged = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Name of the database.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
@@ -281,14 +291,23 @@ namespace KeePassLib
 | 
			
		||||
			set { m_caCompression = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Number of key transformation rounds (in order to make dictionary
 | 
			
		||||
		/// attacks harder).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public ulong KeyEncryptionRounds
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// Number of key transformation rounds (KDF parameter).
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// public ulong KeyEncryptionRounds
 | 
			
		||||
		// {
 | 
			
		||||
		//	get { return m_uKeyEncryptionRounds; }
 | 
			
		||||
		//	set { m_uKeyEncryptionRounds = value; }
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		public KdfParameters KdfParameters
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_uKeyEncryptionRounds; }
 | 
			
		||||
			set { m_uKeyEncryptionRounds = value; }
 | 
			
		||||
			get { return m_kdfParams; }
 | 
			
		||||
			set
 | 
			
		||||
			{
 | 
			
		||||
				if(value == null) throw new ArgumentNullException("value");
 | 
			
		||||
				m_kdfParams = value;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -408,14 +427,37 @@ namespace KeePassLib
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Custom data container that can be used by plugins to store
 | 
			
		||||
		/// 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>
 | 
			
		||||
		public StringDictionaryEx CustomData
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_vCustomData; }
 | 
			
		||||
			set
 | 
			
		||||
			get { return m_dCustomData; }
 | 
			
		||||
			internal set
 | 
			
		||||
			{
 | 
			
		||||
				Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
 | 
			
		||||
				m_vCustomData = value;
 | 
			
		||||
				if(value == null) { Debug.Assert(false); throw new ArgumentNullException("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_caCompression = PwCompressionAlgorithm.GZip;
 | 
			
		||||
			m_uKeyEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds;
 | 
			
		||||
			// m_uKeyEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds;
 | 
			
		||||
			m_kdfParams = KdfPool.GetDefaultParameters();
 | 
			
		||||
 | 
			
		||||
			m_pwUserKey = null;
 | 
			
		||||
			m_memProtConfig = new MemoryProtectionConfig();
 | 
			
		||||
@@ -494,6 +537,7 @@ namespace KeePassLib
 | 
			
		||||
 | 
			
		||||
			DateTime dtNow = DateTime.Now;
 | 
			
		||||
 | 
			
		||||
			m_dtSettingsChanged = dtNow;
 | 
			
		||||
			m_strName = string.Empty;
 | 
			
		||||
			m_dtNameChanged = dtNow;
 | 
			
		||||
			m_strDesc = string.Empty;
 | 
			
		||||
@@ -523,7 +567,8 @@ namespace KeePassLib
 | 
			
		||||
			m_nHistoryMaxItems = DefaultHistoryMaxItems;
 | 
			
		||||
			m_lHistoryMaxSize = DefaultHistoryMaxSize;
 | 
			
		||||
 | 
			
		||||
			m_vCustomData = new StringDictionaryEx();
 | 
			
		||||
			m_dCustomData = new StringDictionaryEx();
 | 
			
		||||
			m_dPublicCustomData = new VariantDictionary();
 | 
			
		||||
 | 
			
		||||
			m_pbHashOfFileOnDisk = null;
 | 
			
		||||
			m_pbHashOfLastIO = null;
 | 
			
		||||
@@ -1393,6 +1438,14 @@ namespace KeePassLib
 | 
			
		||||
				return;
 | 
			
		||||
 | 
			
		||||
			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))
 | 
			
		||||
			{
 | 
			
		||||
@@ -1412,8 +1465,6 @@ namespace KeePassLib
 | 
			
		||||
				m_dtDefaultUserChanged = pdSource.m_dtDefaultUserChanged;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(bForce) m_clr = pdSource.m_clr;
 | 
			
		||||
 | 
			
		||||
			PwUuid pwPrefBin = m_pwRecycleBin, pwAltBin = pdSource.m_pwRecycleBin;
 | 
			
		||||
			if(bForce || (pdSource.m_dtRecycleBinChanged > m_dtRecycleBinChanged))
 | 
			
		||||
			{
 | 
			
		||||
@@ -1440,6 +1491,16 @@ namespace KeePassLib
 | 
			
		||||
			else if(m_pgRootGroup.FindGroup(pwAltTmp, true) != null)
 | 
			
		||||
				m_pwEntryTemplatesGroup = pwAltTmp;
 | 
			
		||||
			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,
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,8 @@ namespace KeePassLib
 | 
			
		||||
 | 
			
		||||
		private List<string> m_vTags = new List<string>();
 | 
			
		||||
 | 
			
		||||
		private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// UUID of this entry.
 | 
			
		||||
		/// </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 EventHandler<ObjectTouchedEventArgs> Touched;
 | 
			
		||||
 | 
			
		||||
@@ -366,6 +385,8 @@ namespace KeePassLib
 | 
			
		||||
 | 
			
		||||
			peNew.m_vTags = new List<string>(m_vTags);
 | 
			
		||||
 | 
			
		||||
			peNew.m_dCustomData = m_dCustomData.CloneDeep();
 | 
			
		||||
 | 
			
		||||
			return peNew;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -486,6 +507,8 @@ namespace KeePassLib
 | 
			
		||||
				if(m_vTags[iTag] != pe.m_vTags[iTag]) return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(!m_dCustomData.Equals(pe.m_dCustomData)) return false;
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -502,10 +525,10 @@ namespace KeePassLib
 | 
			
		||||
		public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer,
 | 
			
		||||
			bool bIncludeHistory, bool bAssignLocationChanged)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(peTemplate != null); if(peTemplate == null) throw new ArgumentNullException("peTemplate");
 | 
			
		||||
			if(peTemplate == null) { Debug.Assert(false); throw new ArgumentNullException("peTemplate"); }
 | 
			
		||||
 | 
			
		||||
			if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.m_tLastMod, m_tLastMod,
 | 
			
		||||
				true) < 0))
 | 
			
		||||
			if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.m_tLastMod,
 | 
			
		||||
				m_tLastMod, true) < 0))
 | 
			
		||||
				return;
 | 
			
		||||
 | 
			
		||||
			// Template UUID should be the same as the current one
 | 
			
		||||
@@ -515,10 +538,11 @@ namespace KeePassLib
 | 
			
		||||
			if(bAssignLocationChanged)
 | 
			
		||||
				m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
 | 
			
		||||
 | 
			
		||||
			m_listStrings = peTemplate.m_listStrings;
 | 
			
		||||
			m_listBinaries = peTemplate.m_listBinaries;
 | 
			
		||||
			m_listAutoType = peTemplate.m_listAutoType;
 | 
			
		||||
			if(bIncludeHistory) m_listHistory = peTemplate.m_listHistory;
 | 
			
		||||
			m_listStrings = peTemplate.m_listStrings.CloneDeep();
 | 
			
		||||
			m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
 | 
			
		||||
			m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
 | 
			
		||||
			if(bIncludeHistory)
 | 
			
		||||
				m_listHistory = peTemplate.m_listHistory.CloneDeep();
 | 
			
		||||
 | 
			
		||||
			m_pwIcon = peTemplate.m_pwIcon;
 | 
			
		||||
			m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
 | 
			
		||||
@@ -536,6 +560,8 @@ namespace KeePassLib
 | 
			
		||||
			m_strOverrideUrl = peTemplate.m_strOverrideUrl;
 | 
			
		||||
 | 
			
		||||
			m_vTags = new List<string>(peTemplate.m_vTags);
 | 
			
		||||
 | 
			
		||||
			m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -787,6 +813,9 @@ namespace KeePassLib
 | 
			
		||||
			foreach(string strTag in m_vTags)
 | 
			
		||||
				uSize += (ulong)strTag.Length;
 | 
			
		||||
 | 
			
		||||
			foreach(KeyValuePair<string, string> kvp in m_dCustomData)
 | 
			
		||||
				uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
 | 
			
		||||
 | 
			
		||||
			return uSize;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,8 @@ namespace KeePassLib
 | 
			
		||||
 | 
			
		||||
		private PwUuid m_pwLastTopVisibleEntry = PwUuid.Zero;
 | 
			
		||||
 | 
			
		||||
		private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// UUID of this group.
 | 
			
		||||
		/// </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 EventHandler<ObjectTouchedEventArgs> Touched;
 | 
			
		||||
 | 
			
		||||
@@ -376,6 +395,8 @@ namespace KeePassLib
 | 
			
		||||
 | 
			
		||||
			pg.m_pwLastTopVisibleEntry = m_pwLastTopVisibleEntry;
 | 
			
		||||
 | 
			
		||||
			pg.m_dCustomData = m_dCustomData.CloneDeep();
 | 
			
		||||
 | 
			
		||||
			return pg;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -444,6 +465,8 @@ namespace KeePassLib
 | 
			
		||||
 | 
			
		||||
			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(m_listEntries.UCount != pg.m_listEntries.UCount) return false;
 | 
			
		||||
@@ -509,6 +532,8 @@ namespace KeePassLib
 | 
			
		||||
			m_bEnableSearching = pgTemplate.m_bEnableSearching;
 | 
			
		||||
 | 
			
		||||
			m_pwLastTopVisibleEntry = pgTemplate.m_pwLastTopVisibleEntry;
 | 
			
		||||
 | 
			
		||||
			m_dCustomData = pgTemplate.m_dCustomData.CloneDeep();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ namespace KeePassLib.Resources
 | 
			
		||||
			if(dictNew == null) throw new ArgumentNullException("dictNew");
 | 
			
		||||
 | 
			
		||||
			m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed);
 | 
			
		||||
			m_strEncAlgorithmAes = TryGetEx(dictNew, "EncAlgorithmAes", m_strEncAlgorithmAes);
 | 
			
		||||
			m_strEncDataTooLarge = TryGetEx(dictNew, "EncDataTooLarge", m_strEncDataTooLarge);
 | 
			
		||||
			m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
 | 
			
		||||
			m_strExpect100Continue = TryGetEx(dictNew, "Expect100Continue", m_strExpect100Continue);
 | 
			
		||||
			m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError);
 | 
			
		||||
@@ -36,6 +36,7 @@ namespace KeePassLib.Resources
 | 
			
		||||
			m_strFileHeaderEndEarly = TryGetEx(dictNew, "FileHeaderEndEarly", m_strFileHeaderEndEarly);
 | 
			
		||||
			m_strFileLoadFailed = TryGetEx(dictNew, "FileLoadFailed", m_strFileLoadFailed);
 | 
			
		||||
			m_strFileLockedWrite = TryGetEx(dictNew, "FileLockedWrite", m_strFileLockedWrite);
 | 
			
		||||
			m_strFileNewVerOrPlgReq = TryGetEx(dictNew, "FileNewVerOrPlgReq", m_strFileNewVerOrPlgReq);
 | 
			
		||||
			m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq);
 | 
			
		||||
			m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning);
 | 
			
		||||
			m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
 | 
			
		||||
@@ -50,6 +51,7 @@ namespace KeePassLib.Resources
 | 
			
		||||
			m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint);
 | 
			
		||||
			m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding);
 | 
			
		||||
			m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
 | 
			
		||||
			m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
 | 
			
		||||
			m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
 | 
			
		||||
			m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
 | 
			
		||||
			m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
 | 
			
		||||
@@ -58,13 +60,14 @@ namespace KeePassLib.Resources
 | 
			
		||||
			m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
 | 
			
		||||
			m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
 | 
			
		||||
			m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
 | 
			
		||||
			m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
 | 
			
		||||
			m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
 | 
			
		||||
			m_strUserAgent = TryGetEx(dictNew, "UserAgent", m_strUserAgent);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static readonly string[] m_vKeyNames = {
 | 
			
		||||
			"CryptoStreamFailed",
 | 
			
		||||
			"EncAlgorithmAes",
 | 
			
		||||
			"EncDataTooLarge",
 | 
			
		||||
			"ErrorInClipboard",
 | 
			
		||||
			"Expect100Continue",
 | 
			
		||||
			"FatalError",
 | 
			
		||||
@@ -73,6 +76,7 @@ namespace KeePassLib.Resources
 | 
			
		||||
			"FileHeaderEndEarly",
 | 
			
		||||
			"FileLoadFailed",
 | 
			
		||||
			"FileLockedWrite",
 | 
			
		||||
			"FileNewVerOrPlgReq",
 | 
			
		||||
			"FileNewVerReq",
 | 
			
		||||
			"FileSaveCorruptionWarning",
 | 
			
		||||
			"FileSaveFailed",
 | 
			
		||||
@@ -87,6 +91,7 @@ namespace KeePassLib.Resources
 | 
			
		||||
			"InvalidCompositeKeyHint",
 | 
			
		||||
			"InvalidDataWhileDecoding",
 | 
			
		||||
			"KeePass1xHint",
 | 
			
		||||
			"KeyBits",
 | 
			
		||||
			"KeyFileDbSel",
 | 
			
		||||
			"MasterSeedLengthInvalid",
 | 
			
		||||
			"OldFormat",
 | 
			
		||||
@@ -95,6 +100,7 @@ namespace KeePassLib.Resources
 | 
			
		||||
			"Timeout",
 | 
			
		||||
			"TryAgainSecs",
 | 
			
		||||
			"UnknownHeaderId",
 | 
			
		||||
			"UnknownKdf",
 | 
			
		||||
			"UserAccountKeyError",
 | 
			
		||||
			"UserAgent"
 | 
			
		||||
		};
 | 
			
		||||
@@ -115,15 +121,15 @@ namespace KeePassLib.Resources
 | 
			
		||||
			get { return m_strCryptoStreamFailed; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strEncAlgorithmAes =
 | 
			
		||||
			@"AES/Rijndael (256-Bit Key)";
 | 
			
		||||
		private static string m_strEncDataTooLarge =
 | 
			
		||||
			@"The data is too large to be encrypted/decrypted securely using {PARAM}.";
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Look up a localized string similar to
 | 
			
		||||
		/// 'AES/Rijndael (256-Bit Key)'.
 | 
			
		||||
		/// 'The data is too large to be encrypted/decrypted securely using {PARAM}.'.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public static string EncAlgorithmAes
 | 
			
		||||
		public static string EncDataTooLarge
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_strEncAlgorithmAes; }
 | 
			
		||||
			get { return m_strEncDataTooLarge; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strErrorInClipboard =
 | 
			
		||||
@@ -214,6 +220,17 @@ namespace KeePassLib.Resources
 | 
			
		||||
			get { return m_strFileLockedWrite; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strFileNewVerOrPlgReq =
 | 
			
		||||
			@"A newer KeePass version or a plugin is required to open this file.";
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Look up a localized string similar to
 | 
			
		||||
		/// 'A newer KeePass version or a plugin is required to open this file.'.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public static string FileNewVerOrPlgReq
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_strFileNewVerOrPlgReq; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strFileNewVerReq =
 | 
			
		||||
			@"A newer KeePass version is required to open this file.";
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -368,6 +385,17 @@ namespace KeePassLib.Resources
 | 
			
		||||
			get { return m_strKeePass1xHint; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strKeyBits =
 | 
			
		||||
			@"{PARAM}-bit key";
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Look up a localized string similar to
 | 
			
		||||
		/// '{PARAM}-bit key'.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public static string KeyBits
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_strKeyBits; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strKeyFileDbSel =
 | 
			
		||||
			@"Database files cannot be used as key files.";
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -456,6 +484,17 @@ namespace KeePassLib.Resources
 | 
			
		||||
			get { return m_strUnknownHeaderId; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strUnknownKdf =
 | 
			
		||||
			@"Unknown key derivation function!";
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Look up a localized string similar to
 | 
			
		||||
		/// 'Unknown key derivation function!'.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public static string UnknownKdf
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_strUnknownKdf; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string m_strUserAccountKeyError =
 | 
			
		||||
			@"The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored.";
 | 
			
		||||
		/// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ namespace KeePassLib.Security
 | 
			
		||||
		{
 | 
			
		||||
			None = 0,
 | 
			
		||||
			ProtectedMemory,
 | 
			
		||||
			Salsa20,
 | 
			
		||||
			ChaCha20,
 | 
			
		||||
			ExtCrypt
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -166,7 +166,7 @@ namespace KeePassLib.Security
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public ProtectedBinary()
 | 
			
		||||
		{
 | 
			
		||||
			Init(false, new byte[0]);
 | 
			
		||||
			Init(false, MemUtil.EmptyByteArray);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -263,11 +263,13 @@ namespace KeePassLib.Security
 | 
			
		||||
				if(pbUpd != null) pbKey32 = pbUpd;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Salsa20Cipher s = new Salsa20Cipher(pbKey32,
 | 
			
		||||
				BitConverter.GetBytes(m_lID));
 | 
			
		||||
			s.Encrypt(m_pbData, m_pbData.Length, true);
 | 
			
		||||
			s.Dispose();
 | 
			
		||||
			m_mp = PbMemProt.Salsa20;
 | 
			
		||||
			byte[] pbIV = new byte[12];
 | 
			
		||||
			MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
 | 
			
		||||
			using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey32, pbIV, true))
 | 
			
		||||
			{
 | 
			
		||||
				c.Encrypt(m_pbData, 0, m_pbData.Length);
 | 
			
		||||
			}
 | 
			
		||||
			m_mp = PbMemProt.ChaCha20;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void Decrypt()
 | 
			
		||||
@@ -276,12 +278,14 @@ namespace KeePassLib.Security
 | 
			
		||||
 | 
			
		||||
			if(m_mp == PbMemProt.ProtectedMemory)
 | 
			
		||||
				ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess);
 | 
			
		||||
			else if(m_mp == PbMemProt.Salsa20)
 | 
			
		||||
			else if(m_mp == PbMemProt.ChaCha20)
 | 
			
		||||
			{
 | 
			
		||||
				Salsa20Cipher s = new Salsa20Cipher(g_pbKey32,
 | 
			
		||||
					BitConverter.GetBytes(m_lID));
 | 
			
		||||
				s.Encrypt(m_pbData, m_pbData.Length, true);
 | 
			
		||||
				s.Dispose();
 | 
			
		||||
				byte[] pbIV = new byte[12];
 | 
			
		||||
				MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
 | 
			
		||||
				using(ChaCha20Cipher c = new ChaCha20Cipher(g_pbKey32, pbIV, true))
 | 
			
		||||
				{
 | 
			
		||||
					c.Decrypt(m_pbData, 0, m_pbData.Length);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if(m_mp == PbMemProt.ExtCrypt)
 | 
			
		||||
				m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID);
 | 
			
		||||
@@ -300,7 +304,7 @@ namespace KeePassLib.Security
 | 
			
		||||
		/// protected data and can therefore be cleared safely.</returns>
 | 
			
		||||
		public byte[] ReadData()
 | 
			
		||||
		{
 | 
			
		||||
			if(m_uDataLen == 0) return new byte[0];
 | 
			
		||||
			if(m_uDataLen == 0) return MemUtil.EmptyByteArray;
 | 
			
		||||
 | 
			
		||||
			byte[] pbReturn = new byte[m_uDataLen];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
				try
 | 
			
		||||
				{
 | 
			
		||||
					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,
 | 
			
		||||
#if KeePassUAP
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,7 @@ using System.Diagnostics;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
#if !KeePassUAP
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Native;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
@@ -37,7 +34,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
{
 | 
			
		||||
	public sealed class HashedBlockStream : Stream
 | 
			
		||||
	{
 | 
			
		||||
		private const int m_nDefaultBufferSize = 1024 * 1024; // 1 MB
 | 
			
		||||
		private const int NbDefaultBufferSize = 1024 * 1024; // 1 MB
 | 
			
		||||
 | 
			
		||||
		private Stream m_sBaseStream;
 | 
			
		||||
		private bool m_bWriting;
 | 
			
		||||
@@ -50,7 +47,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		private byte[] m_pbBuffer;
 | 
			
		||||
		private int m_nBufferPos = 0;
 | 
			
		||||
 | 
			
		||||
		private uint m_uBufferIndex = 0;
 | 
			
		||||
		private uint m_uBlockIndex = 0;
 | 
			
		||||
 | 
			
		||||
		public override bool CanRead
 | 
			
		||||
		{
 | 
			
		||||
@@ -69,13 +66,13 @@ namespace KeePassLib.Serialization
 | 
			
		||||
 | 
			
		||||
		public override long Length
 | 
			
		||||
		{
 | 
			
		||||
			get { throw new NotSupportedException(); }
 | 
			
		||||
			get { Debug.Assert(false); throw new NotSupportedException(); }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public override long Position
 | 
			
		||||
		{
 | 
			
		||||
			get { throw new NotSupportedException(); }
 | 
			
		||||
			set { throw new NotSupportedException(); }
 | 
			
		||||
			get { Debug.Assert(false); throw new NotSupportedException(); }
 | 
			
		||||
			set { Debug.Assert(false); throw new NotSupportedException(); }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public HashedBlockStream(Stream sBaseStream, bool bWriting)
 | 
			
		||||
@@ -100,7 +97,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
 | 
			
		||||
			if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize");
 | 
			
		||||
 | 
			
		||||
			if(nBufferSize == 0) nBufferSize = m_nDefaultBufferSize;
 | 
			
		||||
			if(nBufferSize == 0) nBufferSize = NbDefaultBufferSize;
 | 
			
		||||
 | 
			
		||||
			m_sBaseStream = sBaseStream;
 | 
			
		||||
			m_bWriting = bWriting;
 | 
			
		||||
@@ -114,7 +111,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
 | 
			
		||||
				m_brInput = new BinaryReader(sBaseStream, utf8);
 | 
			
		||||
 | 
			
		||||
				m_pbBuffer = new byte[0];
 | 
			
		||||
				m_pbBuffer = MemUtil.EmptyByteArray;
 | 
			
		||||
			}
 | 
			
		||||
			else // Writing mode
 | 
			
		||||
			{
 | 
			
		||||
@@ -142,7 +139,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
#endif
 | 
			
		||||
			if(m_sBaseStream != null)
 | 
			
		||||
			{
 | 
			
		||||
				if(m_bWriting == false) // Reading mode
 | 
			
		||||
				if(!m_bWriting) // Reading mode
 | 
			
		||||
				{
 | 
			
		||||
					m_brInput.Close();
 | 
			
		||||
					m_brInput = null;
 | 
			
		||||
@@ -209,9 +206,9 @@ namespace KeePassLib.Serialization
 | 
			
		||||
 | 
			
		||||
			m_nBufferPos = 0;
 | 
			
		||||
 | 
			
		||||
			if(m_brInput.ReadUInt32() != m_uBufferIndex)
 | 
			
		||||
			if(m_brInput.ReadUInt32() != m_uBlockIndex)
 | 
			
		||||
				throw new InvalidDataException();
 | 
			
		||||
			++m_uBufferIndex;
 | 
			
		||||
			++m_uBlockIndex;
 | 
			
		||||
 | 
			
		||||
			byte[] pbStoredHash = m_brInput.ReadBytes(32);
 | 
			
		||||
			if((pbStoredHash == null) || (pbStoredHash.Length != 32))
 | 
			
		||||
@@ -236,7 +233,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				m_bEos = true;
 | 
			
		||||
				m_pbBuffer = new byte[0];
 | 
			
		||||
				m_pbBuffer = MemUtil.EmptyByteArray;
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -246,17 +243,13 @@ namespace KeePassLib.Serialization
 | 
			
		||||
 | 
			
		||||
			if(m_bVerify)
 | 
			
		||||
			{
 | 
			
		||||
				SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
				byte[] pbComputedHash = sha256.ComputeHash(m_pbBuffer);
 | 
			
		||||
				byte[] pbComputedHash = CryptoUtil.HashSha256(m_pbBuffer);
 | 
			
		||||
				if((pbComputedHash == null) || (pbComputedHash.Length != 32))
 | 
			
		||||
					throw new InvalidOperationException();
 | 
			
		||||
 | 
			
		||||
				for(int iHashPos = 0; iHashPos < 32; ++iHashPos)
 | 
			
		||||
				{
 | 
			
		||||
					if(pbStoredHash[iHashPos] != pbComputedHash[iHashPos])
 | 
			
		||||
				if(!MemUtil.ArraysEqual(pbStoredHash, pbComputedHash))
 | 
			
		||||
					throw new InvalidDataException();
 | 
			
		||||
			}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
@@ -283,26 +276,24 @@ namespace KeePassLib.Serialization
 | 
			
		||||
 | 
			
		||||
		private void WriteHashedBlock()
 | 
			
		||||
		{
 | 
			
		||||
			m_bwOutput.Write(m_uBufferIndex);
 | 
			
		||||
			++m_uBufferIndex;
 | 
			
		||||
			m_bwOutput.Write(m_uBlockIndex);
 | 
			
		||||
			++m_uBlockIndex;
 | 
			
		||||
 | 
			
		||||
			if(m_nBufferPos > 0)
 | 
			
		||||
			{
 | 
			
		||||
				SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
				byte[] pbHash = CryptoUtil.HashSha256(m_pbBuffer, 0, m_nBufferPos);
 | 
			
		||||
 | 
			
		||||
#if !KeePassLibSD
 | 
			
		||||
				byte[] pbHash = sha256.ComputeHash(m_pbBuffer, 0, m_nBufferPos);
 | 
			
		||||
#else
 | 
			
		||||
				byte[] pbHash;
 | 
			
		||||
				if(m_nBufferPos == m_pbBuffer.Length)
 | 
			
		||||
					pbHash = sha256.ComputeHash(m_pbBuffer);
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					byte[] pbData = new byte[m_nBufferPos];
 | 
			
		||||
					Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
 | 
			
		||||
					pbHash = sha256.ComputeHash(pbData);
 | 
			
		||||
				}
 | 
			
		||||
#endif
 | 
			
		||||
				// For KeePassLibSD:
 | 
			
		||||
				// SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
				// byte[] pbHash;
 | 
			
		||||
				// if(m_nBufferPos == m_pbBuffer.Length)
 | 
			
		||||
				//	pbHash = sha256.ComputeHash(m_pbBuffer);
 | 
			
		||||
				// else
 | 
			
		||||
				// {
 | 
			
		||||
				//	byte[] pbData = new byte[m_nBufferPos];
 | 
			
		||||
				//	Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
 | 
			
		||||
				//	pbHash = sha256.ComputeHash(pbData);
 | 
			
		||||
				// }
 | 
			
		||||
 | 
			
		||||
				m_bwOutput.Write(pbHash);
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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,
 | 
			
		||||
			Group,
 | 
			
		||||
			GroupTimes,
 | 
			
		||||
			GroupCustomData,
 | 
			
		||||
			GroupCustomDataItem,
 | 
			
		||||
			Entry,
 | 
			
		||||
			EntryTimes,
 | 
			
		||||
			EntryString,
 | 
			
		||||
			EntryBinary,
 | 
			
		||||
			EntryAutoType,
 | 
			
		||||
			EntryAutoTypeItem,
 | 
			
		||||
			EntryHistory
 | 
			
		||||
			EntryHistory,
 | 
			
		||||
			EntryCustomData,
 | 
			
		||||
			EntryCustomDataItem
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool m_bReadNextNode = true;
 | 
			
		||||
@@ -84,10 +88,14 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		private byte[] m_pbCustomIconData = null;
 | 
			
		||||
		private string m_strCustomDataKey = null;
 | 
			
		||||
		private string m_strCustomDataValue = null;
 | 
			
		||||
		private string m_strGroupCustomDataKey = null;
 | 
			
		||||
		private string m_strGroupCustomDataValue = null;
 | 
			
		||||
		private string m_strEntryCustomDataKey = null;
 | 
			
		||||
		private string m_strEntryCustomDataValue = null;
 | 
			
		||||
 | 
			
		||||
		private void ReadXmlStreamed(Stream readerStream, Stream sParentStream)
 | 
			
		||||
		private void ReadXmlStreamed(Stream sXml, Stream sParent)
 | 
			
		||||
		{
 | 
			
		||||
			ReadDocumentStreamed(CreateXmlReader(readerStream), sParentStream);
 | 
			
		||||
			ReadDocumentStreamed(CreateXmlReader(sXml), sParent);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal static XmlReaderSettings CreateStdXmlReaderSettings()
 | 
			
		||||
@@ -215,15 +223,25 @@ namespace KeePassLib.Serialization
 | 
			
		||||
						ReadString(xr); // Ignore
 | 
			
		||||
					else if(xr.Name == ElemHeaderHash)
 | 
			
		||||
					{
 | 
			
		||||
						// The header hash is typically only stored in
 | 
			
		||||
						// KDBX <= 3.1 files, not in KDBX >= 4 files
 | 
			
		||||
						// (here, the header is verified via a HMAC),
 | 
			
		||||
						// but we also support it for KDBX >= 4 files
 | 
			
		||||
						// (i.e. if it's present, we check it)
 | 
			
		||||
 | 
			
		||||
						string strHash = ReadString(xr);
 | 
			
		||||
						if(!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
 | 
			
		||||
							!m_bRepairMode)
 | 
			
		||||
						{
 | 
			
		||||
							Debug.Assert(m_uFileVersion <= FileVersion32_3);
 | 
			
		||||
 | 
			
		||||
							byte[] pbHash = Convert.FromBase64String(strHash);
 | 
			
		||||
							if(!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))
 | 
			
		||||
								throw new IOException(KLRes.FileCorrupted);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					else if(xr.Name == ElemSettingsChanged)
 | 
			
		||||
						m_pwDatabase.SettingsChanged = ReadTime(xr);
 | 
			
		||||
					else if(xr.Name == ElemDbName)
 | 
			
		||||
						m_pwDatabase.Name = ReadString(xr);
 | 
			
		||||
					else if(xr.Name == ElemDbNameChanged)
 | 
			
		||||
@@ -383,6 +401,8 @@ namespace KeePassLib.Serialization
 | 
			
		||||
						m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr));
 | 
			
		||||
					else if(xr.Name == ElemLastTopVisibleEntry)
 | 
			
		||||
						m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr);
 | 
			
		||||
					else if(xr.Name == ElemCustomData)
 | 
			
		||||
						return SwitchContext(ctx, KdbContext.GroupCustomData, xr);
 | 
			
		||||
					else if(xr.Name == ElemGroup)
 | 
			
		||||
					{
 | 
			
		||||
						m_ctxGroup = new PwGroup(false, false);
 | 
			
		||||
@@ -403,6 +423,20 @@ namespace KeePassLib.Serialization
 | 
			
		||||
					else ReadUnknown(xr);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbContext.GroupCustomData:
 | 
			
		||||
					if(xr.Name == ElemStringDictExItem)
 | 
			
		||||
						return SwitchContext(ctx, KdbContext.GroupCustomDataItem, xr);
 | 
			
		||||
					else ReadUnknown(xr);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbContext.GroupCustomDataItem:
 | 
			
		||||
					if(xr.Name == ElemKey)
 | 
			
		||||
						m_strGroupCustomDataKey = ReadString(xr);
 | 
			
		||||
					else if(xr.Name == ElemValue)
 | 
			
		||||
						m_strGroupCustomDataValue = ReadString(xr);
 | 
			
		||||
					else ReadUnknown(xr);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbContext.Entry:
 | 
			
		||||
					if(xr.Name == ElemUuid)
 | 
			
		||||
						m_ctxEntry.Uuid = ReadUuid(xr);
 | 
			
		||||
@@ -434,6 +468,8 @@ namespace KeePassLib.Serialization
 | 
			
		||||
						return SwitchContext(ctx, KdbContext.EntryBinary, xr);
 | 
			
		||||
					else if(xr.Name == ElemAutoType)
 | 
			
		||||
						return SwitchContext(ctx, KdbContext.EntryAutoType, xr);
 | 
			
		||||
					else if(xr.Name == ElemCustomData)
 | 
			
		||||
						return SwitchContext(ctx, KdbContext.EntryCustomData, xr);
 | 
			
		||||
					else if(xr.Name == ElemHistory)
 | 
			
		||||
					{
 | 
			
		||||
						Debug.Assert(m_bEntryInHistory == false);
 | 
			
		||||
@@ -508,6 +544,20 @@ namespace KeePassLib.Serialization
 | 
			
		||||
					else ReadUnknown(xr);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbContext.EntryCustomData:
 | 
			
		||||
					if(xr.Name == ElemStringDictExItem)
 | 
			
		||||
						return SwitchContext(ctx, KdbContext.EntryCustomDataItem, xr);
 | 
			
		||||
					else ReadUnknown(xr);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbContext.EntryCustomDataItem:
 | 
			
		||||
					if(xr.Name == ElemKey)
 | 
			
		||||
						m_strEntryCustomDataKey = ReadString(xr);
 | 
			
		||||
					else if(xr.Name == ElemValue)
 | 
			
		||||
						m_strEntryCustomDataValue = ReadString(xr);
 | 
			
		||||
					else ReadUnknown(xr);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbContext.EntryHistory:
 | 
			
		||||
					if(xr.Name == ElemEntry)
 | 
			
		||||
					{
 | 
			
		||||
@@ -609,6 +659,19 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			}
 | 
			
		||||
			else if((ctx == KdbContext.GroupTimes) && (xr.Name == ElemTimes))
 | 
			
		||||
				return KdbContext.Group;
 | 
			
		||||
			else if((ctx == KdbContext.GroupCustomData) && (xr.Name == ElemCustomData))
 | 
			
		||||
				return KdbContext.Group;
 | 
			
		||||
			else if((ctx == KdbContext.GroupCustomDataItem) && (xr.Name == ElemStringDictExItem))
 | 
			
		||||
			{
 | 
			
		||||
				if((m_strGroupCustomDataKey != null) && (m_strGroupCustomDataValue != null))
 | 
			
		||||
					m_ctxGroup.CustomData.Set(m_strGroupCustomDataKey, m_strGroupCustomDataValue);
 | 
			
		||||
				else { Debug.Assert(false); }
 | 
			
		||||
 | 
			
		||||
				m_strGroupCustomDataKey = null;
 | 
			
		||||
				m_strGroupCustomDataValue = null;
 | 
			
		||||
 | 
			
		||||
				return KdbContext.GroupCustomData;
 | 
			
		||||
			}
 | 
			
		||||
			else if((ctx == KdbContext.Entry) && (xr.Name == ElemEntry))
 | 
			
		||||
			{
 | 
			
		||||
				// Create new UUID if absent
 | 
			
		||||
@@ -659,6 +722,19 @@ namespace KeePassLib.Serialization
 | 
			
		||||
				m_ctxATSeq = null;
 | 
			
		||||
				return KdbContext.EntryAutoType;
 | 
			
		||||
			}
 | 
			
		||||
			else if((ctx == KdbContext.EntryCustomData) && (xr.Name == ElemCustomData))
 | 
			
		||||
				return KdbContext.Entry;
 | 
			
		||||
			else if((ctx == KdbContext.EntryCustomDataItem) && (xr.Name == ElemStringDictExItem))
 | 
			
		||||
			{
 | 
			
		||||
				if((m_strEntryCustomDataKey != null) && (m_strEntryCustomDataValue != null))
 | 
			
		||||
					m_ctxEntry.CustomData.Set(m_strEntryCustomDataKey, m_strEntryCustomDataValue);
 | 
			
		||||
				else { Debug.Assert(false); }
 | 
			
		||||
 | 
			
		||||
				m_strEntryCustomDataKey = null;
 | 
			
		||||
				m_strEntryCustomDataValue = null;
 | 
			
		||||
 | 
			
		||||
				return KdbContext.EntryCustomData;
 | 
			
		||||
			}
 | 
			
		||||
			else if((ctx == KdbContext.EntryHistory) && (xr.Name == ElemHistory))
 | 
			
		||||
			{
 | 
			
		||||
				m_bEntryInHistory = false;
 | 
			
		||||
@@ -883,7 +959,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
						byte[] pbEncrypted;
 | 
			
		||||
						if(strEncrypted.Length > 0)
 | 
			
		||||
							pbEncrypted = Convert.FromBase64String(strEncrypted);
 | 
			
		||||
						else pbEncrypted = new byte[0];
 | 
			
		||||
						else pbEncrypted = MemUtil.EmptyByteArray;
 | 
			
		||||
 | 
			
		||||
						byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,10 @@ using System.IO.Compression;
 | 
			
		||||
using KeePassLibSD;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Collections;
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Cryptography.Cipher;
 | 
			
		||||
using KeePassLib.Cryptography.KeyDerivation;
 | 
			
		||||
using KeePassLib.Interfaces;
 | 
			
		||||
using KeePassLib.Keys;
 | 
			
		||||
using KeePassLib.Resources;
 | 
			
		||||
@@ -50,74 +52,108 @@ namespace KeePassLib.Serialization
 | 
			
		||||
	public sealed partial class KdbxFile
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Load a KDB file from a file.
 | 
			
		||||
		/// Load a KDBX file.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="strFilePath">File to load.</param>
 | 
			
		||||
		/// <param name="kdbFormat">Format specifier.</param>
 | 
			
		||||
		/// <param name="fmt">Format.</param>
 | 
			
		||||
		/// <param name="slLogger">Status logger (optional).</param>
 | 
			
		||||
		public void Load(string strFilePath, KdbxFormat kdbFormat, IStatusLogger slLogger)
 | 
			
		||||
		public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
 | 
			
		||||
		{
 | 
			
		||||
			IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
 | 
			
		||||
			Load(IOConnection.OpenRead(ioc), kdbFormat, slLogger);
 | 
			
		||||
			Load(IOConnection.OpenRead(ioc), fmt, slLogger);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Load a KDB file from a stream.
 | 
			
		||||
		/// Load a KDBX file from a stream.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <param name="sSource">Stream to read the data from. Must contain
 | 
			
		||||
		/// a KDBX stream.</param>
 | 
			
		||||
		/// <param name="kdbFormat">Format specifier.</param>
 | 
			
		||||
		/// <param name="fmt">Format.</param>
 | 
			
		||||
		/// <param name="slLogger">Status logger (optional).</param>
 | 
			
		||||
		public void Load(Stream sSource, KdbxFormat kdbFormat, IStatusLogger slLogger)
 | 
			
		||||
		public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(sSource != null);
 | 
			
		||||
			if(sSource == null) throw new ArgumentNullException("sSource");
 | 
			
		||||
 | 
			
		||||
			m_format = kdbFormat;
 | 
			
		||||
			m_format = fmt;
 | 
			
		||||
			m_slLogger = slLogger;
 | 
			
		||||
 | 
			
		||||
			HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null);
 | 
			
		||||
 | 
			
		||||
			UTF8Encoding encNoBom = StrUtil.Utf8;
 | 
			
		||||
			byte[] pbCipherKey = null;
 | 
			
		||||
			byte[] pbHmacKey64 = null;
 | 
			
		||||
 | 
			
		||||
			List<Stream> lStreams = new List<Stream>();
 | 
			
		||||
			lStreams.Add(sSource);
 | 
			
		||||
 | 
			
		||||
			HashingStreamEx sHashing = new HashingStreamEx(sSource, false, null);
 | 
			
		||||
			lStreams.Add(sHashing);
 | 
			
		||||
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				BinaryReaderEx br = null;
 | 
			
		||||
				BinaryReaderEx brDecrypted = null;
 | 
			
		||||
				Stream readerStream = null;
 | 
			
		||||
 | 
			
		||||
				if(kdbFormat == KdbxFormat.Default)
 | 
			
		||||
				Stream sXml;
 | 
			
		||||
				if(fmt == KdbxFormat.Default)
 | 
			
		||||
				{
 | 
			
		||||
					br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
 | 
			
		||||
					ReadHeader(br);
 | 
			
		||||
					BinaryReaderEx br = new BinaryReaderEx(sHashing,
 | 
			
		||||
						encNoBom, KLRes.FileCorrupted);
 | 
			
		||||
					byte[] pbHeader = LoadHeader(br);
 | 
			
		||||
 | 
			
		||||
					Stream sDecrypted = AttachStreamDecryptor(hashedStream);
 | 
			
		||||
					if((sDecrypted == null) || (sDecrypted == hashedStream))
 | 
			
		||||
					int cbEncKey, cbEncIV;
 | 
			
		||||
					ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
 | 
			
		||||
 | 
			
		||||
					ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
 | 
			
		||||
 | 
			
		||||
					Stream sPlain;
 | 
			
		||||
					if(m_uFileVersion <= FileVersion32_3)
 | 
			
		||||
					{
 | 
			
		||||
						Stream sDecrypted = EncryptStream(sHashing, iCipher,
 | 
			
		||||
							pbCipherKey, cbEncIV, false);
 | 
			
		||||
						if((sDecrypted == null) || (sDecrypted == sHashing))
 | 
			
		||||
							throw new SecurityException(KLRes.CryptoStreamFailed);
 | 
			
		||||
						lStreams.Add(sDecrypted);
 | 
			
		||||
 | 
			
		||||
					brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
 | 
			
		||||
						BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
 | 
			
		||||
							encNoBom, KLRes.FileCorrupted);
 | 
			
		||||
						byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
 | 
			
		||||
 | 
			
		||||
						if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
 | 
			
		||||
							throw new InvalidDataException();
 | 
			
		||||
 | 
			
		||||
					for(int iStart = 0; iStart < 32; ++iStart)
 | 
			
		||||
					{
 | 
			
		||||
						if(pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart])
 | 
			
		||||
						if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
 | 
			
		||||
							throw new InvalidCompositeKeyException();
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					Stream sHashed = new HashedBlockStream(sDecrypted, false, 0,
 | 
			
		||||
						!m_bRepairMode);
 | 
			
		||||
						sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
 | 
			
		||||
					}
 | 
			
		||||
					else // KDBX >= 4
 | 
			
		||||
					{
 | 
			
		||||
						byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
 | 
			
		||||
						byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
 | 
			
		||||
						if((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
 | 
			
		||||
							throw new InvalidDataException();
 | 
			
		||||
						if(!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac))
 | 
			
		||||
							throw new InvalidCompositeKeyException();
 | 
			
		||||
 | 
			
		||||
						HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
 | 
			
		||||
							false, !m_bRepairMode, pbHmacKey64);
 | 
			
		||||
						lStreams.Add(sBlocks);
 | 
			
		||||
 | 
			
		||||
						sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
 | 
			
		||||
							cbEncIV, false);
 | 
			
		||||
						if((sPlain == null) || (sPlain == sBlocks))
 | 
			
		||||
							throw new SecurityException(KLRes.CryptoStreamFailed);
 | 
			
		||||
					}
 | 
			
		||||
					lStreams.Add(sPlain);
 | 
			
		||||
 | 
			
		||||
					if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
 | 
			
		||||
						readerStream = new GZipStream(sHashed, CompressionMode.Decompress);
 | 
			
		||||
					else readerStream = sHashed;
 | 
			
		||||
					{
 | 
			
		||||
						sXml = new GZipStream(sPlain, CompressionMode.Decompress);
 | 
			
		||||
						lStreams.Add(sXml);
 | 
			
		||||
					}
 | 
			
		||||
				else if(kdbFormat == KdbxFormat.PlainXml)
 | 
			
		||||
					readerStream = hashedStream;
 | 
			
		||||
				else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
 | 
			
		||||
					else sXml = sPlain;
 | 
			
		||||
				}
 | 
			
		||||
				else if(fmt == KdbxFormat.PlainXml)
 | 
			
		||||
					sXml = sHashing;
 | 
			
		||||
				else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
 | 
			
		||||
 | 
			
		||||
				if(kdbFormat != KdbxFormat.PlainXml) // Is an encrypted format
 | 
			
		||||
				if(fmt == KdbxFormat.Default)
 | 
			
		||||
				{
 | 
			
		||||
					if(m_pbProtectedStreamKey == null)
 | 
			
		||||
					{
 | 
			
		||||
@@ -137,7 +173,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
				// {
 | 
			
		||||
				//	while(true)
 | 
			
		||||
				//	{
 | 
			
		||||
				//		int b = readerStream.ReadByte();
 | 
			
		||||
				//		int b = sXml.ReadByte();
 | 
			
		||||
				//		if(b == -1) break;
 | 
			
		||||
				//		fsOut.WriteByte((byte)b);
 | 
			
		||||
				//	}
 | 
			
		||||
@@ -146,26 +182,29 @@ namespace KeePassLib.Serialization
 | 
			
		||||
				// fsOut.Close();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
				ReadXmlStreamed(readerStream, hashedStream);
 | 
			
		||||
				// ReadXmlDom(readerStream);
 | 
			
		||||
 | 
			
		||||
				readerStream.Close();
 | 
			
		||||
				// GC.KeepAlive(br);
 | 
			
		||||
				// GC.KeepAlive(brDecrypted);
 | 
			
		||||
				ReadXmlStreamed(sXml, sHashing);
 | 
			
		||||
				// ReadXmlDom(sXml);
 | 
			
		||||
			}
 | 
			
		||||
			catch(CryptographicException) // Thrown on invalid padding
 | 
			
		||||
			{
 | 
			
		||||
				throw new CryptographicException(KLRes.FileCorrupted);
 | 
			
		||||
			}
 | 
			
		||||
			finally { CommonCleanUpRead(sSource, hashedStream); }
 | 
			
		||||
			finally
 | 
			
		||||
			{
 | 
			
		||||
				if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
 | 
			
		||||
				if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
 | 
			
		||||
 | 
			
		||||
				CommonCleanUpRead(lStreams, sHashing);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
 | 
			
		||||
		private void CommonCleanUpRead(List<Stream> lStreams, HashingStreamEx sHashing)
 | 
			
		||||
		{
 | 
			
		||||
			hashedStream.Close();
 | 
			
		||||
			m_pbHashOfFileOnDisk = hashedStream.Hash;
 | 
			
		||||
			CloseStreams(lStreams);
 | 
			
		||||
 | 
			
		||||
			sSource.Close();
 | 
			
		||||
			Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
 | 
			
		||||
			m_pbHashOfFileOnDisk = sHashing.Hash;
 | 
			
		||||
			Debug.Assert(m_pbHashOfFileOnDisk != null);
 | 
			
		||||
 | 
			
		||||
			// Reset memory protection settings (to always use reasonable
 | 
			
		||||
			// defaults)
 | 
			
		||||
@@ -187,7 +226,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			m_pbHashOfHeader = null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void ReadHeader(BinaryReaderEx br)
 | 
			
		||||
		private byte[] LoadHeader(BinaryReaderEx br)
 | 
			
		||||
		{
 | 
			
		||||
			MemoryStream msHeader = new MemoryStream();
 | 
			
		||||
			Debug.Assert(br.CopyDataTo == null);
 | 
			
		||||
@@ -212,18 +251,19 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
 | 
			
		||||
				throw new FormatException(KLRes.FileVersionUnsupported +
 | 
			
		||||
					MessageService.NewParagraph + KLRes.FileNewVerReq);
 | 
			
		||||
			m_uFileVersion = uVersion;
 | 
			
		||||
 | 
			
		||||
			while(true)
 | 
			
		||||
			{
 | 
			
		||||
				if(ReadHeaderField(br) == false)
 | 
			
		||||
					break;
 | 
			
		||||
				if(!ReadHeaderField(br)) break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			br.CopyDataTo = null;
 | 
			
		||||
			byte[] pbHeader = msHeader.ToArray();
 | 
			
		||||
			msHeader.Close();
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			m_pbHashOfHeader = sha256.ComputeHash(pbHeader);
 | 
			
		||||
 | 
			
		||||
			m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
 | 
			
		||||
			return pbHeader;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool ReadHeaderField(BinaryReaderEx brSource)
 | 
			
		||||
@@ -232,15 +272,21 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			if(brSource == null) throw new ArgumentNullException("brSource");
 | 
			
		||||
 | 
			
		||||
			byte btFieldID = brSource.ReadByte();
 | 
			
		||||
			ushort uSize = MemUtil.BytesToUInt16(brSource.ReadBytes(2));
 | 
			
		||||
 | 
			
		||||
			byte[] pbData = null;
 | 
			
		||||
			if(uSize > 0)
 | 
			
		||||
			int cbSize;
 | 
			
		||||
			Debug.Assert(m_uFileVersion > 0);
 | 
			
		||||
			if(m_uFileVersion <= FileVersion32_3)
 | 
			
		||||
				cbSize = (int)MemUtil.BytesToUInt16(brSource.ReadBytes(2));
 | 
			
		||||
			else cbSize = MemUtil.BytesToInt32(brSource.ReadBytes(4));
 | 
			
		||||
			if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
 | 
			
		||||
 | 
			
		||||
			byte[] pbData = MemUtil.EmptyByteArray;
 | 
			
		||||
			if(cbSize > 0)
 | 
			
		||||
			{
 | 
			
		||||
				string strPrevExcpText = brSource.ReadExceptionText;
 | 
			
		||||
				brSource.ReadExceptionText = KLRes.FileHeaderEndEarly;
 | 
			
		||||
 | 
			
		||||
				pbData = brSource.ReadBytes(uSize);
 | 
			
		||||
				pbData = brSource.ReadBytes(cbSize);
 | 
			
		||||
 | 
			
		||||
				brSource.ReadExceptionText = strPrevExcpText;
 | 
			
		||||
			}
 | 
			
		||||
@@ -266,13 +312,27 @@ namespace KeePassLib.Serialization
 | 
			
		||||
					CryptoRandom.Instance.AddEntropy(pbData);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				// Obsolete; for backward compatibility only
 | 
			
		||||
				case KdbxHeaderFieldID.TransformSeed:
 | 
			
		||||
					m_pbTransformSeed = pbData;
 | 
			
		||||
					AesKdf kdfS = new AesKdf();
 | 
			
		||||
					if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
 | 
			
		||||
						m_pwDatabase.KdfParameters = kdfS.GetDefaultParameters();
 | 
			
		||||
 | 
			
		||||
					// m_pbTransformSeed = pbData;
 | 
			
		||||
					m_pwDatabase.KdfParameters.SetByteArray(AesKdf.ParamSeed, pbData);
 | 
			
		||||
 | 
			
		||||
					CryptoRandom.Instance.AddEntropy(pbData);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				// Obsolete; for backward compatibility only
 | 
			
		||||
				case KdbxHeaderFieldID.TransformRounds:
 | 
			
		||||
					m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
 | 
			
		||||
					AesKdf kdfR = new AesKdf();
 | 
			
		||||
					if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
 | 
			
		||||
						m_pwDatabase.KdfParameters = kdfR.GetDefaultParameters();
 | 
			
		||||
 | 
			
		||||
					// m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
 | 
			
		||||
					m_pwDatabase.KdfParameters.SetUInt64(AesKdf.ParamRounds,
 | 
			
		||||
						MemUtil.BytesToUInt64(pbData));
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbxHeaderFieldID.EncryptionIV:
 | 
			
		||||
@@ -285,6 +345,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbxHeaderFieldID.StreamStartBytes:
 | 
			
		||||
					Debug.Assert(m_uFileVersion <= FileVersion32_3);
 | 
			
		||||
					m_pbStreamStartBytes = pbData;
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
@@ -292,6 +353,15 @@ namespace KeePassLib.Serialization
 | 
			
		||||
					SetInnerRandomStreamID(pbData);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbxHeaderFieldID.KdfParameters:
 | 
			
		||||
					m_pwDatabase.KdfParameters = KdfParameters.DeserializeExt(pbData);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case KdbxHeaderFieldID.PublicCustomData:
 | 
			
		||||
					Debug.Assert(m_pwDatabase.PublicCustomData.Count == 0);
 | 
			
		||||
					m_pwDatabase.PublicCustomData = VariantDictionary.Deserialize(pbData);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
					Debug.Assert(false);
 | 
			
		||||
					if(m_slLogger != null)
 | 
			
		||||
@@ -305,7 +375,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
 | 
			
		||||
		private void SetCipher(byte[] pbID)
 | 
			
		||||
		{
 | 
			
		||||
			if((pbID == null) || (pbID.Length != 16))
 | 
			
		||||
			if((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
 | 
			
		||||
				throw new FormatException(KLRes.FileUnknownCipher);
 | 
			
		||||
 | 
			
		||||
			m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
 | 
			
		||||
@@ -329,35 +399,6 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			m_craInnerRandomStream = (CrsAlgorithm)uID;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private Stream AttachStreamDecryptor(Stream s)
 | 
			
		||||
		{
 | 
			
		||||
			MemoryStream ms = new MemoryStream();
 | 
			
		||||
 | 
			
		||||
			Debug.Assert(m_pbMasterSeed.Length == 32);
 | 
			
		||||
			if(m_pbMasterSeed.Length != 32)
 | 
			
		||||
				throw new FormatException(KLRes.MasterSeedLengthInvalid);
 | 
			
		||||
			ms.Write(m_pbMasterSeed, 0, 32);
 | 
			
		||||
 | 
			
		||||
			byte[] pKey32 = m_pwDatabase.MasterKey.GenerateKey32(m_pbTransformSeed,
 | 
			
		||||
				m_pwDatabase.KeyEncryptionRounds).ReadData();
 | 
			
		||||
			if((pKey32 == null) || (pKey32.Length != 32))
 | 
			
		||||
				throw new SecurityException(KLRes.InvalidCompositeKey);
 | 
			
		||||
			ms.Write(pKey32, 0, 32);
 | 
			
		||||
			
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			byte[] aesKey = sha256.ComputeHash(ms.ToArray());
 | 
			
		||||
 | 
			
		||||
			ms.Close();
 | 
			
		||||
			Array.Clear(pKey32, 0, 32);
 | 
			
		||||
 | 
			
		||||
			if((aesKey == null) || (aesKey.Length != 32))
 | 
			
		||||
				throw new SecurityException(KLRes.FinalKeyCreationFailed);
 | 
			
		||||
 | 
			
		||||
			ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid);
 | 
			
		||||
			if(iEngine == null) throw new SecurityException(KLRes.FileUnknownCipher);
 | 
			
		||||
			return iEngine.DecryptStream(s, aesKey, m_pbEncryptionIV);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Obsolete]
 | 
			
		||||
		public static List<PwEntry> ReadEntries(PwDatabase pwDatabase, Stream msData)
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ using System.IO.Compression;
 | 
			
		||||
using KeePassLib.Collections;
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Cryptography.Cipher;
 | 
			
		||||
using KeePassLib.Cryptography.KeyDerivation;
 | 
			
		||||
using KeePassLib.Delegates;
 | 
			
		||||
using KeePassLib.Interfaces;
 | 
			
		||||
using KeePassLib.Keys;
 | 
			
		||||
@@ -54,7 +55,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public sealed partial class KdbxFile
 | 
			
		||||
	{
 | 
			
		||||
		// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat format,
 | 
			
		||||
		// public void Save(string strFile, PwGroup pgDataSource, KdbxFormat fmt,
 | 
			
		||||
		//	IStatusLogger slLogger)
 | 
			
		||||
		// {
 | 
			
		||||
		//	bool bMadeUnhidden = UrlUtil.UnhideFile(strFile);
 | 
			
		||||
@@ -72,56 +73,117 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		/// <param name="pgDataSource">Group containing all groups and
 | 
			
		||||
		/// entries to write. If <c>null</c>, the complete database will
 | 
			
		||||
		/// be written.</param>
 | 
			
		||||
		/// <param name="format">Format of the file to create.</param>
 | 
			
		||||
		/// <param name="fmt">Format of the file to create.</param>
 | 
			
		||||
		/// <param name="slLogger">Logger that recieves status information.</param>
 | 
			
		||||
		public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat format,
 | 
			
		||||
		public void Save(Stream sSaveTo, PwGroup pgDataSource, KdbxFormat fmt,
 | 
			
		||||
			IStatusLogger slLogger)
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert(sSaveTo != null);
 | 
			
		||||
			if(sSaveTo == null) throw new ArgumentNullException("sSaveTo");
 | 
			
		||||
 | 
			
		||||
			m_format = format;
 | 
			
		||||
			m_format = fmt;
 | 
			
		||||
			m_slLogger = slLogger;
 | 
			
		||||
 | 
			
		||||
			HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null);
 | 
			
		||||
 | 
			
		||||
			UTF8Encoding encNoBom = StrUtil.Utf8;
 | 
			
		||||
			CryptoRandom cr = CryptoRandom.Instance;
 | 
			
		||||
			byte[] pbCipherKey = null;
 | 
			
		||||
			byte[] pbHmacKey64 = null;
 | 
			
		||||
 | 
			
		||||
			List<Stream> lStreams = new List<Stream>();
 | 
			
		||||
			lStreams.Add(sSaveTo);
 | 
			
		||||
 | 
			
		||||
			HashingStreamEx sHashing = new HashingStreamEx(sSaveTo, true, null);
 | 
			
		||||
			lStreams.Add(sHashing);
 | 
			
		||||
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				m_pbMasterSeed = cr.GetRandomBytes(32);
 | 
			
		||||
				m_pbTransformSeed = cr.GetRandomBytes(32);
 | 
			
		||||
				m_pbEncryptionIV = cr.GetRandomBytes(16);
 | 
			
		||||
				m_uFileVersion = GetMinKdbxVersion();
 | 
			
		||||
 | 
			
		||||
				m_pbProtectedStreamKey = cr.GetRandomBytes(32);
 | 
			
		||||
				int cbEncKey, cbEncIV;
 | 
			
		||||
				ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
 | 
			
		||||
 | 
			
		||||
				m_pbMasterSeed = cr.GetRandomBytes(32);
 | 
			
		||||
				m_pbEncryptionIV = cr.GetRandomBytes((uint)cbEncIV);
 | 
			
		||||
 | 
			
		||||
				// m_pbTransformSeed = cr.GetRandomBytes(32);
 | 
			
		||||
				PwUuid puKdf = m_pwDatabase.KdfParameters.KdfUuid;
 | 
			
		||||
				KdfEngine kdf = KdfPool.Get(puKdf);
 | 
			
		||||
				if(kdf == null)
 | 
			
		||||
					throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
 | 
			
		||||
						// KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
 | 
			
		||||
						"UUID: " + puKdf.ToHexString() + ".");
 | 
			
		||||
				kdf.Randomize(m_pwDatabase.KdfParameters);
 | 
			
		||||
 | 
			
		||||
				if(m_uFileVersion <= FileVersion32_3)
 | 
			
		||||
				{
 | 
			
		||||
					m_craInnerRandomStream = CrsAlgorithm.Salsa20;
 | 
			
		||||
					m_pbProtectedStreamKey = cr.GetRandomBytes(32);
 | 
			
		||||
				}
 | 
			
		||||
				else // KDBX >= 4
 | 
			
		||||
				{
 | 
			
		||||
					m_craInnerRandomStream = CrsAlgorithm.ChaCha20;
 | 
			
		||||
					m_pbProtectedStreamKey = cr.GetRandomBytes(64);
 | 
			
		||||
				}
 | 
			
		||||
				m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
 | 
			
		||||
					m_pbProtectedStreamKey);
 | 
			
		||||
 | 
			
		||||
				if(m_uFileVersion <= FileVersion32_3)
 | 
			
		||||
					m_pbStreamStartBytes = cr.GetRandomBytes(32);
 | 
			
		||||
 | 
			
		||||
				Stream writerStream;
 | 
			
		||||
				Stream sXml;
 | 
			
		||||
				if(m_format == KdbxFormat.Default)
 | 
			
		||||
				{
 | 
			
		||||
					WriteHeader(hashedStream); // Also flushes the stream
 | 
			
		||||
					byte[] pbHeader = GenerateHeader();
 | 
			
		||||
					m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
 | 
			
		||||
 | 
			
		||||
					Stream sEncrypted = AttachStreamEncryptor(hashedStream);
 | 
			
		||||
					if((sEncrypted == null) || (sEncrypted == hashedStream))
 | 
			
		||||
					MemUtil.Write(sHashing, pbHeader);
 | 
			
		||||
					sHashing.Flush();
 | 
			
		||||
 | 
			
		||||
					ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
 | 
			
		||||
 | 
			
		||||
					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);
 | 
			
		||||
 | 
			
		||||
					sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length);
 | 
			
		||||
						MemUtil.Write(sEncrypted, m_pbStreamStartBytes);
 | 
			
		||||
 | 
			
		||||
					Stream sHashed = new HashedBlockStream(sEncrypted, true);
 | 
			
		||||
						sPlain = new HashedBlockStream(sEncrypted, true);
 | 
			
		||||
					}
 | 
			
		||||
					else // KDBX >= 4
 | 
			
		||||
					{
 | 
			
		||||
						byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
 | 
			
		||||
						MemUtil.Write(sHashing, pbHeaderHmac);
 | 
			
		||||
 | 
			
		||||
						Stream sBlocks = new HmacBlockStream(sHashing, true,
 | 
			
		||||
							true, pbHmacKey64);
 | 
			
		||||
						lStreams.Add(sBlocks);
 | 
			
		||||
 | 
			
		||||
						sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
 | 
			
		||||
							cbEncIV, true);
 | 
			
		||||
						if((sPlain == null) || (sPlain == sBlocks))
 | 
			
		||||
							throw new SecurityException(KLRes.CryptoStreamFailed);
 | 
			
		||||
					}
 | 
			
		||||
					lStreams.Add(sPlain);
 | 
			
		||||
 | 
			
		||||
					if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
 | 
			
		||||
						writerStream = new GZipStream(sHashed, CompressionMode.Compress);
 | 
			
		||||
					else
 | 
			
		||||
						writerStream = sHashed;
 | 
			
		||||
					{
 | 
			
		||||
						sXml = new GZipStream(sPlain, CompressionMode.Compress);
 | 
			
		||||
						lStreams.Add(sXml);
 | 
			
		||||
					}
 | 
			
		||||
					else sXml = sPlain;
 | 
			
		||||
				}
 | 
			
		||||
				else if(m_format == KdbxFormat.PlainXml)
 | 
			
		||||
					writerStream = hashedStream;
 | 
			
		||||
				else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
 | 
			
		||||
					sXml = sHashing;
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					Debug.Assert(false);
 | 
			
		||||
					throw new ArgumentOutOfRangeException("fmt");
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
#if KeePassUAP
 | 
			
		||||
				XmlWriterSettings xws = new XmlWriterSettings();
 | 
			
		||||
@@ -130,44 +192,50 @@ namespace KeePassLib.Serialization
 | 
			
		||||
				xws.IndentChars = "\t";
 | 
			
		||||
				xws.NewLineOnAttributes = false;
 | 
			
		||||
 | 
			
		||||
				XmlWriter xw = XmlWriter.Create(writerStream, xws);
 | 
			
		||||
				XmlWriter xw = XmlWriter.Create(sXml, xws);
 | 
			
		||||
#else
 | 
			
		||||
				XmlTextWriter xw = new XmlTextWriter(writerStream, encNoBom);
 | 
			
		||||
				XmlTextWriter xw = new XmlTextWriter(sXml, encNoBom);
 | 
			
		||||
 | 
			
		||||
				xw.Formatting = Formatting.Indented;
 | 
			
		||||
				xw.IndentChar = '\t';
 | 
			
		||||
				xw.Indentation = 1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
				m_xmlWriter = xw;
 | 
			
		||||
 | 
			
		||||
				WriteDocument(pgDataSource);
 | 
			
		||||
 | 
			
		||||
				m_xmlWriter.Flush();
 | 
			
		||||
				m_xmlWriter.Close();
 | 
			
		||||
				writerStream.Close();
 | 
			
		||||
			}
 | 
			
		||||
			finally { CommonCleanUpWrite(sSaveTo, hashedStream); }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream)
 | 
			
		||||
			finally
 | 
			
		||||
			{
 | 
			
		||||
			hashedStream.Close();
 | 
			
		||||
			m_pbHashOfFileOnDisk = hashedStream.Hash;
 | 
			
		||||
				if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
 | 
			
		||||
				if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
 | 
			
		||||
 | 
			
		||||
			sSaveTo.Close();
 | 
			
		||||
				CommonCleanUpWrite(lStreams, sHashing);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
 | 
			
		||||
		{
 | 
			
		||||
			CloseStreams(lStreams);
 | 
			
		||||
 | 
			
		||||
			Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
 | 
			
		||||
			m_pbHashOfFileOnDisk = sHashing.Hash;
 | 
			
		||||
			Debug.Assert(m_pbHashOfFileOnDisk != null);
 | 
			
		||||
 | 
			
		||||
			m_xmlWriter = null;
 | 
			
		||||
			m_pbHashOfHeader = null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void WriteHeader(Stream s)
 | 
			
		||||
		private byte[] GenerateHeader()
 | 
			
		||||
		{
 | 
			
		||||
			byte[] pbHeader;
 | 
			
		||||
			using(MemoryStream ms = new MemoryStream())
 | 
			
		||||
			{
 | 
			
		||||
			MemoryStream ms = new MemoryStream();
 | 
			
		||||
 | 
			
		||||
				MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1));
 | 
			
		||||
				MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
 | 
			
		||||
			MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileVersion32));
 | 
			
		||||
				MemUtil.Write(ms, MemUtil.UInt32ToBytes(m_uFileVersion));
 | 
			
		||||
 | 
			
		||||
				WriteHeaderField(ms, KdbxHeaderFieldID.CipherID,
 | 
			
		||||
					m_pwDatabase.DataCipherUuid.UuidBytes);
 | 
			
		||||
@@ -177,75 +245,72 @@ namespace KeePassLib.Serialization
 | 
			
		||||
					MemUtil.UInt32ToBytes((uint)nCprID));
 | 
			
		||||
 | 
			
		||||
				WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
 | 
			
		||||
			WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed, m_pbTransformSeed);
 | 
			
		||||
 | 
			
		||||
				if(m_uFileVersion <= FileVersion32_3)
 | 
			
		||||
				{
 | 
			
		||||
					Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
 | 
			
		||||
						(new AesKdf()).Uuid));
 | 
			
		||||
					WriteHeaderField(ms, KdbxHeaderFieldID.TransformSeed,
 | 
			
		||||
						m_pwDatabase.KdfParameters.GetByteArray(AesKdf.ParamSeed));
 | 
			
		||||
					WriteHeaderField(ms, KdbxHeaderFieldID.TransformRounds,
 | 
			
		||||
				MemUtil.UInt64ToBytes(m_pwDatabase.KeyEncryptionRounds));
 | 
			
		||||
						MemUtil.UInt64ToBytes(m_pwDatabase.KdfParameters.GetUInt64(
 | 
			
		||||
						AesKdf.ParamRounds, PwDefs.DefaultKeyEncryptionRounds)));
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
					WriteHeaderField(ms, KdbxHeaderFieldID.KdfParameters,
 | 
			
		||||
						KdfParameters.SerializeExt(m_pwDatabase.KdfParameters));
 | 
			
		||||
 | 
			
		||||
				if(m_pbEncryptionIV.Length > 0)
 | 
			
		||||
					WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
 | 
			
		||||
 | 
			
		||||
				WriteHeaderField(ms, KdbxHeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey);
 | 
			
		||||
			WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes, m_pbStreamStartBytes);
 | 
			
		||||
 | 
			
		||||
				if(m_uFileVersion <= FileVersion32_3)
 | 
			
		||||
					WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
 | 
			
		||||
						m_pbStreamStartBytes);
 | 
			
		||||
 | 
			
		||||
				int nIrsID = (int)m_craInnerRandomStream;
 | 
			
		||||
				WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
 | 
			
		||||
				MemUtil.UInt32ToBytes((uint)nIrsID));
 | 
			
		||||
					MemUtil.Int32ToBytes(nIrsID));
 | 
			
		||||
 | 
			
		||||
				// Write public custom data only when there is at least one item,
 | 
			
		||||
				// because KDBX 3.1 didn't support this field yet
 | 
			
		||||
				if(m_pwDatabase.PublicCustomData.Count > 0)
 | 
			
		||||
					WriteHeaderField(ms, KdbxHeaderFieldID.PublicCustomData,
 | 
			
		||||
						VariantDictionary.Serialize(m_pwDatabase.PublicCustomData));
 | 
			
		||||
 | 
			
		||||
				WriteHeaderField(ms, KdbxHeaderFieldID.EndOfHeader, new byte[] {
 | 
			
		||||
					(byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' });
 | 
			
		||||
 | 
			
		||||
			byte[] pbHeader = ms.ToArray();
 | 
			
		||||
			ms.Close();
 | 
			
		||||
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			m_pbHashOfHeader = sha256.ComputeHash(pbHeader);
 | 
			
		||||
 | 
			
		||||
			s.Write(pbHeader, 0, pbHeader.Length);
 | 
			
		||||
			s.Flush();
 | 
			
		||||
				pbHeader = ms.ToArray();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private static void WriteHeaderField(Stream s, KdbxHeaderFieldID kdbID,
 | 
			
		||||
			return pbHeader;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void WriteHeaderField(Stream s, KdbxHeaderFieldID kdbID,
 | 
			
		||||
			byte[] pbData)
 | 
			
		||||
		{
 | 
			
		||||
			s.WriteByte((byte)kdbID);
 | 
			
		||||
 | 
			
		||||
			if(pbData != null)
 | 
			
		||||
			{
 | 
			
		||||
				ushort uLength = (ushort)pbData.Length;
 | 
			
		||||
				MemUtil.Write(s, MemUtil.UInt16ToBytes(uLength));
 | 
			
		||||
			byte[] pb = (pbData ?? MemUtil.EmptyByteArray);
 | 
			
		||||
			int cb = pb.Length;
 | 
			
		||||
			if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
 | 
			
		||||
 | 
			
		||||
				if(uLength > 0) s.Write(pbData, 0, pbData.Length);
 | 
			
		||||
			}
 | 
			
		||||
			else MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)0));
 | 
			
		||||
			Debug.Assert(m_uFileVersion > 0);
 | 
			
		||||
			if(m_uFileVersion <= FileVersion32_3)
 | 
			
		||||
			{
 | 
			
		||||
				if(cb > (int)ushort.MaxValue)
 | 
			
		||||
				{
 | 
			
		||||
					Debug.Assert(false);
 | 
			
		||||
					throw new ArgumentOutOfRangeException("pbData");
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
		private Stream AttachStreamEncryptor(Stream s)
 | 
			
		||||
		{
 | 
			
		||||
			MemoryStream ms = new MemoryStream();
 | 
			
		||||
				MemUtil.Write(s, MemUtil.UInt16ToBytes((ushort)cb));
 | 
			
		||||
			}
 | 
			
		||||
			else MemUtil.Write(s, MemUtil.Int32ToBytes(cb));
 | 
			
		||||
 | 
			
		||||
			Debug.Assert(m_pbMasterSeed != null);
 | 
			
		||||
			Debug.Assert(m_pbMasterSeed.Length == 32);
 | 
			
		||||
			ms.Write(m_pbMasterSeed, 0, 32);
 | 
			
		||||
 | 
			
		||||
			Debug.Assert(m_pwDatabase != null);
 | 
			
		||||
			Debug.Assert(m_pwDatabase.MasterKey != null);
 | 
			
		||||
			ProtectedBinary pbinKey = m_pwDatabase.MasterKey.GenerateKey32(
 | 
			
		||||
				m_pbTransformSeed, m_pwDatabase.KeyEncryptionRounds);
 | 
			
		||||
			Debug.Assert(pbinKey != null);
 | 
			
		||||
			if(pbinKey == null)
 | 
			
		||||
				throw new SecurityException(KLRes.InvalidCompositeKey);
 | 
			
		||||
			byte[] pKey32 = pbinKey.ReadData();
 | 
			
		||||
			if((pKey32 == null) || (pKey32.Length != 32))
 | 
			
		||||
				throw new SecurityException(KLRes.InvalidCompositeKey);
 | 
			
		||||
			ms.Write(pKey32, 0, 32);
 | 
			
		||||
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			byte[] aesKey = sha256.ComputeHash(ms.ToArray());
 | 
			
		||||
			
 | 
			
		||||
			ms.Close();
 | 
			
		||||
			Array.Clear(pKey32, 0, 32);
 | 
			
		||||
 | 
			
		||||
			Debug.Assert(CipherPool.GlobalPool != null);
 | 
			
		||||
			ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid);
 | 
			
		||||
			if(iEngine == null) throw new SecurityException(KLRes.FileUnknownCipher);
 | 
			
		||||
			return iEngine.EncryptStream(s, aesKey, m_pbEncryptionIV);
 | 
			
		||||
			if(cb > 0) s.Write(pb, 0, cb);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void WriteDocument(PwGroup pgDataSource)
 | 
			
		||||
@@ -331,12 +396,15 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		{
 | 
			
		||||
			m_xmlWriter.WriteStartElement(ElemMeta);
 | 
			
		||||
 | 
			
		||||
			WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false); // Generator name
 | 
			
		||||
			WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false);
 | 
			
		||||
 | 
			
		||||
			if(m_pbHashOfHeader != null)
 | 
			
		||||
			if((m_pbHashOfHeader != null) && (m_uFileVersion <= FileVersion32_3))
 | 
			
		||||
				WriteObject(ElemHeaderHash, Convert.ToBase64String(
 | 
			
		||||
					m_pbHashOfHeader), false);
 | 
			
		||||
 | 
			
		||||
			if(m_uFileVersion > FileVersion32_3)
 | 
			
		||||
				WriteObject(ElemSettingsChanged, m_pwDatabase.SettingsChanged);
 | 
			
		||||
 | 
			
		||||
			WriteObject(ElemDbName, m_pwDatabase.Name, true);
 | 
			
		||||
			WriteObject(ElemDbNameChanged, m_pwDatabase.NameChanged);
 | 
			
		||||
			WriteObject(ElemDbDesc, m_pwDatabase.Description, true);
 | 
			
		||||
@@ -387,6 +455,9 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			WriteObject(ElemEnableAutoType, StrUtil.BoolToStringEx(pg.EnableAutoType), false);
 | 
			
		||||
			WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
 | 
			
		||||
			WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
 | 
			
		||||
 | 
			
		||||
			if(pg.CustomData.Count > 0)
 | 
			
		||||
				WriteList(ElemCustomData, pg.CustomData);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void EndGroup()
 | 
			
		||||
@@ -417,6 +488,9 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			WriteList(pe.Binaries);
 | 
			
		||||
			WriteList(ElemAutoType, pe.AutoType);
 | 
			
		||||
 | 
			
		||||
			if(pe.CustomData.Count > 0)
 | 
			
		||||
				WriteList(ElemCustomData, pe.CustomData);
 | 
			
		||||
 | 
			
		||||
			if(!bIsHistory) WriteList(ElemHistory, pe.History, true);
 | 
			
		||||
			else { Debug.Assert(pe.History.UCount == 0); }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,13 +22,21 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Security;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
 | 
			
		||||
#if !KeePassUAP
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Collections;
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Cryptography.Cipher;
 | 
			
		||||
using KeePassLib.Cryptography.KeyDerivation;
 | 
			
		||||
using KeePassLib.Delegates;
 | 
			
		||||
using KeePassLib.Interfaces;
 | 
			
		||||
using KeePassLib.Resources;
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +81,8 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		/// The first 2 bytes are critical (i.e. loading will fail, if the
 | 
			
		||||
		/// file version is too high), the last 2 bytes are informational.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		private const uint FileVersion32 = 0x00030001;
 | 
			
		||||
		private const uint FileVersion32 = 0x00040000;
 | 
			
		||||
		private const uint FileVersion32_3 = 0x00030001; // Old format
 | 
			
		||||
 | 
			
		||||
		private const uint FileVersionCriticalMask = 0xFFFF0000;
 | 
			
		||||
 | 
			
		||||
@@ -92,6 +101,7 @@ namespace KeePassLib.Serialization
 | 
			
		||||
 | 
			
		||||
		private const string ElemGenerator = "Generator";
 | 
			
		||||
		private const string ElemHeaderHash = "HeaderHash";
 | 
			
		||||
		private const string ElemSettingsChanged = "SettingsChanged";
 | 
			
		||||
		private const string ElemDbName = "DatabaseName";
 | 
			
		||||
		private const string ElemDbNameChanged = "DatabaseNameChanged";
 | 
			
		||||
		private const string ElemDbDesc = "DatabaseDescription";
 | 
			
		||||
@@ -192,14 +202,15 @@ namespace KeePassLib.Serialization
 | 
			
		||||
		private KdbxFormat m_format = KdbxFormat.Default;
 | 
			
		||||
		private IStatusLogger m_slLogger = null;
 | 
			
		||||
 | 
			
		||||
		private uint m_uFileVersion = 0;
 | 
			
		||||
		private byte[] m_pbMasterSeed = null;
 | 
			
		||||
		private byte[] m_pbTransformSeed = null;
 | 
			
		||||
		// private byte[] m_pbTransformSeed = null;
 | 
			
		||||
		private byte[] m_pbEncryptionIV = null;
 | 
			
		||||
		private byte[] m_pbProtectedStreamKey = null;
 | 
			
		||||
		private byte[] m_pbStreamStartBytes = null;
 | 
			
		||||
 | 
			
		||||
		// ArcFourVariant only for compatibility; KeePass will default to a
 | 
			
		||||
		// different (more secure) algorithm when *writing* databases
 | 
			
		||||
		// ArcFourVariant only for backward compatibility; KeePass defaults
 | 
			
		||||
		// to a more secure algorithm when *writing* databases
 | 
			
		||||
		private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
 | 
			
		||||
 | 
			
		||||
		private Dictionary<string, ProtectedBinary> m_dictBinPool =
 | 
			
		||||
@@ -222,12 +233,14 @@ namespace KeePassLib.Serialization
 | 
			
		||||
			CipherID = 2,
 | 
			
		||||
			CompressionFlags = 3,
 | 
			
		||||
			MasterSeed = 4,
 | 
			
		||||
			TransformSeed = 5,
 | 
			
		||||
			TransformRounds = 6,
 | 
			
		||||
			TransformSeed = 5, // KDBX 3.1, for backward compatibility only
 | 
			
		||||
			TransformRounds = 6, // KDBX 3.1, for backward compatibility only
 | 
			
		||||
			EncryptionIV = 7,
 | 
			
		||||
			ProtectedStreamKey = 8,
 | 
			
		||||
			StreamStartBytes = 9,
 | 
			
		||||
			InnerRandomStreamID = 10
 | 
			
		||||
			StreamStartBytes = 9, // KDBX 3.1, for backward compatibility only
 | 
			
		||||
			InnerRandomStreamID = 10,
 | 
			
		||||
			KdfParameters = 11, // KDBX 4
 | 
			
		||||
			PublicCustomData = 12 // KDBX 4
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public byte[] HashOfFileOnDisk
 | 
			
		||||
@@ -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)
 | 
			
		||||
		{
 | 
			
		||||
			m_dictBinPool = new Dictionary<string, ProtectedBinary>();
 | 
			
		||||
 
 | 
			
		||||
@@ -27,10 +27,10 @@ using System.Xml.Serialization;
 | 
			
		||||
 | 
			
		||||
#if !KeePassUAP
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
namespace KeePassLib.Translation
 | 
			
		||||
@@ -333,9 +333,7 @@ namespace KeePassLib.Translation
 | 
			
		||||
			WriteControlDependentParams(sb, c);
 | 
			
		||||
 | 
			
		||||
			byte[] pb = StrUtil.Utf8.GetBytes(sb.ToString());
 | 
			
		||||
 | 
			
		||||
			SHA256Managed sha256 = new SHA256Managed();
 | 
			
		||||
			byte[] pbSha = sha256.ComputeHash(pb);
 | 
			
		||||
			byte[] pbSha = CryptoUtil.HashSha256(pb);
 | 
			
		||||
 | 
			
		||||
			// Also see MatchHash
 | 
			
		||||
			return "v1:" + Convert.ToBase64String(pbSha, 0, 3,
 | 
			
		||||
 
 | 
			
		||||
@@ -214,14 +214,11 @@ namespace KeePassLib.Utility
 | 
			
		||||
			const int SizeICONDIR = 6;
 | 
			
		||||
			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(BitConverter.ToUInt16(pb, 0) != 0) return null; // Reserved, 0
 | 
			
		||||
			if(BitConverter.ToUInt16(pb, 2) != 1) return null; // ICO type, 1
 | 
			
		||||
			if(MemUtil.BytesToUInt16(pb, 0) != 0) return null; // Reserved, 0
 | 
			
		||||
			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; }
 | 
			
		||||
 | 
			
		||||
			int cbDir = SizeICONDIR + (n * SizeICONDIRENTRY);
 | 
			
		||||
@@ -235,10 +232,10 @@ namespace KeePassLib.Utility
 | 
			
		||||
				int h = pb[iOffset + 1];
 | 
			
		||||
				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)
 | 
			
		||||
 | 
			
		||||
				int p = BitConverter.ToInt32(pb, iOffset + 12);
 | 
			
		||||
				int p = MemUtil.BytesToInt32(pb, iOffset + 12);
 | 
			
		||||
				if(p < cbDir) return null;
 | 
			
		||||
				if((p + cb) > pb.Length) return null;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,8 @@ namespace KeePassLib.Utility
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public static class MemUtil
 | 
			
		||||
	{
 | 
			
		||||
		internal static readonly byte[] EmptyByteArray = new byte[0];
 | 
			
		||||
 | 
			
		||||
		private static readonly uint[] m_vSBox = new uint[256] {
 | 
			
		||||
			0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230,
 | 
			
		||||
			0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4,
 | 
			
		||||
@@ -262,6 +264,22 @@ namespace KeePassLib.Utility
 | 
			
		||||
			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>
 | 
			
		||||
		/// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
@@ -269,11 +287,26 @@ namespace KeePassLib.Utility
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert((pb != null) && (pb.Length == 2));
 | 
			
		||||
			if(pb == null) throw new ArgumentNullException("pb");
 | 
			
		||||
			if(pb.Length != 2) throw new ArgumentException();
 | 
			
		||||
			if(pb.Length != 2) throw new ArgumentOutOfRangeException("pb");
 | 
			
		||||
 | 
			
		||||
			return (ushort)((ushort)pb[0] | ((ushort)pb[1] << 8));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public static ushort BytesToUInt16(byte[] pb, int iOffset)
 | 
			
		||||
		{
 | 
			
		||||
			if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
 | 
			
		||||
			if((iOffset < 0) || ((iOffset + 1) >= pb.Length))
 | 
			
		||||
			{
 | 
			
		||||
				Debug.Assert(false);
 | 
			
		||||
				throw new ArgumentOutOfRangeException("iOffset");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return (ushort)((ushort)pb[iOffset] | ((ushort)pb[iOffset + 1] << 8));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Convert 4 bytes to a 32-bit unsigned integer (little-endian).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
@@ -281,12 +314,28 @@ namespace KeePassLib.Utility
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert((pb != null) && (pb.Length == 4));
 | 
			
		||||
			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) |
 | 
			
		||||
				((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>
 | 
			
		||||
		/// Convert 8 bytes to a 64-bit unsigned integer (little-endian).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
@@ -294,13 +343,54 @@ namespace KeePassLib.Utility
 | 
			
		||||
		{
 | 
			
		||||
			Debug.Assert((pb != null) && (pb.Length == 8));
 | 
			
		||||
			if(pb == null) throw new ArgumentNullException("pb");
 | 
			
		||||
			if(pb.Length != 8) throw new ArgumentException();
 | 
			
		||||
			if(pb.Length != 8) throw new ArgumentOutOfRangeException("pb");
 | 
			
		||||
 | 
			
		||||
			return ((ulong)pb[0] | ((ulong)pb[1] << 8) | ((ulong)pb[2] << 16) |
 | 
			
		||||
				((ulong)pb[3] << 24) | ((ulong)pb[4] << 32) | ((ulong)pb[5] << 40) |
 | 
			
		||||
				((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>
 | 
			
		||||
		/// Convert a 16-bit unsigned integer to 2 bytes (little-endian).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
@@ -335,6 +425,27 @@ namespace KeePassLib.Utility
 | 
			
		||||
			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>
 | 
			
		||||
		/// Convert a 64-bit unsigned integer to 8 bytes (little-endian).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
@@ -357,6 +468,61 @@ namespace KeePassLib.Utility
 | 
			
		||||
			return pb;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Convert a 64-bit unsigned integer to 8 bytes (little-endian).
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public static void UInt64ToBytesEx(ulong uValue, byte[] pb, int iOffset)
 | 
			
		||||
		{
 | 
			
		||||
			if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
 | 
			
		||||
			if((iOffset < 0) || ((iOffset + 7) >= pb.Length))
 | 
			
		||||
			{
 | 
			
		||||
				Debug.Assert(false);
 | 
			
		||||
				throw new ArgumentOutOfRangeException("iOffset");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			unchecked
 | 
			
		||||
			{
 | 
			
		||||
				pb[iOffset] = (byte)uValue;
 | 
			
		||||
				pb[iOffset + 1] = (byte)(uValue >> 8);
 | 
			
		||||
				pb[iOffset + 2] = (byte)(uValue >> 16);
 | 
			
		||||
				pb[iOffset + 3] = (byte)(uValue >> 24);
 | 
			
		||||
				pb[iOffset + 4] = (byte)(uValue >> 32);
 | 
			
		||||
				pb[iOffset + 5] = (byte)(uValue >> 40);
 | 
			
		||||
				pb[iOffset + 6] = (byte)(uValue >> 48);
 | 
			
		||||
				pb[iOffset + 7] = (byte)(uValue >> 56);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static byte[] Int32ToBytes(int iValue)
 | 
			
		||||
		{
 | 
			
		||||
			return UInt32ToBytes((uint)iValue);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static byte[] Int64ToBytes(long lValue)
 | 
			
		||||
		{
 | 
			
		||||
			return UInt64ToBytes((ulong)lValue);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static uint RotateLeft32(uint u, int nBits)
 | 
			
		||||
		{
 | 
			
		||||
			return ((u << nBits) | (u >> (32 - nBits)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static uint RotateRight32(uint u, int nBits)
 | 
			
		||||
		{
 | 
			
		||||
			return ((u >> nBits) | (u << (32 - nBits)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static ulong RotateLeft64(ulong u, int nBits)
 | 
			
		||||
		{
 | 
			
		||||
			return ((u << nBits) | (u >> (64 - nBits)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static ulong RotateRight64(ulong u, int nBits)
 | 
			
		||||
		{
 | 
			
		||||
			return ((u >> nBits) | (u << (64 - nBits)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool ArraysEqual(byte[] x, byte[] y)
 | 
			
		||||
		{
 | 
			
		||||
			// Return false if one of them is null (not comparable)!
 | 
			
		||||
@@ -372,19 +538,21 @@ namespace KeePassLib.Utility
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static void XorArray(byte[] pbSource, int nSourceOffset,
 | 
			
		||||
			byte[] pbBuffer, int nBufferOffset, int nLength)
 | 
			
		||||
		public static void XorArray(byte[] pbSource, int iSourceOffset,
 | 
			
		||||
			byte[] pbBuffer, int iBufferOffset, int cb)
 | 
			
		||||
		{
 | 
			
		||||
			if(pbSource == null) throw new ArgumentNullException("pbSource");
 | 
			
		||||
			if(nSourceOffset < 0) throw new ArgumentException();
 | 
			
		||||
			if(iSourceOffset < 0) throw new ArgumentOutOfRangeException("iSourceOffset");
 | 
			
		||||
			if(pbBuffer == null) throw new ArgumentNullException("pbBuffer");
 | 
			
		||||
			if(nBufferOffset < 0) throw new ArgumentException();
 | 
			
		||||
			if(nLength < 0) throw new ArgumentException();
 | 
			
		||||
			if((nSourceOffset + nLength) > pbSource.Length) throw new ArgumentException();
 | 
			
		||||
			if((nBufferOffset + nLength) > pbBuffer.Length) throw new ArgumentException();
 | 
			
		||||
			if(iBufferOffset < 0) throw new ArgumentOutOfRangeException("iBufferOffset");
 | 
			
		||||
			if(cb < 0) throw new ArgumentOutOfRangeException("cb");
 | 
			
		||||
			if(iSourceOffset > (pbSource.Length - cb))
 | 
			
		||||
				throw new ArgumentOutOfRangeException("cb");
 | 
			
		||||
			if(iBufferOffset > (pbBuffer.Length - cb))
 | 
			
		||||
				throw new ArgumentOutOfRangeException("cb");
 | 
			
		||||
 | 
			
		||||
			for(int i = 0; i < nLength; ++i)
 | 
			
		||||
				pbBuffer[nBufferOffset + i] ^= pbSource[nSourceOffset + i];
 | 
			
		||||
			for(int i = 0; i < cb; ++i)
 | 
			
		||||
				pbBuffer[iBufferOffset + i] ^= pbSource[iSourceOffset + i];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
@@ -463,7 +631,8 @@ namespace KeePassLib.Utility
 | 
			
		||||
			if(s == null) { Debug.Assert(false); return; }
 | 
			
		||||
			if(pbData == null) { Debug.Assert(false); return; }
 | 
			
		||||
 | 
			
		||||
			s.Write(pbData, 0, pbData.Length);
 | 
			
		||||
			Debug.Assert(pbData.Length >= 0);
 | 
			
		||||
			if(pbData.Length > 0) s.Write(pbData, 0, pbData.Length);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static byte[] Compress(byte[] pbData)
 | 
			
		||||
 
 | 
			
		||||
@@ -70,6 +70,12 @@ namespace KeePassLib.Utility
 | 
			
		||||
		// 1418:
 | 
			
		||||
		//   Minimizing a form while loading it doesn't work.
 | 
			
		||||
		//   https://sourceforge.net/p/keepass/bugs/1418/
 | 
			
		||||
		// 2139:
 | 
			
		||||
		//   Shortcut keys are ignored.
 | 
			
		||||
		//   https://sourceforge.net/p/keepass/feature-requests/2139/
 | 
			
		||||
		// 2140:
 | 
			
		||||
		//   Explicit control focusing is ignored.
 | 
			
		||||
		//   https://sourceforge.net/p/keepass/feature-requests/2140/
 | 
			
		||||
		// 5795:
 | 
			
		||||
		//   Text in input field is incomplete.
 | 
			
		||||
		//   https://bugzilla.xamarin.com/show_bug.cgi?id=5795
 | 
			
		||||
 
 | 
			
		||||
@@ -978,13 +978,12 @@ namespace KeePassLib.Utility
 | 
			
		||||
		public static bool IsHexString(string str, bool bStrict)
 | 
			
		||||
		{
 | 
			
		||||
			if(str == null) throw new ArgumentNullException("str");
 | 
			
		||||
			if(str.Length == 0) return true;
 | 
			
		||||
 | 
			
		||||
			foreach(char ch in str)
 | 
			
		||||
			{
 | 
			
		||||
				if((ch >= '0') && (ch <= '9')) continue;
 | 
			
		||||
				if((ch >= 'a') && (ch <= 'z')) continue;
 | 
			
		||||
				if((ch >= 'A') && (ch <= 'Z')) continue;
 | 
			
		||||
				if((ch >= 'a') && (ch <= 'f')) continue;
 | 
			
		||||
				if((ch >= 'A') && (ch <= 'F')) continue;
 | 
			
		||||
 | 
			
		||||
				if(bStrict) return false;
 | 
			
		||||
 | 
			
		||||
@@ -997,6 +996,29 @@ namespace KeePassLib.Utility
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool IsHexString(byte[] pbUtf8, bool bStrict)
 | 
			
		||||
		{
 | 
			
		||||
			if(pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
 | 
			
		||||
 | 
			
		||||
			for(int i = 0; i < pbUtf8.Length; ++i)
 | 
			
		||||
			{
 | 
			
		||||
				byte bt = pbUtf8[i];
 | 
			
		||||
				if((bt >= (byte)'0') && (bt <= (byte)'9')) continue;
 | 
			
		||||
				if((bt >= (byte)'a') && (bt <= (byte)'f')) continue;
 | 
			
		||||
				if((bt >= (byte)'A') && (bt <= (byte)'F')) continue;
 | 
			
		||||
 | 
			
		||||
				if(bStrict) return false;
 | 
			
		||||
 | 
			
		||||
				if((bt == (byte)' ') || (bt == (byte)'\t') ||
 | 
			
		||||
					(bt == (byte)'\r') || (bt == (byte)'\n'))
 | 
			
		||||
					continue;
 | 
			
		||||
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
#if !KeePassLibSD
 | 
			
		||||
		private static readonly char[] m_vPatternPartsSep = new char[] { '*' };
 | 
			
		||||
		public static bool SimplePatternMatch(string strPattern, string strText,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user