mirror of
https://github.com/wismna/ModernKeePassLib.git
synced 2025-12-06 00:02:44 -05:00
Setup solution
This commit is contained in:
244
ModernKeePassLib/Collections/AutoTypeConfig.cs
Normal file
244
ModernKeePassLib/Collections/AutoTypeConfig.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Interfaces;
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
[Flags]
|
||||
public enum AutoTypeObfuscationOptions
|
||||
{
|
||||
None = 0,
|
||||
UseClipboard = 1
|
||||
}
|
||||
|
||||
public sealed class AutoTypeAssociation : IEquatable<AutoTypeAssociation>,
|
||||
IDeepCloneable<AutoTypeAssociation>
|
||||
{
|
||||
private string m_strWindow = string.Empty;
|
||||
public string WindowName
|
||||
{
|
||||
get { return m_strWindow; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_strWindow = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strSequence = string.Empty;
|
||||
public string Sequence
|
||||
{
|
||||
get { return m_strSequence; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_strSequence = value;
|
||||
}
|
||||
}
|
||||
|
||||
public AutoTypeAssociation() { }
|
||||
|
||||
public AutoTypeAssociation(string strWindow, string strSeq)
|
||||
{
|
||||
if(strWindow == null) throw new ArgumentNullException("strWindow");
|
||||
if(strSeq == null) throw new ArgumentNullException("strSeq");
|
||||
|
||||
m_strWindow = strWindow;
|
||||
m_strSequence = strSeq;
|
||||
}
|
||||
|
||||
public bool Equals(AutoTypeAssociation other)
|
||||
{
|
||||
if(other == null) return false;
|
||||
|
||||
if(m_strWindow != other.m_strWindow) return false;
|
||||
if(m_strSequence != other.m_strSequence) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public AutoTypeAssociation CloneDeep()
|
||||
{
|
||||
return (AutoTypeAssociation)this.MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of auto-type associations.
|
||||
/// </summary>
|
||||
public sealed class AutoTypeConfig : IEquatable<AutoTypeConfig>,
|
||||
IDeepCloneable<AutoTypeConfig>
|
||||
{
|
||||
private bool m_bEnabled = true;
|
||||
private AutoTypeObfuscationOptions m_atooObfuscation =
|
||||
AutoTypeObfuscationOptions.None;
|
||||
private string m_strDefaultSequence = string.Empty;
|
||||
private List<AutoTypeAssociation> m_lWindowAssocs =
|
||||
new List<AutoTypeAssociation>();
|
||||
|
||||
/// <summary>
|
||||
/// Specify whether auto-type is enabled or not.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get { return m_bEnabled; }
|
||||
set { m_bEnabled = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specify whether the typing should be obfuscated.
|
||||
/// </summary>
|
||||
public AutoTypeObfuscationOptions ObfuscationOptions
|
||||
{
|
||||
get { return m_atooObfuscation; }
|
||||
set { m_atooObfuscation = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default keystroke sequence that is auto-typed if
|
||||
/// no matching window is found in the <c>Associations</c>
|
||||
/// container.
|
||||
/// </summary>
|
||||
public string DefaultSequence
|
||||
{
|
||||
get { return m_strDefaultSequence; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_strDefaultSequence = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all auto-type window/keystroke sequence pairs.
|
||||
/// </summary>
|
||||
public IEnumerable<AutoTypeAssociation> Associations
|
||||
{
|
||||
get { return m_lWindowAssocs; }
|
||||
}
|
||||
|
||||
public int AssociationsCount
|
||||
{
|
||||
get { return m_lWindowAssocs.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new auto-type associations list.
|
||||
/// </summary>
|
||||
public AutoTypeConfig()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all associations.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_lWindowAssocs.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the auto-type associations list.
|
||||
/// </summary>
|
||||
/// <returns>New, cloned object.</returns>
|
||||
public AutoTypeConfig CloneDeep()
|
||||
{
|
||||
AutoTypeConfig newCfg = new AutoTypeConfig();
|
||||
|
||||
newCfg.m_bEnabled = m_bEnabled;
|
||||
newCfg.m_atooObfuscation = m_atooObfuscation;
|
||||
newCfg.m_strDefaultSequence = m_strDefaultSequence;
|
||||
|
||||
foreach(AutoTypeAssociation a in m_lWindowAssocs)
|
||||
newCfg.Add(a.CloneDeep());
|
||||
|
||||
return newCfg;
|
||||
}
|
||||
|
||||
public bool Equals(AutoTypeConfig other)
|
||||
{
|
||||
if(other == null) { Debug.Assert(false); return false; }
|
||||
|
||||
if(m_bEnabled != other.m_bEnabled) return false;
|
||||
if(m_atooObfuscation != other.m_atooObfuscation) return false;
|
||||
if(m_strDefaultSequence != other.m_strDefaultSequence) return false;
|
||||
|
||||
if(m_lWindowAssocs.Count != other.m_lWindowAssocs.Count) return false;
|
||||
for(int i = 0; i < m_lWindowAssocs.Count; ++i)
|
||||
{
|
||||
if(!m_lWindowAssocs[i].Equals(other.m_lWindowAssocs[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public AutoTypeAssociation GetAt(int iIndex)
|
||||
{
|
||||
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
|
||||
throw new ArgumentOutOfRangeException("iIndex");
|
||||
|
||||
return m_lWindowAssocs[iIndex];
|
||||
}
|
||||
|
||||
public void Add(AutoTypeAssociation a)
|
||||
{
|
||||
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
|
||||
|
||||
m_lWindowAssocs.Add(a);
|
||||
}
|
||||
|
||||
public void Insert(int iIndex, AutoTypeAssociation a)
|
||||
{
|
||||
if((iIndex < 0) || (iIndex > m_lWindowAssocs.Count))
|
||||
throw new ArgumentOutOfRangeException("iIndex");
|
||||
if(a == null) { Debug.Assert(false); throw new ArgumentNullException("a"); }
|
||||
|
||||
m_lWindowAssocs.Insert(iIndex, a);
|
||||
}
|
||||
|
||||
public void RemoveAt(int iIndex)
|
||||
{
|
||||
if((iIndex < 0) || (iIndex >= m_lWindowAssocs.Count))
|
||||
throw new ArgumentOutOfRangeException("iIndex");
|
||||
|
||||
m_lWindowAssocs.RemoveAt(iIndex);
|
||||
}
|
||||
|
||||
// public void Sort()
|
||||
// {
|
||||
// m_lWindowAssocs.Sort(AutoTypeConfig.AssocCompareFn);
|
||||
// }
|
||||
|
||||
// private static int AssocCompareFn(AutoTypeAssociation x,
|
||||
// AutoTypeAssociation y)
|
||||
// {
|
||||
// if(x == null) { Debug.Assert(false); return ((y == null) ? 0 : -1); }
|
||||
// if(y == null) { Debug.Assert(false); return 1; }
|
||||
// int cn = x.WindowName.CompareTo(y.WindowName);
|
||||
// if(cn != 0) return cn;
|
||||
// return x.Sequence.CompareTo(y.Sequence);
|
||||
// }
|
||||
}
|
||||
}
|
||||
172
ModernKeePassLib/Collections/ProtectedBinaryDictionary.cs
Normal file
172
ModernKeePassLib/Collections/ProtectedBinaryDictionary.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Security;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of <c>ProtectedBinary</c> objects (dictionary).
|
||||
/// </summary>
|
||||
public sealed class ProtectedBinaryDictionary :
|
||||
IDeepCloneable<ProtectedBinaryDictionary>,
|
||||
IEnumerable<KeyValuePair<string, ProtectedBinary>>
|
||||
{
|
||||
private SortedDictionary<string, ProtectedBinary> m_vBinaries =
|
||||
new SortedDictionary<string, ProtectedBinary>();
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of binaries in this entry.
|
||||
/// </summary>
|
||||
public uint UCount
|
||||
{
|
||||
get { return (uint)m_vBinaries.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new list of protected binaries.
|
||||
/// </summary>
|
||||
public ProtectedBinaryDictionary()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_vBinaries.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, ProtectedBinary>> GetEnumerator()
|
||||
{
|
||||
return m_vBinaries.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_vBinaries.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the current <c>ProtectedBinaryList</c> object, including all
|
||||
/// stored protected strings.
|
||||
/// </summary>
|
||||
/// <returns>New <c>ProtectedBinaryList</c> object.</returns>
|
||||
public ProtectedBinaryDictionary CloneDeep()
|
||||
{
|
||||
ProtectedBinaryDictionary plNew = new ProtectedBinaryDictionary();
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_vBinaries)
|
||||
{
|
||||
// ProtectedBinary objects are immutable
|
||||
plNew.Set(kvpBin.Key, kvpBin.Value);
|
||||
}
|
||||
|
||||
return plNew;
|
||||
}
|
||||
|
||||
public bool EqualsDictionary(ProtectedBinaryDictionary dict)
|
||||
{
|
||||
if(dict == null) { Debug.Assert(false); return false; }
|
||||
|
||||
if(m_vBinaries.Count != dict.m_vBinaries.Count) return false;
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_vBinaries)
|
||||
{
|
||||
ProtectedBinary pb = dict.Get(kvp.Key);
|
||||
if(pb == null) return false;
|
||||
if(!pb.Equals(kvp.Value)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get one of the stored binaries.
|
||||
/// </summary>
|
||||
/// <param name="strName">Binary identifier.</param>
|
||||
/// <returns>Protected binary. If the binary identified by
|
||||
/// <paramref name="strName" /> cannot be found, the function
|
||||
/// returns <c>null</c>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public ProtectedBinary Get(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
ProtectedBinary pb;
|
||||
if(m_vBinaries.TryGetValue(strName, out pb)) return pb;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a binary object.
|
||||
/// </summary>
|
||||
/// <param name="strField">Identifier of the binary field to modify.</param>
|
||||
/// <param name="pbNewValue">New value. This parameter must not be <c>null</c>.</param>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if any of the input
|
||||
/// parameters is <c>null</c>.</exception>
|
||||
public void Set(string strField, ProtectedBinary pbNewValue)
|
||||
{
|
||||
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
|
||||
Debug.Assert(pbNewValue != null); if(pbNewValue == null) throw new ArgumentNullException("pbNewValue");
|
||||
|
||||
m_vBinaries[strField] = pbNewValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a binary object.
|
||||
/// </summary>
|
||||
/// <param name="strField">Identifier of the binary field to remove.</param>
|
||||
/// <returns>Returns <c>true</c> if the object has been successfully
|
||||
/// removed, otherwise <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");
|
||||
|
||||
return m_vBinaries.Remove(strField);
|
||||
}
|
||||
|
||||
public string KeysToString()
|
||||
{
|
||||
if(m_vBinaries.Count == 0) return string.Empty;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_vBinaries)
|
||||
{
|
||||
if(sb.Length > 0) sb.Append(", ");
|
||||
sb.Append(kvp.Key);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
174
ModernKeePassLib/Collections/ProtectedBinarySet.cs
Normal file
174
ModernKeePassLib/Collections/ProtectedBinarySet.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Security;
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
internal sealed class ProtectedBinarySet : IEnumerable<KeyValuePair<int, ProtectedBinary>>
|
||||
{
|
||||
private Dictionary<int, ProtectedBinary> m_d =
|
||||
new Dictionary<int, ProtectedBinary>();
|
||||
|
||||
public ProtectedBinarySet()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_d.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<int, ProtectedBinary>> GetEnumerator()
|
||||
{
|
||||
return m_d.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_d.Clear();
|
||||
}
|
||||
|
||||
private int GetFreeID()
|
||||
{
|
||||
int i = m_d.Count;
|
||||
while(m_d.ContainsKey(i)) { ++i; }
|
||||
Debug.Assert(i == m_d.Count); // m_d.Count should be free
|
||||
return i;
|
||||
}
|
||||
|
||||
public ProtectedBinary Get(int iID)
|
||||
{
|
||||
ProtectedBinary pb;
|
||||
if(m_d.TryGetValue(iID, out pb)) return pb;
|
||||
|
||||
// Debug.Assert(false); // No assert
|
||||
return null;
|
||||
}
|
||||
|
||||
public int Find(ProtectedBinary pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return -1; }
|
||||
|
||||
// Fast search by reference
|
||||
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
|
||||
{
|
||||
if(object.ReferenceEquals(pb, kvp.Value))
|
||||
{
|
||||
Debug.Assert(pb.Equals(kvp.Value));
|
||||
return kvp.Key;
|
||||
}
|
||||
}
|
||||
|
||||
// Slow search by content
|
||||
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
|
||||
{
|
||||
if(pb.Equals(kvp.Value)) return kvp.Key;
|
||||
}
|
||||
|
||||
// Debug.Assert(false); // No assert
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Set(int iID, ProtectedBinary pb)
|
||||
{
|
||||
if(iID < 0) { Debug.Assert(false); return; }
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
m_d[iID] = pb;
|
||||
}
|
||||
|
||||
public void Add(ProtectedBinary pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
int i = Find(pb);
|
||||
if(i >= 0) return; // Exists already
|
||||
|
||||
i = GetFreeID();
|
||||
m_d[i] = pb;
|
||||
}
|
||||
|
||||
public void AddFrom(ProtectedBinaryDictionary d)
|
||||
{
|
||||
if(d == null) { Debug.Assert(false); return; }
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in d)
|
||||
{
|
||||
Add(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFrom(PwGroup pg)
|
||||
{
|
||||
if(pg == null) { Debug.Assert(false); return; }
|
||||
|
||||
EntryHandler eh = delegate(PwEntry pe)
|
||||
{
|
||||
if(pe == null) { Debug.Assert(false); return true; }
|
||||
|
||||
AddFrom(pe.Binaries);
|
||||
foreach(PwEntry peHistory in pe.History)
|
||||
{
|
||||
if(peHistory == null) { Debug.Assert(false); continue; }
|
||||
AddFrom(peHistory.Binaries);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
pg.TraverseTree(TraversalMethod.PreOrder, null, eh);
|
||||
}
|
||||
|
||||
public ProtectedBinary[] ToArray()
|
||||
{
|
||||
int n = m_d.Count;
|
||||
ProtectedBinary[] v = new ProtectedBinary[n];
|
||||
|
||||
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
|
||||
{
|
||||
if((kvp.Key < 0) || (kvp.Key >= n))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
v[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
if(v[i] == null)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
298
ModernKeePassLib/Collections/ProtectedStringDictionary.cs
Normal file
298
ModernKeePassLib/Collections/ProtectedStringDictionary.cs
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of <c>ProtectedString</c> objects (dictionary).
|
||||
/// </summary>
|
||||
public sealed class ProtectedStringDictionary :
|
||||
IDeepCloneable<ProtectedStringDictionary>,
|
||||
IEnumerable<KeyValuePair<string, ProtectedString>>
|
||||
{
|
||||
private SortedDictionary<string, ProtectedString> m_vStrings =
|
||||
new SortedDictionary<string, ProtectedString>();
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of strings in this entry.
|
||||
/// </summary>
|
||||
public uint UCount
|
||||
{
|
||||
get { return (uint)m_vStrings.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new list of protected strings.
|
||||
/// </summary>
|
||||
public ProtectedStringDictionary()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_vStrings.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, ProtectedString>> GetEnumerator()
|
||||
{
|
||||
return m_vStrings.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_vStrings.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the current <c>ProtectedStringList</c> object, including all
|
||||
/// stored protected strings.
|
||||
/// </summary>
|
||||
/// <returns>New <c>ProtectedStringList</c> object.</returns>
|
||||
public ProtectedStringDictionary CloneDeep()
|
||||
{
|
||||
ProtectedStringDictionary plNew = new ProtectedStringDictionary();
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedString> kvpStr in m_vStrings)
|
||||
{
|
||||
// ProtectedString objects are immutable
|
||||
plNew.Set(kvpStr.Key, kvpStr.Value);
|
||||
}
|
||||
|
||||
return plNew;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public bool EqualsDictionary(ProtectedStringDictionary dict)
|
||||
{
|
||||
return EqualsDictionary(dict, PwCompareOptions.None, MemProtCmpMode.None);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public bool EqualsDictionary(ProtectedStringDictionary dict,
|
||||
MemProtCmpMode mpCompare)
|
||||
{
|
||||
return EqualsDictionary(dict, PwCompareOptions.None, mpCompare);
|
||||
}
|
||||
|
||||
public bool EqualsDictionary(ProtectedStringDictionary dict,
|
||||
PwCompareOptions pwOpt, MemProtCmpMode mpCompare)
|
||||
{
|
||||
if(dict == null) { Debug.Assert(false); return false; }
|
||||
|
||||
bool bNeEqStd = ((pwOpt & PwCompareOptions.NullEmptyEquivStd) !=
|
||||
PwCompareOptions.None);
|
||||
if(!bNeEqStd)
|
||||
{
|
||||
if(m_vStrings.Count != dict.m_vStrings.Count) return false;
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedString> kvp in m_vStrings)
|
||||
{
|
||||
bool bStdField = PwDefs.IsStandardField(kvp.Key);
|
||||
ProtectedString ps = dict.Get(kvp.Key);
|
||||
|
||||
if(bNeEqStd && (ps == null) && bStdField)
|
||||
ps = ProtectedString.Empty;
|
||||
|
||||
if(ps == null) return false;
|
||||
|
||||
if(mpCompare == MemProtCmpMode.Full)
|
||||
{
|
||||
if(ps.IsProtected != kvp.Value.IsProtected) return false;
|
||||
}
|
||||
else if(mpCompare == MemProtCmpMode.CustomOnly)
|
||||
{
|
||||
if(!bStdField && (ps.IsProtected != kvp.Value.IsProtected))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ps.Equals(kvp.Value, false)) return false;
|
||||
}
|
||||
|
||||
if(bNeEqStd)
|
||||
{
|
||||
foreach(KeyValuePair<string, ProtectedString> kvp in dict.m_vStrings)
|
||||
{
|
||||
ProtectedString ps = Get(kvp.Key);
|
||||
|
||||
if(ps != null) continue; // Compared previously
|
||||
if(!PwDefs.IsStandardField(kvp.Key)) return false;
|
||||
if(!kvp.Value.IsEmpty) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get one of the protected strings.
|
||||
/// </summary>
|
||||
/// <param name="strName">String identifier.</param>
|
||||
/// <returns>Protected string. If the string identified by
|
||||
/// <paramref name="strName" /> cannot be found, the function
|
||||
/// returns <c>null</c>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input parameter
|
||||
/// is <c>null</c>.</exception>
|
||||
public ProtectedString Get(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
ProtectedString ps;
|
||||
if(m_vStrings.TryGetValue(strName, out ps)) return ps;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get one of the protected strings. The return value is never <c>null</c>.
|
||||
/// If the requested string cannot be found, an empty protected string
|
||||
/// object is returned.
|
||||
/// </summary>
|
||||
/// <param name="strName">String identifier.</param>
|
||||
/// <returns>Returns a protected string object. If the standard string
|
||||
/// has not been set yet, the return value is an empty string (<c>""</c>).</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public ProtectedString GetSafe(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
ProtectedString ps;
|
||||
if(m_vStrings.TryGetValue(strName, out ps)) return ps;
|
||||
|
||||
return ProtectedString.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if a named string exists.
|
||||
/// </summary>
|
||||
/// <param name="strName">Name of the string to try.</param>
|
||||
/// <returns>Returns <c>true</c> if the string exists, otherwise <c>false</c>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if
|
||||
/// <paramref name="strName" /> is <c>null</c>.</exception>
|
||||
public bool Exists(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
return m_vStrings.ContainsKey(strName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get one of the protected strings. If the string doesn't exist, the
|
||||
/// return value is an empty string (<c>""</c>).
|
||||
/// </summary>
|
||||
/// <param name="strName">Name of the requested string.</param>
|
||||
/// <returns>Requested string value or an empty string, if the named
|
||||
/// string doesn't exist.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public string ReadSafe(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
ProtectedString ps;
|
||||
if(m_vStrings.TryGetValue(strName, out ps))
|
||||
return ps.ReadString();
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get one of the entry strings. If the string doesn't exist, the
|
||||
/// return value is an empty string (<c>""</c>). If the string is
|
||||
/// in-memory protected, the return value is <c>PwDefs.HiddenPassword</c>.
|
||||
/// </summary>
|
||||
/// <param name="strName">Name of the requested string.</param>
|
||||
/// <returns>Returns the requested string in plain-text or
|
||||
/// <c>PwDefs.HiddenPassword</c> if the string cannot be found.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public string ReadSafeEx(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
ProtectedString ps;
|
||||
if(m_vStrings.TryGetValue(strName, out ps))
|
||||
{
|
||||
if(ps.IsProtected) return PwDefs.HiddenPassword;
|
||||
return ps.ReadString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a string.
|
||||
/// </summary>
|
||||
/// <param name="strField">Identifier of the string field to modify.</param>
|
||||
/// <param name="psNewValue">New value. This parameter must not be <c>null</c>.</param>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
|
||||
/// parameters is <c>null</c>.</exception>
|
||||
public void Set(string strField, ProtectedString psNewValue)
|
||||
{
|
||||
Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField");
|
||||
Debug.Assert(psNewValue != null); if(psNewValue == null) throw new ArgumentNullException("psNewValue");
|
||||
|
||||
m_vStrings[strField] = psNewValue;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <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");
|
||||
|
||||
return m_vStrings.Remove(strField);
|
||||
}
|
||||
|
||||
public List<string> GetKeys()
|
||||
{
|
||||
return new List<string>(m_vStrings.Keys);
|
||||
}
|
||||
|
||||
public void EnableProtection(string strField, bool bProtect)
|
||||
{
|
||||
ProtectedString ps = Get(strField);
|
||||
if(ps == null) return; // Nothing to do, no assert
|
||||
|
||||
if(ps.IsProtected != bProtect)
|
||||
Set(strField, ps.WithProtection(bProtect));
|
||||
}
|
||||
}
|
||||
}
|
||||
380
ModernKeePassLib/Collections/PwObjectList.cs
Normal file
380
ModernKeePassLib/Collections/PwObjectList.cs
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// List of objects that implement <c>IDeepCloneable</c>,
|
||||
/// and cannot be <c>null</c>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type specifier.</typeparam>
|
||||
public sealed class PwObjectList<T> : IEnumerable<T>
|
||||
where T : class, IDeepCloneable<T>
|
||||
{
|
||||
private List<T> m_vObjects = new List<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Get number of objects in this list.
|
||||
/// </summary>
|
||||
public uint UCount
|
||||
{
|
||||
get { return (uint)m_vObjects.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new list of objects.
|
||||
/// </summary>
|
||||
public PwObjectList()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_vObjects.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_vObjects.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
// Do not destroy contained objects!
|
||||
m_vObjects.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the current <c>PwObjectList</c>, including all
|
||||
/// stored objects (deep copy).
|
||||
/// </summary>
|
||||
/// <returns>New <c>PwObjectList</c>.</returns>
|
||||
public PwObjectList<T> CloneDeep()
|
||||
{
|
||||
PwObjectList<T> pl = new PwObjectList<T>();
|
||||
|
||||
foreach(T po in m_vObjects)
|
||||
pl.Add(po.CloneDeep());
|
||||
|
||||
return pl;
|
||||
}
|
||||
|
||||
public PwObjectList<T> CloneShallow()
|
||||
{
|
||||
PwObjectList<T> tNew = new PwObjectList<T>();
|
||||
|
||||
foreach(T po in m_vObjects) tNew.Add(po);
|
||||
|
||||
return tNew;
|
||||
}
|
||||
|
||||
public List<T> CloneShallowToList()
|
||||
{
|
||||
PwObjectList<T> tNew = CloneShallow();
|
||||
return tNew.m_vObjects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an object to this list.
|
||||
/// </summary>
|
||||
/// <param name="pwObject">Object to be added.</param>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public void Add(T pwObject)
|
||||
{
|
||||
Debug.Assert(pwObject != null);
|
||||
if(pwObject == null) throw new ArgumentNullException("pwObject");
|
||||
|
||||
m_vObjects.Add(pwObject);
|
||||
}
|
||||
|
||||
public void Add(PwObjectList<T> vObjects)
|
||||
{
|
||||
Debug.Assert(vObjects != null);
|
||||
if(vObjects == null) throw new ArgumentNullException("vObjects");
|
||||
|
||||
foreach(T po in vObjects)
|
||||
{
|
||||
m_vObjects.Add(po);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(List<T> vObjects)
|
||||
{
|
||||
Debug.Assert(vObjects != null);
|
||||
if(vObjects == null) throw new ArgumentNullException("vObjects");
|
||||
|
||||
foreach(T po in vObjects)
|
||||
{
|
||||
m_vObjects.Add(po);
|
||||
}
|
||||
}
|
||||
|
||||
public void Insert(uint uIndex, T pwObject)
|
||||
{
|
||||
Debug.Assert(pwObject != null);
|
||||
if(pwObject == null) throw new ArgumentNullException("pwObject");
|
||||
|
||||
m_vObjects.Insert((int)uIndex, pwObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an object of the list.
|
||||
/// </summary>
|
||||
/// <param name="uIndex">Index of the object to get. Must be valid, otherwise an
|
||||
/// exception is thrown.</param>
|
||||
/// <returns>Reference to an existing <c>T</c> object. Is never <c>null</c>.</returns>
|
||||
public T GetAt(uint uIndex)
|
||||
{
|
||||
Debug.Assert(uIndex < m_vObjects.Count);
|
||||
if(uIndex >= m_vObjects.Count) throw new ArgumentOutOfRangeException("uIndex");
|
||||
|
||||
return m_vObjects[(int)uIndex];
|
||||
}
|
||||
|
||||
public void SetAt(uint uIndex, T pwObject)
|
||||
{
|
||||
Debug.Assert(pwObject != null);
|
||||
if(pwObject == null) throw new ArgumentNullException("pwObject");
|
||||
if(uIndex >= (uint)m_vObjects.Count)
|
||||
throw new ArgumentOutOfRangeException("uIndex");
|
||||
|
||||
m_vObjects[(int)uIndex] = pwObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a range of objects.
|
||||
/// </summary>
|
||||
/// <param name="uStartIndexIncl">Index of the first object to be
|
||||
/// returned (inclusive).</param>
|
||||
/// <param name="uEndIndexIncl">Index of the last object to be
|
||||
/// returned (inclusive).</param>
|
||||
/// <returns></returns>
|
||||
public List<T> GetRange(uint uStartIndexIncl, uint uEndIndexIncl)
|
||||
{
|
||||
if(uStartIndexIncl >= (uint)m_vObjects.Count)
|
||||
throw new ArgumentOutOfRangeException("uStartIndexIncl");
|
||||
if(uEndIndexIncl >= (uint)m_vObjects.Count)
|
||||
throw new ArgumentOutOfRangeException("uEndIndexIncl");
|
||||
if(uStartIndexIncl > uEndIndexIncl)
|
||||
throw new ArgumentException();
|
||||
|
||||
List<T> list = new List<T>((int)(uEndIndexIncl - uStartIndexIncl) + 1);
|
||||
for(uint u = uStartIndexIncl; u <= uEndIndexIncl; ++u)
|
||||
{
|
||||
list.Add(m_vObjects[(int)u]);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public int IndexOf(T pwReference)
|
||||
{
|
||||
Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference");
|
||||
|
||||
return m_vObjects.IndexOf(pwReference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete an object of this list. The object to be deleted is identified
|
||||
/// by a reference handle.
|
||||
/// </summary>
|
||||
/// <param name="pwReference">Reference of the object to be deleted.</param>
|
||||
/// <returns>Returns <c>true</c> if the object was deleted, <c>false</c> if
|
||||
/// the object wasn't found in this list.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public bool Remove(T pwReference)
|
||||
{
|
||||
Debug.Assert(pwReference != null); if(pwReference == null) throw new ArgumentNullException("pwReference");
|
||||
|
||||
return m_vObjects.Remove(pwReference);
|
||||
}
|
||||
|
||||
public void RemoveAt(uint uIndex)
|
||||
{
|
||||
m_vObjects.RemoveAt((int)uIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move an object up or down.
|
||||
/// </summary>
|
||||
/// <param name="tObject">The object to be moved.</param>
|
||||
/// <param name="bUp">Move one up. If <c>false</c>, move one down.</param>
|
||||
public void MoveOne(T tObject, bool bUp)
|
||||
{
|
||||
Debug.Assert(tObject != null);
|
||||
if(tObject == null) throw new ArgumentNullException("tObject");
|
||||
|
||||
int nCount = m_vObjects.Count;
|
||||
if(nCount <= 1) return;
|
||||
|
||||
int nIndex = m_vObjects.IndexOf(tObject);
|
||||
if(nIndex < 0) { Debug.Assert(false); return; }
|
||||
|
||||
if(bUp && (nIndex > 0)) // No assert for top item
|
||||
{
|
||||
T tTemp = m_vObjects[nIndex - 1];
|
||||
m_vObjects[nIndex - 1] = m_vObjects[nIndex];
|
||||
m_vObjects[nIndex] = tTemp;
|
||||
}
|
||||
else if(!bUp && (nIndex != (nCount - 1))) // No assert for bottom item
|
||||
{
|
||||
T tTemp = m_vObjects[nIndex + 1];
|
||||
m_vObjects[nIndex + 1] = m_vObjects[nIndex];
|
||||
m_vObjects[nIndex] = tTemp;
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveOne(T[] vObjects, bool bUp)
|
||||
{
|
||||
Debug.Assert(vObjects != null);
|
||||
if(vObjects == null) throw new ArgumentNullException("vObjects");
|
||||
|
||||
List<int> lIndices = new List<int>();
|
||||
foreach(T t in vObjects)
|
||||
{
|
||||
if(t == null) { Debug.Assert(false); continue; }
|
||||
|
||||
int p = IndexOf(t);
|
||||
if(p >= 0) lIndices.Add(p);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
MoveOne(lIndices.ToArray(), bUp);
|
||||
}
|
||||
|
||||
public void MoveOne(int[] vIndices, bool bUp)
|
||||
{
|
||||
Debug.Assert(vIndices != null);
|
||||
if(vIndices == null) throw new ArgumentNullException("vIndices");
|
||||
|
||||
int n = m_vObjects.Count;
|
||||
if(n <= 1) return; // No moving possible
|
||||
|
||||
int m = vIndices.Length;
|
||||
if(m == 0) return; // Nothing to move
|
||||
|
||||
int[] v = new int[m];
|
||||
Array.Copy(vIndices, v, m);
|
||||
Array.Sort<int>(v);
|
||||
|
||||
if((bUp && (v[0] <= 0)) || (!bUp && (v[m - 1] >= (n - 1))))
|
||||
return; // Moving as a block is not possible
|
||||
|
||||
int iStart = (bUp ? 0 : (m - 1));
|
||||
int iExcl = (bUp ? m : -1);
|
||||
int iStep = (bUp ? 1 : -1);
|
||||
|
||||
for(int i = iStart; i != iExcl; i += iStep)
|
||||
{
|
||||
int p = v[i];
|
||||
if((p < 0) || (p >= n)) { Debug.Assert(false); continue; }
|
||||
|
||||
T t = m_vObjects[p];
|
||||
|
||||
if(bUp)
|
||||
{
|
||||
Debug.Assert(p > 0);
|
||||
m_vObjects.RemoveAt(p);
|
||||
m_vObjects.Insert(p - 1, t);
|
||||
}
|
||||
else // Down
|
||||
{
|
||||
Debug.Assert(p < (n - 1));
|
||||
m_vObjects.RemoveAt(p);
|
||||
m_vObjects.Insert(p + 1, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move some of the objects in this list to the top/bottom.
|
||||
/// </summary>
|
||||
/// <param name="vObjects">List of objects to be moved.</param>
|
||||
/// <param name="bTop">Move to top. If <c>false</c>, move to bottom.</param>
|
||||
public void MoveTopBottom(T[] vObjects, bool bTop)
|
||||
{
|
||||
Debug.Assert(vObjects != null);
|
||||
if(vObjects == null) throw new ArgumentNullException("vObjects");
|
||||
|
||||
if(vObjects.Length == 0) return;
|
||||
|
||||
int nCount = m_vObjects.Count;
|
||||
foreach(T t in vObjects) m_vObjects.Remove(t);
|
||||
|
||||
if(bTop)
|
||||
{
|
||||
int nPos = 0;
|
||||
foreach(T t in vObjects)
|
||||
{
|
||||
m_vObjects.Insert(nPos, t);
|
||||
++nPos;
|
||||
}
|
||||
}
|
||||
else // Move to bottom
|
||||
{
|
||||
foreach(T t in vObjects) m_vObjects.Add(t);
|
||||
}
|
||||
|
||||
Debug.Assert(nCount == m_vObjects.Count);
|
||||
if(nCount != m_vObjects.Count)
|
||||
throw new ArgumentException("At least one of the T objects in the vObjects list doesn't exist!");
|
||||
}
|
||||
|
||||
public void Sort(IComparer<T> tComparer)
|
||||
{
|
||||
if(tComparer == null) throw new ArgumentNullException("tComparer");
|
||||
|
||||
m_vObjects.Sort(tComparer);
|
||||
}
|
||||
|
||||
public void Sort(Comparison<T> tComparison)
|
||||
{
|
||||
if(tComparison == null) throw new ArgumentNullException("tComparison");
|
||||
|
||||
m_vObjects.Sort(tComparison);
|
||||
}
|
||||
|
||||
public static PwObjectList<T> FromArray(T[] tArray)
|
||||
{
|
||||
if(tArray == null) throw new ArgumentNullException("tArray");
|
||||
|
||||
PwObjectList<T> l = new PwObjectList<T>();
|
||||
foreach(T t in tArray) { l.Add(t); }
|
||||
return l;
|
||||
}
|
||||
|
||||
public static PwObjectList<T> FromList(List<T> tList)
|
||||
{
|
||||
if(tList == null) throw new ArgumentNullException("tList");
|
||||
|
||||
PwObjectList<T> l = new PwObjectList<T>();
|
||||
l.Add(tList);
|
||||
return l;
|
||||
}
|
||||
}
|
||||
}
|
||||
232
ModernKeePassLib/Collections/PwObjectPool.cs
Normal file
232
ModernKeePassLib/Collections/PwObjectPool.cs
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
public sealed class PwObjectPool
|
||||
{
|
||||
private SortedDictionary<PwUuid, IStructureItem> m_dict =
|
||||
new SortedDictionary<PwUuid, IStructureItem>();
|
||||
|
||||
public static PwObjectPool FromGroupRecursive(PwGroup pgRoot, bool bEntries)
|
||||
{
|
||||
if(pgRoot == null) throw new ArgumentNullException("pgRoot");
|
||||
|
||||
PwObjectPool p = new PwObjectPool();
|
||||
|
||||
if(!bEntries) p.m_dict[pgRoot.Uuid] = pgRoot;
|
||||
GroupHandler gh = delegate(PwGroup pg)
|
||||
{
|
||||
p.m_dict[pg.Uuid] = pg;
|
||||
return true;
|
||||
};
|
||||
|
||||
EntryHandler eh = delegate(PwEntry pe)
|
||||
{
|
||||
p.m_dict[pe.Uuid] = pe;
|
||||
return true;
|
||||
};
|
||||
|
||||
pgRoot.TraverseTree(TraversalMethod.PreOrder, bEntries ? null : gh,
|
||||
bEntries ? eh : null);
|
||||
return p;
|
||||
}
|
||||
|
||||
public IStructureItem Get(PwUuid pwUuid)
|
||||
{
|
||||
IStructureItem pItem;
|
||||
m_dict.TryGetValue(pwUuid, out pItem);
|
||||
return pItem;
|
||||
}
|
||||
|
||||
public bool ContainsOnlyType(Type t)
|
||||
{
|
||||
foreach(KeyValuePair<PwUuid, IStructureItem> kvp in m_dict)
|
||||
{
|
||||
if(kvp.Value.GetType() != t) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PwObjectPoolEx
|
||||
{
|
||||
private Dictionary<PwUuid, ulong> m_dUuidToId =
|
||||
new Dictionary<PwUuid, ulong>();
|
||||
private Dictionary<ulong, IStructureItem> m_dIdToItem =
|
||||
new Dictionary<ulong, IStructureItem>();
|
||||
|
||||
private PwObjectPoolEx()
|
||||
{
|
||||
}
|
||||
|
||||
public static PwObjectPoolEx FromGroup(PwGroup pg)
|
||||
{
|
||||
PwObjectPoolEx p = new PwObjectPoolEx();
|
||||
|
||||
if(pg == null) { Debug.Assert(false); return p; }
|
||||
|
||||
ulong uFreeId = 2; // 0 = "not found", 1 is a hole
|
||||
|
||||
p.m_dUuidToId[pg.Uuid] = uFreeId;
|
||||
p.m_dIdToItem[uFreeId] = pg;
|
||||
uFreeId += 2; // Make hole
|
||||
|
||||
p.AddGroupRec(pg, ref uFreeId);
|
||||
return p;
|
||||
}
|
||||
|
||||
private void AddGroupRec(PwGroup pg, ref ulong uFreeId)
|
||||
{
|
||||
if(pg == null) { Debug.Assert(false); return; }
|
||||
|
||||
ulong uId = uFreeId;
|
||||
|
||||
// Consecutive entries must have consecutive IDs
|
||||
foreach(PwEntry pe in pg.Entries)
|
||||
{
|
||||
Debug.Assert(!m_dUuidToId.ContainsKey(pe.Uuid));
|
||||
Debug.Assert(!m_dIdToItem.ContainsValue(pe));
|
||||
|
||||
m_dUuidToId[pe.Uuid] = uId;
|
||||
m_dIdToItem[uId] = pe;
|
||||
++uId;
|
||||
}
|
||||
++uId; // Make hole
|
||||
|
||||
// Consecutive groups must have consecutive IDs
|
||||
foreach(PwGroup pgSub in pg.Groups)
|
||||
{
|
||||
Debug.Assert(!m_dUuidToId.ContainsKey(pgSub.Uuid));
|
||||
Debug.Assert(!m_dIdToItem.ContainsValue(pgSub));
|
||||
|
||||
m_dUuidToId[pgSub.Uuid] = uId;
|
||||
m_dIdToItem[uId] = pgSub;
|
||||
++uId;
|
||||
}
|
||||
++uId; // Make hole
|
||||
|
||||
foreach(PwGroup pgSub in pg.Groups)
|
||||
{
|
||||
AddGroupRec(pgSub, ref uId);
|
||||
}
|
||||
|
||||
uFreeId = uId;
|
||||
}
|
||||
|
||||
public ulong GetIdByUuid(PwUuid pwUuid)
|
||||
{
|
||||
if(pwUuid == null) { Debug.Assert(false); return 0; }
|
||||
|
||||
ulong uId;
|
||||
m_dUuidToId.TryGetValue(pwUuid, out uId);
|
||||
return uId;
|
||||
}
|
||||
|
||||
public IStructureItem GetItemByUuid(PwUuid pwUuid)
|
||||
{
|
||||
if(pwUuid == null) { Debug.Assert(false); return null; }
|
||||
|
||||
ulong uId;
|
||||
if(!m_dUuidToId.TryGetValue(pwUuid, out uId)) return null;
|
||||
Debug.Assert(uId != 0);
|
||||
|
||||
return GetItemById(uId);
|
||||
}
|
||||
|
||||
public IStructureItem GetItemById(ulong uId)
|
||||
{
|
||||
IStructureItem p;
|
||||
m_dIdToItem.TryGetValue(uId, out p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PwObjectBlock<T> : IEnumerable<T>
|
||||
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
|
||||
{
|
||||
private List<T> m_l = new List<T>();
|
||||
|
||||
public T PrimaryItem
|
||||
{
|
||||
get { return ((m_l.Count > 0) ? m_l[0] : null); }
|
||||
}
|
||||
|
||||
private DateTime m_dtLocationChanged = TimeUtil.SafeMinValueUtc;
|
||||
public DateTime LocationChanged
|
||||
{
|
||||
get { return m_dtLocationChanged; }
|
||||
}
|
||||
|
||||
private PwObjectPoolEx m_poolAssoc = null;
|
||||
public PwObjectPoolEx PoolAssoc
|
||||
{
|
||||
get { return m_poolAssoc; }
|
||||
}
|
||||
|
||||
public PwObjectBlock()
|
||||
{
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString()
|
||||
{
|
||||
return ("PwObjectBlock, Count = " + m_l.Count.ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_l.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_l.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(T t, DateTime dtLoc, PwObjectPoolEx pool)
|
||||
{
|
||||
if(t == null) { Debug.Assert(false); return; }
|
||||
|
||||
m_l.Add(t);
|
||||
|
||||
if(dtLoc > m_dtLocationChanged)
|
||||
{
|
||||
m_dtLocationChanged = dtLoc;
|
||||
m_poolAssoc = pool;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
130
ModernKeePassLib/Collections/StringDictionaryEx.cs
Normal file
130
ModernKeePassLib/Collections/StringDictionaryEx.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
|
||||
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
|
||||
{
|
||||
private SortedDictionary<string, string> m_dict =
|
||||
new SortedDictionary<string, string>();
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_dict.Count; }
|
||||
}
|
||||
|
||||
public StringDictionaryEx()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_dict.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return m_dict.GetEnumerator();
|
||||
}
|
||||
|
||||
public StringDictionaryEx CloneDeep()
|
||||
{
|
||||
StringDictionaryEx sdNew = new StringDictionaryEx();
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in m_dict)
|
||||
sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
|
||||
|
||||
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)
|
||||
{
|
||||
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
|
||||
|
||||
string s;
|
||||
if(m_dict.TryGetValue(strName, out s)) return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool Exists(string strName)
|
||||
{
|
||||
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
|
||||
|
||||
return m_dict.ContainsKey(strName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a string.
|
||||
/// </summary>
|
||||
/// <param name="strField">Identifier of the string field to modify.</param>
|
||||
/// <param name="strNewValue">New value. This parameter must not be <c>null</c>.</param>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
|
||||
/// parameters is <c>null</c>.</exception>
|
||||
public void Set(string strField, string strNewValue)
|
||||
{
|
||||
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
|
||||
if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("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>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown if the input
|
||||
/// parameter is <c>null</c>.</exception>
|
||||
public bool Remove(string strField)
|
||||
{
|
||||
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
|
||||
|
||||
return m_dict.Remove(strField);
|
||||
}
|
||||
}
|
||||
}
|
||||
415
ModernKeePassLib/Collections/VariantDictionary.cs
Normal file
415
ModernKeePassLib/Collections/VariantDictionary.cs
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Collections
|
||||
{
|
||||
public class VariantDictionary
|
||||
{
|
||||
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, 0, pbName.Length);
|
||||
|
||||
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, 0, pbValue.Length));
|
||||
break;
|
||||
|
||||
case (byte)VdType.ByteArray:
|
||||
d.SetByteArray(strName, pbValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.Assert(false); // Unknown type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(ms.ReadByte() < 0);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
254
ModernKeePassLib/Cryptography/Cipher/ChaCha20Cipher.cs
Normal file
254
ModernKeePassLib/Cryptography/Cipher/ChaCha20Cipher.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.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)
|
||||
{
|
||||
if(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
176
ModernKeePassLib/Cryptography/Cipher/ChaCha20Engine.cs
Normal file
176
ModernKeePassLib/Cryptography/Cipher/ChaCha20Engine.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Resources;
|
||||
|
||||
namespace ModernKeePassLib.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 s, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return new ChaCha20Stream(s, true, pbKey, pbIV);
|
||||
}
|
||||
|
||||
public Stream DecryptStream(Stream s, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return new ChaCha20Stream(s, false, pbKey, pbIV);
|
||||
}
|
||||
}
|
||||
|
||||
public 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)
|
||||
{
|
||||
if(m_sBase != null)
|
||||
{
|
||||
m_c.Dispose();
|
||||
m_c = null;
|
||||
|
||||
m_sBase.Dispose();
|
||||
m_sBase = null;
|
||||
}
|
||||
|
||||
m_pbBuffer = null;
|
||||
}
|
||||
|
||||
base.Dispose(bDisposing);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
165
ModernKeePassLib/Cryptography/Cipher/CipherPool.cs
Normal file
165
ModernKeePassLib/Cryptography/Cipher/CipherPool.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
/// <summary>
|
||||
/// Pool of encryption/decryption algorithms (ciphers).
|
||||
/// </summary>
|
||||
public sealed class CipherPool
|
||||
{
|
||||
private List<ICipherEngine> m_lCiphers = new List<ICipherEngine>();
|
||||
|
||||
private static CipherPool m_poolGlobal = null;
|
||||
public static CipherPool GlobalPool
|
||||
{
|
||||
get
|
||||
{
|
||||
CipherPool cp = m_poolGlobal;
|
||||
if(cp == null)
|
||||
{
|
||||
cp = new CipherPool();
|
||||
cp.AddCipher(new StandardAesEngine());
|
||||
cp.AddCipher(new ChaCha20Engine());
|
||||
|
||||
m_poolGlobal = cp;
|
||||
}
|
||||
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all cipher engines from the current pool.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_lCiphers.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a cipher engine to the pool.
|
||||
/// </summary>
|
||||
/// <param name="c">Cipher engine to add. Must not be <c>null</c>.</param>
|
||||
public void AddCipher(ICipherEngine c)
|
||||
{
|
||||
if(c == null) { Debug.Assert(false); throw new ArgumentNullException("c"); }
|
||||
|
||||
// Return if a cipher with that ID is registered already
|
||||
foreach(ICipherEngine cEx in m_lCiphers)
|
||||
{
|
||||
if(cEx.CipherUuid.Equals(c.CipherUuid))
|
||||
return;
|
||||
}
|
||||
|
||||
m_lCiphers.Add(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a cipher identified by its UUID.
|
||||
/// </summary>
|
||||
/// <param name="uuidCipher">UUID of the cipher to return.</param>
|
||||
/// <returns>Reference to the requested cipher. If the cipher is
|
||||
/// not found, <c>null</c> is returned.</returns>
|
||||
public ICipherEngine GetCipher(PwUuid uuidCipher)
|
||||
{
|
||||
foreach(ICipherEngine c in m_lCiphers)
|
||||
{
|
||||
if(c.CipherUuid.Equals(uuidCipher))
|
||||
return c;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of a cipher. This index is temporary and should
|
||||
/// not be stored or used to identify a cipher.
|
||||
/// </summary>
|
||||
/// <param name="uuidCipher">UUID of the cipher.</param>
|
||||
/// <returns>Index of the requested cipher. Returns <c>-1</c> if
|
||||
/// the specified cipher is not found.</returns>
|
||||
public int GetCipherIndex(PwUuid uuidCipher)
|
||||
{
|
||||
for(int i = 0; i < m_lCiphers.Count; ++i)
|
||||
{
|
||||
if(m_lCiphers[i].CipherUuid.Equals(uuidCipher))
|
||||
return i;
|
||||
}
|
||||
|
||||
Debug.Assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of a cipher. This index is temporary and should
|
||||
/// not be stored or used to identify a cipher.
|
||||
/// </summary>
|
||||
/// <param name="strDisplayName">Name of the cipher. Note that
|
||||
/// multiple ciphers can have the same name. In this case, the
|
||||
/// first matching cipher is returned.</param>
|
||||
/// <returns>Cipher with the specified name or <c>-1</c> if
|
||||
/// no cipher with that name is found.</returns>
|
||||
public int GetCipherIndex(string strDisplayName)
|
||||
{
|
||||
for(int i = 0; i < m_lCiphers.Count; ++i)
|
||||
{
|
||||
if(m_lCiphers[i].DisplayName == strDisplayName)
|
||||
return i;
|
||||
}
|
||||
|
||||
Debug.Assert(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of cipher engines in this pool.
|
||||
/// </summary>
|
||||
public int EngineCount
|
||||
{
|
||||
get { return m_lCiphers.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the cipher engine at the specified position. Throws
|
||||
/// an exception if the index is invalid. You can use this
|
||||
/// to iterate over all ciphers, but do not use it to
|
||||
/// identify ciphers.
|
||||
/// </summary>
|
||||
/// <param name="nIndex">Index of the requested cipher engine.</param>
|
||||
/// <returns>Reference to the cipher engine at the specified
|
||||
/// position.</returns>
|
||||
public ICipherEngine this[int nIndex]
|
||||
{
|
||||
get
|
||||
{
|
||||
if((nIndex < 0) || (nIndex >= m_lCiphers.Count))
|
||||
throw new ArgumentOutOfRangeException("nIndex");
|
||||
|
||||
return m_lCiphers[nIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
ModernKeePassLib/Cryptography/Cipher/CtrBlockCipher.cs
Normal file
109
ModernKeePassLib/Cryptography/Cipher/CtrBlockCipher.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public abstract class CtrBlockCipher : IDisposable
|
||||
{
|
||||
private bool m_bDisposed = false;
|
||||
|
||||
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)
|
||||
{
|
||||
if(bDisposing)
|
||||
{
|
||||
MemUtil.ZeroByteArray(m_pBlock);
|
||||
m_iBlockPos = m_pBlock.Length;
|
||||
|
||||
m_bDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
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_bDisposed) throw new ObjectDisposedException(null);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
ModernKeePassLib/Cryptography/Cipher/ICipherEngine.cs
Normal file
69
ModernKeePassLib/Cryptography/Cipher/ICipherEngine.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.IO;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public interface ICipherEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// UUID of the engine. If you want to write an engine/plugin,
|
||||
/// please contact the KeePass team to obtain a new UUID.
|
||||
/// </summary>
|
||||
PwUuid CipherUuid
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name displayed in the list of available encryption/decryption
|
||||
/// engines in the GUI.
|
||||
/// </summary>
|
||||
string DisplayName
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
Stream EncryptStream(Stream s, byte[] pbKey, byte[] pbIV);
|
||||
Stream DecryptStream(Stream s, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
165
ModernKeePassLib/Cryptography/Cipher/Salsa20Cipher.cs
Normal file
165
ModernKeePassLib/Cryptography/Cipher/Salsa20Cipher.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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
|
||||
*/
|
||||
|
||||
// Implementation of the Salsa20 cipher, based on the eSTREAM
|
||||
// submission by D. J. Bernstein.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public sealed class Salsa20Cipher : CtrBlockCipher
|
||||
{
|
||||
private uint[] m_s = new uint[16]; // State
|
||||
private uint[] m_x = new uint[16]; // Working buffer
|
||||
|
||||
private static readonly uint[] g_sigma = new uint[4] {
|
||||
0x61707865, 0x3320646E, 0x79622D32, 0x6B206574
|
||||
};
|
||||
|
||||
public override int BlockSize
|
||||
{
|
||||
get { return 64; }
|
||||
}
|
||||
|
||||
public Salsa20Cipher(byte[] pbKey32, byte[] pbIV8) : base()
|
||||
{
|
||||
if(pbKey32 == null) throw new ArgumentNullException("pbKey32");
|
||||
if(pbKey32.Length != 32) throw new ArgumentOutOfRangeException("pbKey32");
|
||||
if(pbIV8 == null) throw new ArgumentNullException("pbIV8");
|
||||
if(pbIV8.Length != 8) throw new ArgumentOutOfRangeException("pbIV8");
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
protected override void Dispose(bool bDisposing)
|
||||
{
|
||||
if(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)
|
||||
{
|
||||
x[ 4] ^= MemUtil.RotateLeft32(x[ 0] + x[12], 7);
|
||||
x[ 8] ^= MemUtil.RotateLeft32(x[ 4] + x[ 0], 9);
|
||||
x[12] ^= MemUtil.RotateLeft32(x[ 8] + x[ 4], 13);
|
||||
x[ 0] ^= MemUtil.RotateLeft32(x[12] + x[ 8], 18);
|
||||
|
||||
x[ 9] ^= MemUtil.RotateLeft32(x[ 5] + x[ 1], 7);
|
||||
x[13] ^= MemUtil.RotateLeft32(x[ 9] + x[ 5], 9);
|
||||
x[ 1] ^= MemUtil.RotateLeft32(x[13] + x[ 9], 13);
|
||||
x[ 5] ^= MemUtil.RotateLeft32(x[ 1] + x[13], 18);
|
||||
|
||||
x[14] ^= MemUtil.RotateLeft32(x[10] + x[ 6], 7);
|
||||
x[ 2] ^= MemUtil.RotateLeft32(x[14] + x[10], 9);
|
||||
x[ 6] ^= MemUtil.RotateLeft32(x[ 2] + x[14], 13);
|
||||
x[10] ^= MemUtil.RotateLeft32(x[ 6] + x[ 2], 18);
|
||||
|
||||
x[ 3] ^= MemUtil.RotateLeft32(x[15] + x[11], 7);
|
||||
x[ 7] ^= MemUtil.RotateLeft32(x[ 3] + x[15], 9);
|
||||
x[11] ^= MemUtil.RotateLeft32(x[ 7] + x[ 3], 13);
|
||||
x[15] ^= MemUtil.RotateLeft32(x[11] + x[ 7], 18);
|
||||
|
||||
x[ 1] ^= MemUtil.RotateLeft32(x[ 0] + x[ 3], 7);
|
||||
x[ 2] ^= MemUtil.RotateLeft32(x[ 1] + x[ 0], 9);
|
||||
x[ 3] ^= MemUtil.RotateLeft32(x[ 2] + x[ 1], 13);
|
||||
x[ 0] ^= MemUtil.RotateLeft32(x[ 3] + x[ 2], 18);
|
||||
|
||||
x[ 6] ^= MemUtil.RotateLeft32(x[ 5] + x[ 4], 7);
|
||||
x[ 7] ^= MemUtil.RotateLeft32(x[ 6] + x[ 5], 9);
|
||||
x[ 4] ^= MemUtil.RotateLeft32(x[ 7] + x[ 6], 13);
|
||||
x[ 5] ^= MemUtil.RotateLeft32(x[ 4] + x[ 7], 18);
|
||||
|
||||
x[11] ^= MemUtil.RotateLeft32(x[10] + x[ 9], 7);
|
||||
x[ 8] ^= MemUtil.RotateLeft32(x[11] + x[10], 9);
|
||||
x[ 9] ^= MemUtil.RotateLeft32(x[ 8] + x[11], 13);
|
||||
x[10] ^= MemUtil.RotateLeft32(x[ 9] + x[ 8], 18);
|
||||
|
||||
x[12] ^= MemUtil.RotateLeft32(x[15] + x[14], 7);
|
||||
x[13] ^= MemUtil.RotateLeft32(x[12] + x[15], 9);
|
||||
x[14] ^= MemUtil.RotateLeft32(x[13] + x[12], 13);
|
||||
x[15] ^= MemUtil.RotateLeft32(x[14] + x[13], 18);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 16; ++i) x[i] += s[i];
|
||||
|
||||
for(int i = 0; i < 16; ++i)
|
||||
{
|
||||
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[8];
|
||||
if(s[8] == 0) ++s[9];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
ModernKeePassLib/Cryptography/Cipher/StandardAesEngine.cs
Normal file
133
ModernKeePassLib/Cryptography/Cipher/StandardAesEngine.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Security;
|
||||
using System.Text;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Resources;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public sealed class StandardAesEngine : ICipherEngine
|
||||
{
|
||||
#if !KeePassUAP
|
||||
private const CipherMode SaeCipherMode = CipherMode.CBC;
|
||||
private const PaddingMode SaePaddingMode = PaddingMode.PKCS7;
|
||||
#endif
|
||||
|
||||
private static PwUuid g_uuidAes = null;
|
||||
public static PwUuid AesUuid
|
||||
{
|
||||
get
|
||||
{
|
||||
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 pu;
|
||||
}
|
||||
}
|
||||
|
||||
public PwUuid CipherUuid
|
||||
{
|
||||
get { return StandardAesEngine.AesUuid; }
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
return ("AES/Rijndael (" + KLRes.KeyBits.Replace(@"{PARAM}",
|
||||
"256") + ", FIPS 197)");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateArguments(Stream s, bool bEncrypt, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
|
||||
if(pbKey.Length != 32) { Debug.Assert(false); throw new ArgumentOutOfRangeException("pbKey"); }
|
||||
|
||||
if(pbIV == null) { Debug.Assert(false); throw new ArgumentNullException("pbIV"); }
|
||||
if(pbIV.Length != 16) { Debug.Assert(false); throw new ArgumentOutOfRangeException("pbIV"); }
|
||||
|
||||
if(bEncrypt)
|
||||
{
|
||||
Debug.Assert(s.CanWrite);
|
||||
if(!s.CanWrite) throw new ArgumentException("Stream must be writable!");
|
||||
}
|
||||
else // Decrypt
|
||||
{
|
||||
Debug.Assert(s.CanRead);
|
||||
if(!s.CanRead) throw new ArgumentException("Stream must be readable!");
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream CreateStream(Stream s, bool bEncrypt, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
StandardAesEngine.ValidateArguments(s, bEncrypt, pbKey, pbIV);
|
||||
|
||||
#if KeePassUAP
|
||||
return StandardAesEngineExt.CreateStream(s, bEncrypt, pbKey, pbIV);
|
||||
#else
|
||||
SymmetricAlgorithm a = CryptoUtil.CreateAes();
|
||||
if(a.BlockSize != 128) // AES block size
|
||||
{
|
||||
Debug.Assert(false);
|
||||
a.BlockSize = 128;
|
||||
}
|
||||
a.KeySize = 256;
|
||||
a.Mode = SaeCipherMode;
|
||||
a.Padding = SaePaddingMode;
|
||||
|
||||
ICryptoTransform t;
|
||||
if(bEncrypt) t = a.CreateEncryptor(pbKey, pbIV);
|
||||
else t = a.CreateDecryptor(pbKey, pbIV);
|
||||
if(t == null) { Debug.Assert(false); throw new SecurityException("Unable to create AES transform!"); }
|
||||
|
||||
return new CryptoStreamEx(s, t, bEncrypt ? CryptoStreamMode.Write :
|
||||
CryptoStreamMode.Read, a);
|
||||
#endif
|
||||
}
|
||||
|
||||
public Stream EncryptStream(Stream s, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return StandardAesEngine.CreateStream(s, true, pbKey, pbIV);
|
||||
}
|
||||
|
||||
public Stream DecryptStream(Stream s, byte[] pbKey, byte[] pbIV)
|
||||
{
|
||||
return StandardAesEngine.CreateStream(s, false, pbKey, pbIV);
|
||||
}
|
||||
}
|
||||
}
|
||||
387
ModernKeePassLib/Cryptography/CryptoRandom.cs
Normal file
387
ModernKeePassLib/Cryptography/CryptoRandom.cs
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
#if !ModernKeePassLib
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Cryptographically secure pseudo-random number generator.
|
||||
/// The returned values are unpredictable and cannot be reproduced.
|
||||
/// <c>CryptoRandom</c> is a singleton class.
|
||||
/// </summary>
|
||||
public sealed class CryptoRandom
|
||||
{
|
||||
private ProtectedBinary m_pbEntropyPool = new ProtectedBinary(
|
||||
true, new byte[64]);
|
||||
private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
|
||||
private ulong m_uCounter;
|
||||
private ulong m_uGeneratedBytesCount = 0;
|
||||
|
||||
private static readonly object g_oSyncRoot = new object();
|
||||
private readonly object m_oSyncRoot = new object();
|
||||
|
||||
private static CryptoRandom g_pInstance = null;
|
||||
public static CryptoRandom Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
CryptoRandom cr;
|
||||
lock(g_oSyncRoot)
|
||||
{
|
||||
cr = g_pInstance;
|
||||
if(cr == null)
|
||||
{
|
||||
cr = new CryptoRandom();
|
||||
g_pInstance = cr;
|
||||
}
|
||||
}
|
||||
|
||||
return cr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of random bytes that this instance generated so far.
|
||||
/// Note that this number can be higher than the number of random bytes
|
||||
/// actually requested using the <c>GetRandomBytes</c> method.
|
||||
/// </summary>
|
||||
public ulong GeneratedBytesCount
|
||||
{
|
||||
get
|
||||
{
|
||||
ulong u;
|
||||
lock(m_oSyncRoot) { u = m_uGeneratedBytesCount; }
|
||||
return u;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered whenever the internal <c>GenerateRandom256</c>
|
||||
/// method is called to generate random bytes.
|
||||
/// </summary>
|
||||
public event EventHandler GenerateRandom256Pre;
|
||||
|
||||
private CryptoRandom()
|
||||
{
|
||||
m_uCounter = (ulong)DateTime.UtcNow.ToBinary();
|
||||
|
||||
byte[] pb = GetSystemEntropy();
|
||||
AddEntropy(pb);
|
||||
MemUtil.ZeroByteArray(pb);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal seed of the random number generator based
|
||||
/// on entropy data.
|
||||
/// This method is thread-safe.
|
||||
/// </summary>
|
||||
/// <param name="pbEntropy">Entropy bytes.</param>
|
||||
public void AddEntropy(byte[] pbEntropy)
|
||||
{
|
||||
if(pbEntropy == null) { Debug.Assert(false); return; }
|
||||
if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
|
||||
|
||||
byte[] pbNewData = pbEntropy;
|
||||
if(pbEntropy.Length > 64)
|
||||
{
|
||||
#if KeePassLibSD
|
||||
using(SHA256Managed shaNew = new SHA256Managed())
|
||||
#else
|
||||
using(SHA512Managed shaNew = new SHA512Managed())
|
||||
#endif
|
||||
{
|
||||
pbNewData = shaNew.ComputeHash(pbEntropy);
|
||||
}
|
||||
}
|
||||
|
||||
lock(m_oSyncRoot)
|
||||
{
|
||||
byte[] pbPool = m_pbEntropyPool.ReadData();
|
||||
int cbPool = pbPool.Length;
|
||||
int cbNew = pbNewData.Length;
|
||||
|
||||
byte[] pbCmp = new byte[cbPool + cbNew];
|
||||
Array.Copy(pbPool, pbCmp, cbPool);
|
||||
Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew);
|
||||
|
||||
#if KeePassLibSD
|
||||
using(SHA256Managed shaPool = new SHA256Managed())
|
||||
#else
|
||||
using(SHA512Managed shaPool = new SHA512Managed())
|
||||
#endif
|
||||
{
|
||||
byte[] pbNewPool = shaPool.ComputeHash(pbCmp);
|
||||
m_pbEntropyPool = new ProtectedBinary(true, pbNewPool);
|
||||
MemUtil.ZeroByteArray(pbNewPool);
|
||||
}
|
||||
|
||||
MemUtil.ZeroByteArray(pbCmp);
|
||||
MemUtil.ZeroByteArray(pbPool);
|
||||
}
|
||||
|
||||
if(pbNewData != pbEntropy) MemUtil.ZeroByteArray(pbNewData);
|
||||
}
|
||||
|
||||
private byte[] GetSystemEntropy()
|
||||
{
|
||||
SHA512Managed h = new SHA512Managed();
|
||||
byte[] pb4 = new byte[4];
|
||||
byte[] pb8 = new byte[8];
|
||||
|
||||
GAction<byte[], bool> f = delegate(byte[] pbValue, bool bClearValue)
|
||||
{
|
||||
if(pbValue == null) { Debug.Assert(false); return; }
|
||||
if(pbValue.Length == 0) return;
|
||||
h.TransformBlock(pbValue, 0, pbValue.Length, pbValue, 0);
|
||||
if(bClearValue) MemUtil.ZeroByteArray(pbValue);
|
||||
};
|
||||
Action<int> fI32 = delegate(int iValue)
|
||||
{
|
||||
MemUtil.Int32ToBytesEx(iValue, pb4, 0);
|
||||
f(pb4, false);
|
||||
};
|
||||
Action<long> fI64 = delegate(long lValue)
|
||||
{
|
||||
MemUtil.Int64ToBytesEx(lValue, pb8, 0);
|
||||
f(pb8, false);
|
||||
};
|
||||
Action<string> fStr = delegate(string strValue)
|
||||
{
|
||||
if(strValue == null) { Debug.Assert(false); return; }
|
||||
if(strValue.Length == 0) return;
|
||||
f(StrUtil.Utf8.GetBytes(strValue), false);
|
||||
};
|
||||
|
||||
fI32(Environment.TickCount);
|
||||
fI64(DateTime.UtcNow.ToBinary());
|
||||
|
||||
#if !KeePassLibSD && !ModernKeePassLib
|
||||
// In try-catch for systems without GUI;
|
||||
// https://sourceforge.net/p/keepass/discussion/329221/thread/20335b73/
|
||||
try
|
||||
{
|
||||
Point pt = Cursor.Position;
|
||||
fI32(pt.X);
|
||||
fI32(pt.Y);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
fI32((int)NativeLib.GetPlatformID());
|
||||
#if KeePassUAP
|
||||
fStr(EnvironmentExt.OSVersion.VersionString);
|
||||
#else
|
||||
fStr(Environment.OSVersion.VersionString);
|
||||
#endif
|
||||
|
||||
fI32(Environment.ProcessorCount);
|
||||
|
||||
#if !KeePassUAP
|
||||
fStr(Environment.CommandLine);
|
||||
fI64(Environment.WorkingSet);
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try
|
||||
{
|
||||
foreach(DictionaryEntry de in Environment.GetEnvironmentVariables())
|
||||
{
|
||||
fStr(de.Key as string);
|
||||
fStr(de.Value as string);
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try
|
||||
{
|
||||
#if KeePassUAP
|
||||
f(DiagnosticsExt.GetProcessEntropy(), true);
|
||||
#elif !KeePassLibSD
|
||||
using(Process p = Process.GetCurrentProcess())
|
||||
{
|
||||
fI64(p.Handle.ToInt64());
|
||||
fI32(p.HandleCount);
|
||||
fI32(p.Id);
|
||||
fI64(p.NonpagedSystemMemorySize64);
|
||||
fI64(p.PagedMemorySize64);
|
||||
fI64(p.PagedSystemMemorySize64);
|
||||
fI64(p.PeakPagedMemorySize64);
|
||||
fI64(p.PeakVirtualMemorySize64);
|
||||
fI64(p.PeakWorkingSet64);
|
||||
fI64(p.PrivateMemorySize64);
|
||||
fI64(p.StartTime.ToBinary());
|
||||
fI64(p.VirtualMemorySize64);
|
||||
fI64(p.WorkingSet64);
|
||||
|
||||
// Not supported in Mono 1.2.6:
|
||||
// fI32(p.SessionId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
|
||||
|
||||
try
|
||||
{
|
||||
CultureInfo ci = CultureInfo.CurrentCulture;
|
||||
if(ci != null) fI32(ci.GetHashCode());
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
f(Guid.NewGuid().ToByteArray(), false);
|
||||
f(GetCspRandom(), true);
|
||||
|
||||
h.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
|
||||
byte[] pbHash = h.Hash;
|
||||
h.Clear();
|
||||
MemUtil.ZeroByteArray(pb4);
|
||||
MemUtil.ZeroByteArray(pb8);
|
||||
return pbHash;
|
||||
}
|
||||
|
||||
private byte[] GetCspRandom()
|
||||
{
|
||||
byte[] pb = new byte[32];
|
||||
|
||||
try { m_rng.GetBytes(pb); }
|
||||
catch(Exception)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
MemUtil.Int64ToBytesEx(DateTime.UtcNow.ToBinary(), pb, 0);
|
||||
}
|
||||
|
||||
return pb;
|
||||
}
|
||||
|
||||
private byte[] GenerateRandom256()
|
||||
{
|
||||
if(this.GenerateRandom256Pre != null)
|
||||
this.GenerateRandom256Pre(this, EventArgs.Empty);
|
||||
|
||||
byte[] pbCmp;
|
||||
lock(m_oSyncRoot)
|
||||
{
|
||||
m_uCounter += 0x74D8B29E4D38E161UL; // Prime number
|
||||
byte[] pbCounter = MemUtil.UInt64ToBytes(m_uCounter);
|
||||
|
||||
byte[] pbCsp = GetCspRandom();
|
||||
|
||||
byte[] pbPool = m_pbEntropyPool.ReadData();
|
||||
int cbPool = pbPool.Length;
|
||||
int cbCtr = pbCounter.Length;
|
||||
int cbCsp = pbCsp.Length;
|
||||
|
||||
pbCmp = new byte[cbPool + cbCtr + cbCsp];
|
||||
Array.Copy(pbPool, pbCmp, cbPool);
|
||||
Array.Copy(pbCounter, 0, pbCmp, cbPool, cbCtr);
|
||||
Array.Copy(pbCsp, 0, pbCmp, cbPool + cbCtr, cbCsp);
|
||||
|
||||
MemUtil.ZeroByteArray(pbCsp);
|
||||
MemUtil.ZeroByteArray(pbPool);
|
||||
|
||||
m_uGeneratedBytesCount += 32;
|
||||
}
|
||||
|
||||
byte[] pbRet = CryptoUtil.HashSha256(pbCmp);
|
||||
MemUtil.ZeroByteArray(pbCmp);
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a number of cryptographically strong random bytes.
|
||||
/// This method is thread-safe.
|
||||
/// </summary>
|
||||
/// <param name="uRequestedBytes">Number of requested random bytes.</param>
|
||||
/// <returns>A byte array consisting of <paramref name="uRequestedBytes" />
|
||||
/// random bytes.</returns>
|
||||
public byte[] GetRandomBytes(uint uRequestedBytes)
|
||||
{
|
||||
if(uRequestedBytes == 0) return MemUtil.EmptyByteArray;
|
||||
if(uRequestedBytes > (uint)int.MaxValue)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ArgumentOutOfRangeException("uRequestedBytes");
|
||||
}
|
||||
|
||||
int cbRem = (int)uRequestedBytes;
|
||||
byte[] pbRes = new byte[cbRem];
|
||||
int iPos = 0;
|
||||
|
||||
while(cbRem != 0)
|
||||
{
|
||||
byte[] pbRandom256 = GenerateRandom256();
|
||||
Debug.Assert(pbRandom256.Length == 32);
|
||||
|
||||
int cbCopy = Math.Min(cbRem, pbRandom256.Length);
|
||||
Array.Copy(pbRandom256, 0, pbRes, iPos, cbCopy);
|
||||
|
||||
MemUtil.ZeroByteArray(pbRandom256);
|
||||
|
||||
iPos += cbCopy;
|
||||
cbRem -= cbCopy;
|
||||
}
|
||||
|
||||
Debug.Assert(iPos == pbRes.Length);
|
||||
return pbRes;
|
||||
}
|
||||
|
||||
private static int g_iWeakSeed = 0;
|
||||
public static Random NewWeakRandom()
|
||||
{
|
||||
long s64 = DateTime.UtcNow.ToBinary();
|
||||
int s32 = (int)((s64 >> 32) ^ s64);
|
||||
|
||||
lock(g_oSyncRoot)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
g_iWeakSeed += 0x78A8C4B7; // Prime number
|
||||
s32 ^= g_iWeakSeed;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent overflow in the Random constructor of .NET 2.0
|
||||
if(s32 == int.MinValue) s32 = int.MaxValue;
|
||||
|
||||
return new Random(s32);
|
||||
}
|
||||
}
|
||||
}
|
||||
262
ModernKeePassLib/Cryptography/CryptoRandomStream.cs
Normal file
262
ModernKeePassLib/Cryptography/CryptoRandomStream.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Algorithms supported by <c>CryptoRandomStream</c>.
|
||||
/// </summary>
|
||||
public enum CrsAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Not supported.
|
||||
/// </summary>
|
||||
Null = 0,
|
||||
|
||||
/// <summary>
|
||||
/// A variant of the ARCFour algorithm (RC4 incompatible).
|
||||
/// Insecure; for backward compatibility only.
|
||||
/// </summary>
|
||||
ArcFourVariant = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Salsa20 stream cipher algorithm.
|
||||
/// </summary>
|
||||
Salsa20 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// ChaCha20 stream cipher algorithm.
|
||||
/// </summary>
|
||||
ChaCha20 = 3,
|
||||
|
||||
Count = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A random stream class. The class is initialized using random
|
||||
/// bytes provided by the caller. The produced stream has random
|
||||
/// properties, but for the same seed always the same stream
|
||||
/// is produced, i.e. this class can be used as stream cipher.
|
||||
/// </summary>
|
||||
public sealed class CryptoRandomStream : IDisposable
|
||||
{
|
||||
private readonly CrsAlgorithm m_crsAlgorithm;
|
||||
private bool m_bDisposed = false;
|
||||
|
||||
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="a">Algorithm to use.</param>
|
||||
/// <param name="pbKey">Initialization key. Must not be <c>null</c>
|
||||
/// and must contain at least 1 byte.</param>
|
||||
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
|
||||
{
|
||||
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
|
||||
|
||||
int cbKey = pbKey.Length;
|
||||
if(cbKey <= 0)
|
||||
{
|
||||
Debug.Assert(false); // Need at least one byte
|
||||
throw new ArgumentOutOfRangeException("pbKey");
|
||||
}
|
||||
|
||||
m_crsAlgorithm = a;
|
||||
|
||||
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(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
|
||||
|
||||
unchecked
|
||||
{
|
||||
byte j = 0, t;
|
||||
int inxKey = 0;
|
||||
for(int w = 0; w < 256; ++w) // Key setup
|
||||
{
|
||||
j += (byte)(m_pbState[w] + pbKey[inxKey]);
|
||||
|
||||
t = m_pbState[0]; // Swap entries
|
||||
m_pbState[0] = m_pbState[j];
|
||||
m_pbState[j] = t;
|
||||
|
||||
++inxKey;
|
||||
if(inxKey >= cbKey) inxKey = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GetRandomBytes(512); // Increases security, see cryptanalysis
|
||||
}
|
||||
else // Unknown algorithm
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ArgumentOutOfRangeException("a");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing)
|
||||
{
|
||||
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
|
||||
m_chacha20.Dispose();
|
||||
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
|
||||
m_salsa20.Dispose();
|
||||
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
|
||||
{
|
||||
MemUtil.ZeroByteArray(m_pbState);
|
||||
m_i = 0;
|
||||
m_j = 0;
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
m_bDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get <paramref name="uRequestedCount" /> random bytes.
|
||||
/// </summary>
|
||||
/// <param name="uRequestedCount">Number of random bytes to retrieve.</param>
|
||||
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
|
||||
public byte[] GetRandomBytes(uint uRequestedCount)
|
||||
{
|
||||
if(m_bDisposed) throw new ObjectDisposedException(null);
|
||||
|
||||
if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
|
||||
if(uRequestedCount > (uint)int.MaxValue)
|
||||
throw new ArgumentOutOfRangeException("uRequestedCount");
|
||||
int cb = (int)uRequestedCount;
|
||||
|
||||
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(int w = 0; w < cb; ++w)
|
||||
{
|
||||
++m_i;
|
||||
m_j += m_pbState[m_i];
|
||||
|
||||
byte t = m_pbState[m_i]; // Swap entries
|
||||
m_pbState[m_i] = m_pbState[m_j];
|
||||
m_pbState[m_j] = t;
|
||||
|
||||
t = (byte)(m_pbState[m_i] + m_pbState[m_j]);
|
||||
pbRet[w] = m_pbState[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
return pbRet;
|
||||
}
|
||||
|
||||
public ulong GetRandomUInt64()
|
||||
{
|
||||
byte[] pb = GetRandomBytes(8);
|
||||
return MemUtil.BytesToUInt64(pb);
|
||||
}
|
||||
|
||||
#if CRSBENCHMARK
|
||||
public static string Benchmark()
|
||||
{
|
||||
int nRounds = 2000000;
|
||||
|
||||
string str = "ArcFour small: " + BenchTime(CrsAlgorithm.ArcFourVariant,
|
||||
nRounds, 16).ToString() + "\r\n";
|
||||
str += "ArcFour big: " + BenchTime(CrsAlgorithm.ArcFourVariant,
|
||||
32, 2 * 1024 * 1024).ToString() + "\r\n";
|
||||
str += "Salsa20 small: " + BenchTime(CrsAlgorithm.Salsa20,
|
||||
nRounds, 16).ToString() + "\r\n";
|
||||
str += "Salsa20 big: " + BenchTime(CrsAlgorithm.Salsa20,
|
||||
32, 2 * 1024 * 1024).ToString();
|
||||
return str;
|
||||
}
|
||||
|
||||
private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize)
|
||||
{
|
||||
byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 };
|
||||
|
||||
int nStart = Environment.TickCount;
|
||||
for(int i = 0; i < nRounds; ++i)
|
||||
{
|
||||
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
|
||||
{
|
||||
c.GetRandomBytes((uint)nDataSize);
|
||||
}
|
||||
}
|
||||
int nEnd = Environment.TickCount;
|
||||
|
||||
return (nEnd - nStart);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
64
ModernKeePassLib/Cryptography/CryptoStreamEx.cs
Normal file
64
ModernKeePassLib/Cryptography/CryptoStreamEx.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public sealed class CryptoStreamEx : CryptoStream
|
||||
{
|
||||
private ICryptoTransform m_t;
|
||||
private SymmetricAlgorithm m_a;
|
||||
|
||||
public CryptoStreamEx(Stream s, ICryptoTransform t, CryptoStreamMode m,
|
||||
SymmetricAlgorithm a) : base(s, t, m)
|
||||
{
|
||||
m_t = t;
|
||||
m_a = a;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try { base.Dispose(disposing); }
|
||||
// Unnecessary exception from CryptoStream with
|
||||
// RijndaelManagedTransform when a stream hasn't been
|
||||
// read completely (e.g. incorrect master key)
|
||||
catch (CryptographicException) { }
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
try { if (m_t != null) { m_t.Dispose(); m_t = null; } }
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
|
||||
// In .NET 2.0, SymmetricAlgorithm.Dispose() is not public
|
||||
try { if (m_a != null) { m_a.Clear(); m_a = null; } }
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
254
ModernKeePassLib/Cryptography/CryptoUtil.cs
Normal file
254
ModernKeePassLib/Cryptography/CryptoUtil.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Reflection;
|
||||
using System.Text;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public static class CryptoUtil
|
||||
{
|
||||
private static bool? g_obProtData = null;
|
||||
public static bool IsProtectedDataSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
if(g_obProtData.HasValue) return g_obProtData.Value;
|
||||
|
||||
bool b = false;
|
||||
try
|
||||
{
|
||||
Random r = CryptoRandom.NewWeakRandom();
|
||||
|
||||
byte[] pbData = new byte[137];
|
||||
r.NextBytes(pbData);
|
||||
|
||||
byte[] pbEnt = new byte[41];
|
||||
r.NextBytes(pbEnt);
|
||||
|
||||
byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
if((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData))
|
||||
{
|
||||
byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
if((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData))
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
Debug.Assert(b); // Should be supported on all systems
|
||||
g_obProtData = b;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
internal static byte[] HashSha256(string strFilePath)
|
||||
{
|
||||
byte[] pbHash = null;
|
||||
|
||||
using(FileStream fs = new FileStream(strFilePath, FileMode.Open,
|
||||
FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using(SHA256Managed h = new SHA256Managed())
|
||||
{
|
||||
pbHash = h.ComputeHash(fs);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#if !KeePassUAP
|
||||
private static bool? g_obAesCsp = null;
|
||||
public static SymmetricAlgorithm CreateAes()
|
||||
{
|
||||
if(g_obAesCsp.HasValue)
|
||||
return (g_obAesCsp.Value ? CreateAesCsp() : new RijndaelManaged());
|
||||
|
||||
SymmetricAlgorithm a = CreateAesCsp();
|
||||
g_obAesCsp = (a != null);
|
||||
return (a ?? new RijndaelManaged());
|
||||
}
|
||||
|
||||
private static SymmetricAlgorithm CreateAesCsp()
|
||||
{
|
||||
try
|
||||
{
|
||||
// On Windows, the CSP implementation is only minimally
|
||||
// faster (and for key derivations it's not used anyway,
|
||||
// as KeePass uses a native implementation based on
|
||||
// CNG/BCrypt, which is much faster)
|
||||
if(!NativeLib.IsUnix()) return null;
|
||||
|
||||
string strFqn = Assembly.CreateQualifiedName(
|
||||
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
|
||||
"System.Security.Cryptography.AesCryptoServiceProvider");
|
||||
|
||||
Type t = Type.GetType(strFqn);
|
||||
if(t == null) return null;
|
||||
|
||||
return (Activator.CreateInstance(t) as SymmetricAlgorithm);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy,
|
||||
DataProtectionScope s)
|
||||
{
|
||||
return ProtectDataPriv(pb, true, pbOptEntropy, s);
|
||||
}
|
||||
|
||||
public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy,
|
||||
DataProtectionScope s)
|
||||
{
|
||||
return ProtectDataPriv(pb, false, pbOptEntropy, s);
|
||||
}
|
||||
|
||||
private static byte[] ProtectDataPriv(byte[] pb, bool bProtect,
|
||||
byte[] pbOptEntropy, DataProtectionScope s)
|
||||
{
|
||||
if(pb == null) throw new ArgumentNullException("pb");
|
||||
|
||||
if((pbOptEntropy != null) && (pbOptEntropy.Length == 0))
|
||||
pbOptEntropy = null;
|
||||
|
||||
if(CryptoUtil.IsProtectedDataSupported)
|
||||
{
|
||||
if(bProtect)
|
||||
return ProtectedData.Protect(pb, pbOptEntropy, s);
|
||||
return ProtectedData.Unprotect(pb, pbOptEntropy, s);
|
||||
}
|
||||
|
||||
Debug.Assert(false);
|
||||
byte[] pbCopy = new byte[pb.Length];
|
||||
Array.Copy(pb, pbCopy, pb.Length);
|
||||
return pbCopy;
|
||||
}
|
||||
}
|
||||
}
|
||||
232
ModernKeePassLib/Cryptography/Hash/Blake2b.cs
Normal file
232
ModernKeePassLib/Cryptography/Hash/Blake2b.cs
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
187
ModernKeePassLib/Cryptography/HashingStreamEx.cs
Normal file
187
ModernKeePassLib/Cryptography/HashingStreamEx.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public sealed class HashingStreamEx : Stream
|
||||
{
|
||||
private readonly Stream m_sBaseStream;
|
||||
private readonly bool m_bWriting;
|
||||
private HashAlgorithm m_hash;
|
||||
|
||||
private byte[] m_pbFinalHash = null;
|
||||
|
||||
public byte[] Hash
|
||||
{
|
||||
get { return m_pbFinalHash; }
|
||||
}
|
||||
|
||||
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 { return m_sBaseStream.Length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return m_sBaseStream.Position; }
|
||||
set { Debug.Assert(false); throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public HashingStreamEx(Stream sBaseStream, bool bWriting, HashAlgorithm hashAlgorithm)
|
||||
{
|
||||
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
|
||||
|
||||
m_sBaseStream = sBaseStream;
|
||||
m_bWriting = bWriting;
|
||||
|
||||
#if !KeePassLibSD
|
||||
m_hash = (hashAlgorithm ?? new SHA256Managed());
|
||||
#else // KeePassLibSD
|
||||
m_hash = null;
|
||||
|
||||
try { m_hash = HashAlgorithm.Create("SHA256"); }
|
||||
catch(Exception) { }
|
||||
try { if(m_hash == null) m_hash = HashAlgorithm.Create(); }
|
||||
catch(Exception) { }
|
||||
#endif
|
||||
if(m_hash == null) { Debug.Assert(false); return; }
|
||||
|
||||
// Validate hash algorithm
|
||||
if(!m_hash.CanReuseTransform || !m_hash.CanTransformMultipleBlocks)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
m_hash = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing)
|
||||
{
|
||||
if(m_hash != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_hash.TransformFinalBlock(MemUtil.EmptyByteArray, 0, 0);
|
||||
m_pbFinalHash = m_hash.Hash;
|
||||
m_hash.Clear();
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
m_hash = null;
|
||||
}
|
||||
|
||||
m_sBaseStream.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
m_sBaseStream.Flush();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public override int Read(byte[] pbBuffer, int nOffset, int nCount)
|
||||
{
|
||||
if(m_bWriting) { Debug.Assert(false); throw new InvalidOperationException(); }
|
||||
|
||||
int nRead = m_sBaseStream.Read(pbBuffer, nOffset, nCount);
|
||||
int nPartialRead = nRead;
|
||||
while((nRead < nCount) && (nPartialRead != 0))
|
||||
{
|
||||
nPartialRead = m_sBaseStream.Read(pbBuffer, nOffset + nRead,
|
||||
nCount - nRead);
|
||||
nRead += nPartialRead;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
byte[] pbOrg = new byte[pbBuffer.Length];
|
||||
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
|
||||
#endif
|
||||
|
||||
if((m_hash != null) && (nRead > 0))
|
||||
m_hash.TransformBlock(pbBuffer, nOffset, nRead, pbBuffer, nOffset);
|
||||
|
||||
#if DEBUG
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
#endif
|
||||
|
||||
return nRead;
|
||||
}
|
||||
|
||||
public override void Write(byte[] pbBuffer, int nOffset, int nCount)
|
||||
{
|
||||
if(!m_bWriting) { Debug.Assert(false); throw new InvalidOperationException(); }
|
||||
|
||||
#if DEBUG
|
||||
byte[] pbOrg = new byte[pbBuffer.Length];
|
||||
Array.Copy(pbBuffer, pbOrg, pbBuffer.Length);
|
||||
#endif
|
||||
|
||||
if((m_hash != null) && (nCount > 0))
|
||||
m_hash.TransformBlock(pbBuffer, nOffset, nCount, pbBuffer, nOffset);
|
||||
|
||||
#if DEBUG
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbBuffer, pbOrg));
|
||||
#endif
|
||||
|
||||
m_sBaseStream.Write(pbBuffer, nOffset, nCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
98
ModernKeePassLib/Cryptography/HmacOtp.cs
Normal file
98
ModernKeePassLib/Cryptography/HmacOtp.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Globalization;
|
||||
using System.Text;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if !KeePassLibSD
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Generate HMAC-based one-time passwords as specified in RFC 4226.
|
||||
/// </summary>
|
||||
public static class HmacOtp
|
||||
{
|
||||
private static readonly uint[] g_vDigitsPower = new uint[] { 1,
|
||||
10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||
|
||||
public static string Generate(byte[] pbSecret, ulong uFactor,
|
||||
uint uCodeDigits, bool bAddChecksum, int iTruncationOffset)
|
||||
{
|
||||
byte[] pbText = MemUtil.UInt64ToBytes(uFactor);
|
||||
Array.Reverse(pbText); // To big-endian
|
||||
|
||||
byte[] pbHash;
|
||||
using(HMACSHA1 h = new HMACSHA1(pbSecret))
|
||||
{
|
||||
pbHash = h.ComputeHash(pbText);
|
||||
}
|
||||
|
||||
uint uOffset = (uint)(pbHash[pbHash.Length - 1] & 0xF);
|
||||
if((iTruncationOffset >= 0) && (iTruncationOffset < (pbHash.Length - 4)))
|
||||
uOffset = (uint)iTruncationOffset;
|
||||
|
||||
uint uBinary = (uint)(((pbHash[uOffset] & 0x7F) << 24) |
|
||||
((pbHash[uOffset + 1] & 0xFF) << 16) |
|
||||
((pbHash[uOffset + 2] & 0xFF) << 8) |
|
||||
(pbHash[uOffset + 3] & 0xFF));
|
||||
|
||||
uint uOtp = (uBinary % g_vDigitsPower[uCodeDigits]);
|
||||
if(bAddChecksum)
|
||||
uOtp = ((uOtp * 10) + CalculateChecksum(uOtp, uCodeDigits));
|
||||
|
||||
uint uDigits = (bAddChecksum ? (uCodeDigits + 1) : uCodeDigits);
|
||||
return uOtp.ToString(NumberFormatInfo.InvariantInfo).PadLeft(
|
||||
(int)uDigits, '0');
|
||||
}
|
||||
|
||||
private static readonly uint[] g_vDoubleDigits = new uint[] {
|
||||
0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
|
||||
|
||||
private static uint CalculateChecksum(uint uNum, uint uDigits)
|
||||
{
|
||||
bool bDoubleDigit = true;
|
||||
uint uTotal = 0;
|
||||
|
||||
while(0 < uDigits--)
|
||||
{
|
||||
uint uDigit = (uNum % 10);
|
||||
uNum /= 10;
|
||||
|
||||
if(bDoubleDigit) uDigit = g_vDoubleDigits[uDigit];
|
||||
|
||||
uTotal += uDigit;
|
||||
bDoubleDigit = !bDoubleDigit;
|
||||
}
|
||||
|
||||
uint uResult = (uTotal % 10);
|
||||
if(uResult != 0) uResult = 10 - uResult;
|
||||
|
||||
return uResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
399
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.GCrypt.cs
Normal file
399
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.GCrypt.cs
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public sealed partial class AesKdf : KdfEngine
|
||||
{
|
||||
private static bool TransformKeyGCrypt(byte[] pbData32, byte[] pbSeed32,
|
||||
ulong uRounds)
|
||||
{
|
||||
byte[] pbNewData32 = null;
|
||||
try
|
||||
{
|
||||
if(GCryptInitLib())
|
||||
{
|
||||
pbNewData32 = new byte[32];
|
||||
Array.Copy(pbData32, pbNewData32, 32);
|
||||
|
||||
if(TransformKeyGCryptPriv(pbNewData32, pbSeed32, uRounds))
|
||||
{
|
||||
Array.Copy(pbNewData32, pbData32, 32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception) { }
|
||||
finally { if(pbNewData32 != null) MemUtil.ZeroByteArray(pbNewData32); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TransformKeyBenchmarkGCrypt(uint uTimeMs, out ulong uRounds)
|
||||
{
|
||||
uRounds = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if(GCryptInitLib())
|
||||
return TransformKeyBenchmarkGCryptPriv(uTimeMs, ref uRounds);
|
||||
}
|
||||
catch(Exception) { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool GCryptInitLib()
|
||||
{
|
||||
if(!NativeLib.IsUnix()) return false; // Independent of workaround state
|
||||
if(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off
|
||||
|
||||
// gcry_check_version initializes the library;
|
||||
// throws when LibGCrypt is not available
|
||||
NativeMethods.gcry_check_version(IntPtr.Zero);
|
||||
return true;
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// Multi-threaded implementation
|
||||
|
||||
// For some reason, the following multi-threaded implementation
|
||||
// is slower than the single-threaded implementation below
|
||||
// (threading overhead by Mono? LibGCrypt threading issues?)
|
||||
/* private sealed class GCryptTransformInfo : IDisposable
|
||||
{
|
||||
public IntPtr Data16;
|
||||
public IntPtr Seed32;
|
||||
public ulong Rounds;
|
||||
public uint TimeMs;
|
||||
|
||||
public bool Success = false;
|
||||
|
||||
public GCryptTransformInfo(byte[] pbData32, int iDataOffset,
|
||||
byte[] pbSeed32, ulong uRounds, uint uTimeMs)
|
||||
{
|
||||
this.Data16 = Marshal.AllocCoTaskMem(16);
|
||||
Marshal.Copy(pbData32, iDataOffset, this.Data16, 16);
|
||||
|
||||
this.Seed32 = Marshal.AllocCoTaskMem(32);
|
||||
Marshal.Copy(pbSeed32, 0, this.Seed32, 32);
|
||||
|
||||
this.Rounds = uRounds;
|
||||
this.TimeMs = uTimeMs;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(this.Data16 != IntPtr.Zero)
|
||||
{
|
||||
Marshal.WriteInt64(this.Data16, 0);
|
||||
Marshal.WriteInt64(this.Data16, 8, 0);
|
||||
Marshal.FreeCoTaskMem(this.Data16);
|
||||
this.Data16 = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if(this.Seed32 != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(this.Seed32);
|
||||
this.Seed32 = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static GCryptTransformInfo[] GCryptRun(byte[] pbData32,
|
||||
byte[] pbSeed32, ulong uRounds, uint uTimeMs, ParameterizedThreadStart fL,
|
||||
ParameterizedThreadStart fR)
|
||||
{
|
||||
GCryptTransformInfo tiL = new GCryptTransformInfo(pbData32, 0,
|
||||
pbSeed32, uRounds, uTimeMs);
|
||||
GCryptTransformInfo tiR = new GCryptTransformInfo(pbData32, 16,
|
||||
pbSeed32, uRounds, uTimeMs);
|
||||
|
||||
Thread th = new Thread(fL);
|
||||
th.Start(tiL);
|
||||
|
||||
fR(tiR);
|
||||
|
||||
th.Join();
|
||||
|
||||
Marshal.Copy(tiL.Data16, pbData32, 0, 16);
|
||||
Marshal.Copy(tiR.Data16, pbData32, 16, 16);
|
||||
|
||||
tiL.Dispose();
|
||||
tiR.Dispose();
|
||||
|
||||
if(tiL.Success && tiR.Success)
|
||||
return new GCryptTransformInfo[2] { tiL, tiR };
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
|
||||
ulong uRounds)
|
||||
{
|
||||
return (GCryptRun(pbData32, pbSeed32, uRounds, 0,
|
||||
new ParameterizedThreadStart(AesKdf.GCryptTransformTh),
|
||||
new ParameterizedThreadStart(AesKdf.GCryptTransformTh)) != null);
|
||||
}
|
||||
|
||||
private static bool GCryptInitCipher(ref IntPtr h, GCryptTransformInfo ti)
|
||||
{
|
||||
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
|
||||
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
|
||||
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
|
||||
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
if(NativeMethods.gcry_cipher_setkey(h, ti.Seed32, n32) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GCryptTransformTh(object o)
|
||||
{
|
||||
IntPtr h = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
GCryptTransformInfo ti = (o as GCryptTransformInfo);
|
||||
if(ti == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(!GCryptInitCipher(ref h, ti)) return;
|
||||
|
||||
IntPtr n16 = new IntPtr(16);
|
||||
for(ulong u = 0; u < ti.Rounds; ++u)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ti.Success = true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
|
||||
{
|
||||
GCryptTransformInfo[] v = GCryptRun(new byte[32], new byte[32],
|
||||
0, uTimeMs,
|
||||
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh),
|
||||
new ParameterizedThreadStart(AesKdf.GCryptBenchmarkTh));
|
||||
|
||||
if(v != null)
|
||||
{
|
||||
ulong uL = Math.Min(v[0].Rounds, ulong.MaxValue >> 1);
|
||||
ulong uR = Math.Min(v[1].Rounds, ulong.MaxValue >> 1);
|
||||
uRounds = (uL + uR) / 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void GCryptBenchmarkTh(object o)
|
||||
{
|
||||
IntPtr h = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
GCryptTransformInfo ti = (o as GCryptTransformInfo);
|
||||
if(ti == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(!GCryptInitCipher(ref h, ti)) return;
|
||||
|
||||
ulong r = 0;
|
||||
IntPtr n16 = new IntPtr(16);
|
||||
int tStart = Environment.TickCount;
|
||||
while(true)
|
||||
{
|
||||
for(ulong j = 0; j < BenchStep; ++j)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, ti.Data16, n16,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
r += BenchStep;
|
||||
if(r < BenchStep) // Overflow check
|
||||
{
|
||||
r = ulong.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
uint tElapsed = (uint)(Environment.TickCount - tStart);
|
||||
if(tElapsed > ti.TimeMs) break;
|
||||
}
|
||||
|
||||
ti.Rounds = r;
|
||||
ti.Success = true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
try { if(h != IntPtr.Zero) NativeMethods.gcry_cipher_close(h); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
} */
|
||||
|
||||
// =============================================================
|
||||
// Single-threaded implementation
|
||||
|
||||
private static bool GCryptInitCipher(ref IntPtr h, IntPtr pSeed32)
|
||||
{
|
||||
NativeMethods.gcry_cipher_open(ref h, NativeMethods.GCRY_CIPHER_AES256,
|
||||
NativeMethods.GCRY_CIPHER_MODE_ECB, 0);
|
||||
if(h == IntPtr.Zero) { Debug.Assert(false); return false; }
|
||||
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
if(NativeMethods.gcry_cipher_setkey(h, pSeed32, n32) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool GCryptBegin(byte[] pbData32, byte[] pbSeed32,
|
||||
ref IntPtr h, ref IntPtr pData32, ref IntPtr pSeed32)
|
||||
{
|
||||
pData32 = Marshal.AllocCoTaskMem(32);
|
||||
pSeed32 = Marshal.AllocCoTaskMem(32);
|
||||
|
||||
Marshal.Copy(pbData32, 0, pData32, 32);
|
||||
Marshal.Copy(pbSeed32, 0, pSeed32, 32);
|
||||
|
||||
return GCryptInitCipher(ref h, pSeed32);
|
||||
}
|
||||
|
||||
private static void GCryptEnd(IntPtr h, IntPtr pData32, IntPtr pSeed32)
|
||||
{
|
||||
NativeMethods.gcry_cipher_close(h);
|
||||
|
||||
Marshal.WriteInt64(pData32, 0);
|
||||
Marshal.WriteInt64(pData32, 8, 0);
|
||||
Marshal.WriteInt64(pData32, 16, 0);
|
||||
Marshal.WriteInt64(pData32, 24, 0);
|
||||
|
||||
Marshal.FreeCoTaskMem(pData32);
|
||||
Marshal.FreeCoTaskMem(pSeed32);
|
||||
}
|
||||
|
||||
private static bool TransformKeyGCryptPriv(byte[] pbData32, byte[] pbSeed32,
|
||||
ulong uRounds)
|
||||
{
|
||||
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
|
||||
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
for(ulong i = 0; i < uRounds; ++i)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Marshal.Copy(pData32, pbData32, 0, 32);
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { GCryptEnd(h, pData32, pSeed32); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TransformKeyBenchmarkGCryptPriv(uint uTimeMs, ref ulong uRounds)
|
||||
{
|
||||
byte[] pbData32 = new byte[32];
|
||||
byte[] pbSeed32 = new byte[32];
|
||||
|
||||
IntPtr h = IntPtr.Zero, pData32 = IntPtr.Zero, pSeed32 = IntPtr.Zero;
|
||||
if(!GCryptBegin(pbData32, pbSeed32, ref h, ref pData32, ref pSeed32))
|
||||
return false;
|
||||
|
||||
uint uMaxMs = uTimeMs;
|
||||
ulong uDiv = 1;
|
||||
if(uMaxMs <= (uint.MaxValue >> 1)) { uMaxMs *= 2U; uDiv = 2; }
|
||||
|
||||
try
|
||||
{
|
||||
ulong r = 0;
|
||||
IntPtr n32 = new IntPtr(32);
|
||||
int tStart = Environment.TickCount;
|
||||
while(true)
|
||||
{
|
||||
for(ulong j = 0; j < BenchStep; ++j)
|
||||
{
|
||||
if(NativeMethods.gcry_cipher_encrypt(h, pData32, n32,
|
||||
IntPtr.Zero, IntPtr.Zero) != 0)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
r += BenchStep;
|
||||
if(r < BenchStep) // Overflow check
|
||||
{
|
||||
r = ulong.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
uint tElapsed = (uint)(Environment.TickCount - tStart);
|
||||
if(tElapsed > uMaxMs) break;
|
||||
}
|
||||
|
||||
uRounds = r / uDiv;
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { GCryptEnd(h, pData32, pSeed32); }
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
281
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs
Normal file
281
ModernKeePassLib/Cryptography/KeyDerivation/AesKdf.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.KeyDerivation
|
||||
{
|
||||
public sealed partial 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 static readonly string ParamRounds = "R"; // UInt64
|
||||
public static readonly string ParamSeed = "S"; // Byte[32]
|
||||
|
||||
private const ulong BenchStep = 3001;
|
||||
|
||||
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
|
||||
{
|
||||
#if !ModernKeePassLib
|
||||
if(NativeLib.TransformKey256(pbNewKey, pbKeySeed32, uNumRounds))
|
||||
return CryptoUtil.HashSha256(pbNewKey);
|
||||
#endif
|
||||
|
||||
if(TransformKeyGCrypt(pbNewKey, pbKeySeed32, uNumRounds))
|
||||
return CryptoUtil.HashSha256(pbNewKey);
|
||||
|
||||
if(TransformKeyManaged(pbNewKey, pbKeySeed32, uNumRounds))
|
||||
return CryptoUtil.HashSha256(pbNewKey);
|
||||
}
|
||||
finally { MemUtil.ZeroByteArray(pbNewKey); }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
|
||||
ulong uNumRounds)
|
||||
{
|
||||
#if KeePassUAP
|
||||
KeyParameter kp = new KeyParameter(pbKeySeed32);
|
||||
AesEngine aes = new AesEngine();
|
||||
aes.Init(true, kp);
|
||||
|
||||
for(ulong u = 0; u < uNumRounds; ++u)
|
||||
{
|
||||
aes.ProcessBlock(pbNewKey32, 0, pbNewKey32, 0);
|
||||
aes.ProcessBlock(pbNewKey32, 16, pbNewKey32, 16);
|
||||
}
|
||||
|
||||
aes.Reset();
|
||||
#else
|
||||
byte[] pbIV = new byte[16];
|
||||
|
||||
using(SymmetricAlgorithm a = CryptoUtil.CreateAes())
|
||||
{
|
||||
if(a.BlockSize != 128) // AES block size
|
||||
{
|
||||
Debug.Assert(false);
|
||||
a.BlockSize = 128;
|
||||
}
|
||||
a.KeySize = 256;
|
||||
a.Mode = CipherMode.ECB;
|
||||
|
||||
using(ICryptoTransform t = a.CreateEncryptor(pbKeySeed32, pbIV))
|
||||
{
|
||||
// !t.CanReuseTransform -- doesn't work with Mono
|
||||
if((t == null) || (t.InputBlockSize != 16) ||
|
||||
(t.OutputBlockSize != 16))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(ulong u = 0; u < uNumRounds; ++u)
|
||||
{
|
||||
t.TransformBlock(pbNewKey32, 0, 16, pbNewKey32, 0);
|
||||
t.TransformBlock(pbNewKey32, 16, 16, pbNewKey32, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override KdfParameters GetBestParameters(uint uMilliseconds)
|
||||
{
|
||||
KdfParameters p = GetDefaultParameters();
|
||||
ulong uRounds;
|
||||
#if !ModernKeePassLib
|
||||
// Try native method
|
||||
if(NativeLib.TransformKeyBenchmark256(uMilliseconds, out uRounds))
|
||||
{
|
||||
p.SetUInt64(ParamRounds, uRounds);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
if(TransformKeyBenchmarkGCrypt(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];
|
||||
|
||||
using(SymmetricAlgorithm a = CryptoUtil.CreateAes())
|
||||
{
|
||||
if(a.BlockSize != 128) // AES block size
|
||||
{
|
||||
Debug.Assert(false);
|
||||
a.BlockSize = 128;
|
||||
}
|
||||
a.KeySize = 256;
|
||||
a.Mode = CipherMode.ECB;
|
||||
|
||||
using(ICryptoTransform t = a.CreateEncryptor(pbKey, pbIV))
|
||||
{
|
||||
// !t.CanReuseTransform -- doesn't work with Mono
|
||||
if((t == null) || (t.InputBlockSize != 16) ||
|
||||
(t.OutputBlockSize != 16))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
p.SetUInt64(ParamRounds, PwDefs.DefaultKeyEncryptionRounds);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
uRounds = 0;
|
||||
int tStart = Environment.TickCount;
|
||||
while(true)
|
||||
{
|
||||
for(ulong j = 0; j < BenchStep; ++j)
|
||||
{
|
||||
#if KeePassUAP
|
||||
aes.ProcessBlock(pbNewKey, 0, pbNewKey, 0);
|
||||
aes.ProcessBlock(pbNewKey, 16, pbNewKey, 16);
|
||||
#else
|
||||
t.TransformBlock(pbNewKey, 0, 16, pbNewKey, 0);
|
||||
t.TransformBlock(pbNewKey, 16, 16, pbNewKey, 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
uRounds += BenchStep;
|
||||
if(uRounds < BenchStep) // Overflow check
|
||||
{
|
||||
uRounds = ulong.MaxValue;
|
||||
break;
|
||||
}
|
||||
|
||||
uint tElapsed = (uint)(Environment.TickCount - tStart);
|
||||
if(tElapsed > uMilliseconds) break;
|
||||
}
|
||||
|
||||
p.SetUInt64(ParamRounds, uRounds);
|
||||
#if KeePassUAP
|
||||
aes.Reset();
|
||||
#else
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
637
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
Normal file
637
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.Core.cs
Normal file
@@ -0,0 +1,637 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 System.Threading.Tasks;
|
||||
using ModernKeePassLib.Cryptography.Hash;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.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;
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if(this.Finished != null)
|
||||
{
|
||||
this.Finished.Dispose();
|
||||
this.Finished = null;
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
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 *
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
(ulong)Marshal.SizeOf<ulong>()
|
||||
#else
|
||||
(ulong)Marshal.SizeOf(typeof(ulong))
|
||||
#endif
|
||||
));
|
||||
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];
|
||||
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
Array.Copy(vSrc, (int)uSrcOffset, vDst, (int)uDstOffset,
|
||||
(int)NbBlockSizeInQW);
|
||||
#else
|
||||
Array.Copy(vSrc, (long)uSrcOffset, vDst, (long)uDstOffset,
|
||||
(long)NbBlockSizeInQW);
|
||||
#endif
|
||||
}
|
||||
|
||||
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 ModernKeePassLib
|
||||
Task.Factory.StartNew(FillSegmentThr, ti);
|
||||
//ThreadPool.RunAsync(a => FillSegmentThr(ti));
|
||||
#else
|
||||
if(!ThreadPool.QueueUserWorkItem(FillSegmentThr, ti))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
#endif
|
||||
v[l] = ti;
|
||||
}
|
||||
|
||||
for(int l = 0; l < np; ++l)
|
||||
{
|
||||
v[l].Finished.WaitOne();
|
||||
v[l].Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.cs
Normal file
144
ModernKeePassLib/Cryptography/KeyDerivation/Argon2Kdf.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.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 static readonly string ParamSalt = "S"; // Byte[]
|
||||
public static readonly string ParamParallelism = "P"; // UInt32
|
||||
public static readonly string ParamMemory = "M"; // UInt64
|
||||
public static readonly string ParamIterations = "I"; // UInt64
|
||||
public static readonly string ParamVersion = "V"; // UInt32
|
||||
public static readonly string ParamSecretKey = "K"; // Byte[]
|
||||
public static readonly 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
ModernKeePassLib/Cryptography/KeyDerivation/KdfEngine.cs
Normal file
142
ModernKeePassLib/Cryptography/KeyDerivation/KdfEngine.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
ModernKeePassLib/Cryptography/KeyDerivation/KdfParameters.cs
Normal file
80
ModernKeePassLib/Cryptography/KeyDerivation/KdfParameters.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.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
ModernKeePassLib/Cryptography/KeyDerivation/KdfPool.cs
Normal file
96
ModernKeePassLib/Cryptography/KeyDerivation/KdfPool.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
internal static class CharSetBasedGenerator
|
||||
{
|
||||
internal static PwgError Generate(out ProtectedString psOut,
|
||||
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
psOut = ProtectedString.Empty;
|
||||
if(pwProfile.Length == 0) return PwgError.Success;
|
||||
|
||||
PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString());
|
||||
PwGenerator.PrepareCharSet(pcs, pwProfile);
|
||||
|
||||
char[] v = new char[pwProfile.Length];
|
||||
try
|
||||
{
|
||||
for(int i = 0; i < v.Length; ++i)
|
||||
{
|
||||
char ch = PwGenerator.GenerateCharacter(pwProfile,
|
||||
pcs, crsRandomSource);
|
||||
|
||||
if(ch == char.MinValue)
|
||||
return PwgError.TooFewCharacters;
|
||||
|
||||
v[i] = ch;
|
||||
}
|
||||
|
||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(v);
|
||||
psOut = new ProtectedString(true, pbUtf8);
|
||||
MemUtil.ZeroByteArray(pbUtf8);
|
||||
}
|
||||
finally { MemUtil.ZeroArray<char>(v); }
|
||||
|
||||
return PwgError.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
|
||||
using ModernKeePassLib;
|
||||
using ModernKeePassLib.Security;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
public abstract class CustomPwGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Each custom password generation algorithm must have
|
||||
/// its own unique UUID.
|
||||
/// </summary>
|
||||
public abstract PwUuid Uuid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Displayable name of the password generation algorithm.
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
public virtual bool SupportsOptions
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Password generation function.
|
||||
/// </summary>
|
||||
/// <param name="prf">Password generation options chosen
|
||||
/// by the user. This may be <c>null</c>, if the default
|
||||
/// options should be used.</param>
|
||||
/// <param name="crsRandomSource">Source that the algorithm
|
||||
/// can use to generate random numbers.</param>
|
||||
/// <returns>Generated password or <c>null</c> in case
|
||||
/// of failure. If returning <c>null</c>, the caller assumes
|
||||
/// that an error message has already been shown to the user.</returns>
|
||||
public abstract ProtectedString Generate(PwProfile prf,
|
||||
CryptoRandomStream crsRandomSource);
|
||||
|
||||
public virtual string GetOptions(string strCurrentOptions)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
public sealed class CustomPwGeneratorPool : IEnumerable<CustomPwGenerator>
|
||||
{
|
||||
private List<CustomPwGenerator> m_vGens = new List<CustomPwGenerator>();
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_vGens.Count; }
|
||||
}
|
||||
|
||||
public CustomPwGeneratorPool()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_vGens.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<CustomPwGenerator> GetEnumerator()
|
||||
{
|
||||
return m_vGens.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(CustomPwGenerator pwg)
|
||||
{
|
||||
if(pwg == null) throw new ArgumentNullException("pwg");
|
||||
|
||||
PwUuid uuid = pwg.Uuid;
|
||||
if(uuid == null) throw new ArgumentException();
|
||||
|
||||
int nIndex = FindIndex(uuid);
|
||||
|
||||
if(nIndex >= 0) m_vGens[nIndex] = pwg; // Replace
|
||||
else m_vGens.Add(pwg);
|
||||
}
|
||||
|
||||
public CustomPwGenerator Find(PwUuid uuid)
|
||||
{
|
||||
if(uuid == null) throw new ArgumentNullException("uuid");
|
||||
|
||||
foreach(CustomPwGenerator pwg in m_vGens)
|
||||
{
|
||||
if(uuid.Equals(pwg.Uuid)) return pwg;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public CustomPwGenerator Find(string strName)
|
||||
{
|
||||
if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
foreach(CustomPwGenerator pwg in m_vGens)
|
||||
{
|
||||
if(pwg.Name == strName) return pwg;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int FindIndex(PwUuid uuid)
|
||||
{
|
||||
if(uuid == null) throw new ArgumentNullException("uuid");
|
||||
|
||||
for(int i = 0; i < m_vGens.Count; ++i)
|
||||
{
|
||||
if(uuid.Equals(m_vGens[i].Uuid)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool Remove(PwUuid uuid)
|
||||
{
|
||||
if(uuid == null) throw new ArgumentNullException("uuid");
|
||||
|
||||
int nIndex = FindIndex(uuid);
|
||||
if(nIndex < 0) return false;
|
||||
|
||||
m_vGens.RemoveAt(nIndex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
internal static class PatternBasedGenerator
|
||||
{
|
||||
internal static PwgError Generate(out ProtectedString psOut,
|
||||
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
psOut = ProtectedString.Empty;
|
||||
|
||||
LinkedList<char> llGenerated = new LinkedList<char>();
|
||||
PwCharSet pcsCurrent = new PwCharSet();
|
||||
PwCharSet pcsCustom = new PwCharSet();
|
||||
PwCharSet pcsUsed = new PwCharSet();
|
||||
bool bInCharSetDef = false;
|
||||
|
||||
string strPattern = ExpandPattern(pwProfile.Pattern);
|
||||
if(strPattern.Length == 0) return PwgError.Success;
|
||||
|
||||
CharStream csStream = new CharStream(strPattern);
|
||||
char ch = csStream.ReadChar();
|
||||
|
||||
while(ch != char.MinValue)
|
||||
{
|
||||
pcsCurrent.Clear();
|
||||
|
||||
bool bGenerateChar = false;
|
||||
|
||||
if(ch == '\\')
|
||||
{
|
||||
ch = csStream.ReadChar();
|
||||
if(ch == char.MinValue) // Backslash at the end
|
||||
{
|
||||
llGenerated.AddLast('\\');
|
||||
break;
|
||||
}
|
||||
|
||||
if(bInCharSetDef) pcsCustom.Add(ch);
|
||||
else
|
||||
{
|
||||
llGenerated.AddLast(ch);
|
||||
pcsUsed.Add(ch);
|
||||
}
|
||||
}
|
||||
else if(ch == '^')
|
||||
{
|
||||
ch = csStream.ReadChar();
|
||||
if(ch == char.MinValue) // ^ at the end
|
||||
{
|
||||
llGenerated.AddLast('^');
|
||||
break;
|
||||
}
|
||||
|
||||
if(bInCharSetDef) pcsCustom.Remove(ch);
|
||||
}
|
||||
else if(ch == '[')
|
||||
{
|
||||
pcsCustom.Clear();
|
||||
bInCharSetDef = true;
|
||||
}
|
||||
else if(ch == ']')
|
||||
{
|
||||
pcsCurrent.Add(pcsCustom.ToString());
|
||||
|
||||
bInCharSetDef = false;
|
||||
bGenerateChar = true;
|
||||
}
|
||||
else if(bInCharSetDef)
|
||||
{
|
||||
if(pcsCustom.AddCharSet(ch) == false)
|
||||
pcsCustom.Add(ch);
|
||||
}
|
||||
else if(pcsCurrent.AddCharSet(ch) == false)
|
||||
{
|
||||
llGenerated.AddLast(ch);
|
||||
pcsUsed.Add(ch);
|
||||
}
|
||||
else bGenerateChar = true;
|
||||
|
||||
if(bGenerateChar)
|
||||
{
|
||||
PwGenerator.PrepareCharSet(pcsCurrent, pwProfile);
|
||||
|
||||
if(pwProfile.NoRepeatingCharacters)
|
||||
pcsCurrent.Remove(pcsUsed.ToString());
|
||||
|
||||
char chGen = PwGenerator.GenerateCharacter(pwProfile,
|
||||
pcsCurrent, crsRandomSource);
|
||||
|
||||
if(chGen == char.MinValue) return PwgError.TooFewCharacters;
|
||||
|
||||
llGenerated.AddLast(chGen);
|
||||
pcsUsed.Add(chGen);
|
||||
}
|
||||
|
||||
ch = csStream.ReadChar();
|
||||
}
|
||||
|
||||
if(llGenerated.Count == 0) return PwgError.Success;
|
||||
|
||||
char[] v = new char[llGenerated.Count];
|
||||
llGenerated.CopyTo(v, 0);
|
||||
|
||||
if(pwProfile.PatternPermutePassword)
|
||||
PwGenerator.Shuffle(v, crsRandomSource);
|
||||
|
||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(v);
|
||||
psOut = new ProtectedString(true, pbUtf8);
|
||||
MemUtil.ZeroByteArray(pbUtf8);
|
||||
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
llGenerated.Clear();
|
||||
|
||||
return PwgError.Success;
|
||||
}
|
||||
|
||||
private static string ExpandPattern(string strPattern)
|
||||
{
|
||||
if(strPattern == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
string str = strPattern;
|
||||
|
||||
while(true)
|
||||
{
|
||||
int nOpen = FindFirstUnescapedChar(str, '{');
|
||||
int nClose = FindFirstUnescapedChar(str, '}');
|
||||
|
||||
if((nOpen >= 0) && (nOpen < nClose))
|
||||
{
|
||||
string strCount = str.Substring(nOpen + 1, nClose - nOpen - 1);
|
||||
str = str.Remove(nOpen, nClose - nOpen + 1);
|
||||
|
||||
uint uRepeat;
|
||||
if(StrUtil.TryParseUInt(strCount, out uRepeat) && (nOpen >= 1))
|
||||
{
|
||||
if(uRepeat == 0)
|
||||
str = str.Remove(nOpen - 1, 1);
|
||||
else
|
||||
str = str.Insert(nOpen, new string(str[nOpen - 1], (int)uRepeat - 1));
|
||||
}
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private static int FindFirstUnescapedChar(string str, char ch)
|
||||
{
|
||||
for(int i = 0; i < str.Length; ++i)
|
||||
{
|
||||
char chCur = str[i];
|
||||
|
||||
if(chCur == '\\') ++i; // Next is escaped, skip it
|
||||
else if(chCur == ch) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
351
ModernKeePassLib/Cryptography/PasswordGenerator/PwCharSet.cs
Normal file
351
ModernKeePassLib/Cryptography/PasswordGenerator/PwCharSet.cs
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
public sealed class PwCharSet
|
||||
{
|
||||
public static readonly string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
public static readonly string LowerCase = "abcdefghijklmnopqrstuvwxyz";
|
||||
public static readonly string Digits = "0123456789";
|
||||
|
||||
public static readonly string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
|
||||
public static readonly string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
|
||||
public static readonly string UpperVowels = "AEIOU";
|
||||
public static readonly string LowerVowels = "aeiou";
|
||||
|
||||
public static readonly string Punctuation = @",.;:";
|
||||
public static readonly string Brackets = @"[]{}()<>";
|
||||
|
||||
public static readonly string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
||||
|
||||
public static readonly string UpperHex = "0123456789ABCDEF";
|
||||
public static readonly string LowerHex = "0123456789abcdef";
|
||||
|
||||
public static readonly string Invalid = "\t\r\n";
|
||||
public static readonly string LookAlike = @"O0l1I|";
|
||||
|
||||
internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
|
||||
|
||||
private const int CharTabSize = 0x10000 / 8;
|
||||
|
||||
private List<char> m_vChars = new List<char>();
|
||||
private byte[] m_vTab = new byte[CharTabSize];
|
||||
|
||||
private static string m_strHighAnsi = null;
|
||||
public static string HighAnsiChars
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_strHighAnsi == null) { new PwCharSet(); } // Create string
|
||||
Debug.Assert(m_strHighAnsi != null);
|
||||
return m_strHighAnsi;
|
||||
}
|
||||
}
|
||||
|
||||
private static string m_strSpecial = null;
|
||||
public static string SpecialChars
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_strSpecial == null) { new PwCharSet(); } // Create string
|
||||
Debug.Assert(m_strSpecial != null);
|
||||
return m_strSpecial;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty character set collection object.
|
||||
/// </summary>
|
||||
public PwCharSet()
|
||||
{
|
||||
Initialize(true);
|
||||
}
|
||||
|
||||
public PwCharSet(string strCharSet)
|
||||
{
|
||||
Initialize(true);
|
||||
Add(strCharSet);
|
||||
}
|
||||
|
||||
private PwCharSet(bool bFullInitialize)
|
||||
{
|
||||
Initialize(bFullInitialize);
|
||||
}
|
||||
|
||||
private void Initialize(bool bFullInitialize)
|
||||
{
|
||||
Clear();
|
||||
|
||||
if(!bFullInitialize) return;
|
||||
|
||||
if(m_strHighAnsi == null)
|
||||
{
|
||||
StringBuilder sbHighAnsi = new StringBuilder();
|
||||
// [U+0080, U+009F] are C1 control characters,
|
||||
// U+00A0 is non-breaking space
|
||||
for(char ch = '\u00A1'; ch <= '\u00AC'; ++ch)
|
||||
sbHighAnsi.Append(ch);
|
||||
// U+00AD is soft hyphen (format character)
|
||||
for(char ch = '\u00AE'; ch < '\u00FF'; ++ch)
|
||||
sbHighAnsi.Append(ch);
|
||||
sbHighAnsi.Append('\u00FF');
|
||||
|
||||
m_strHighAnsi = sbHighAnsi.ToString();
|
||||
}
|
||||
|
||||
if(m_strSpecial == null)
|
||||
{
|
||||
PwCharSet pcs = new PwCharSet(false);
|
||||
pcs.AddRange('!', '/');
|
||||
pcs.AddRange(':', '@');
|
||||
pcs.AddRange('[', '`');
|
||||
pcs.Add(@"|~");
|
||||
pcs.Remove(@"-_ ");
|
||||
pcs.Remove(PwCharSet.Brackets);
|
||||
|
||||
m_strSpecial = pcs.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of characters in this set.
|
||||
/// </summary>
|
||||
public uint Size
|
||||
{
|
||||
get { return (uint)m_vChars.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a character of the set using an index.
|
||||
/// </summary>
|
||||
/// <param name="uPos">Index of the character to get.</param>
|
||||
/// <returns>Character at the specified position. If the index is invalid,
|
||||
/// an <c>ArgumentOutOfRangeException</c> is thrown.</returns>
|
||||
public char this[uint uPos]
|
||||
{
|
||||
get
|
||||
{
|
||||
if(uPos >= (uint)m_vChars.Count)
|
||||
throw new ArgumentOutOfRangeException("uPos");
|
||||
|
||||
return m_vChars[(int)uPos];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all characters from this set.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_vChars.Clear();
|
||||
Array.Clear(m_vTab, 0, m_vTab.Length);
|
||||
}
|
||||
|
||||
public bool Contains(char ch)
|
||||
{
|
||||
return (((m_vTab[ch / 8] >> (ch % 8)) & 1) != char.MinValue);
|
||||
}
|
||||
|
||||
public bool Contains(string strCharacters)
|
||||
{
|
||||
Debug.Assert(strCharacters != null);
|
||||
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
|
||||
|
||||
foreach(char ch in strCharacters)
|
||||
{
|
||||
if(!Contains(ch)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add characters to the set.
|
||||
/// </summary>
|
||||
/// <param name="ch">Character to add.</param>
|
||||
public void Add(char ch)
|
||||
{
|
||||
if(ch == char.MinValue) { Debug.Assert(false); return; }
|
||||
|
||||
if(!Contains(ch))
|
||||
{
|
||||
m_vChars.Add(ch);
|
||||
m_vTab[ch / 8] |= (byte)(1 << (ch % 8));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add characters to the set.
|
||||
/// </summary>
|
||||
/// <param name="strCharSet">String containing characters to add.</param>
|
||||
public void Add(string strCharSet)
|
||||
{
|
||||
Debug.Assert(strCharSet != null);
|
||||
if(strCharSet == null) throw new ArgumentNullException("strCharSet");
|
||||
|
||||
m_vChars.Capacity = m_vChars.Count + strCharSet.Length;
|
||||
|
||||
foreach(char ch in strCharSet)
|
||||
Add(ch);
|
||||
}
|
||||
|
||||
public void Add(string strCharSet1, string strCharSet2)
|
||||
{
|
||||
Add(strCharSet1);
|
||||
Add(strCharSet2);
|
||||
}
|
||||
|
||||
public void Add(string strCharSet1, string strCharSet2, string strCharSet3)
|
||||
{
|
||||
Add(strCharSet1);
|
||||
Add(strCharSet2);
|
||||
Add(strCharSet3);
|
||||
}
|
||||
|
||||
public void AddRange(char chMin, char chMax)
|
||||
{
|
||||
m_vChars.Capacity = m_vChars.Count + (chMax - chMin) + 1;
|
||||
|
||||
for(char ch = chMin; ch < chMax; ++ch)
|
||||
Add(ch);
|
||||
|
||||
Add(chMax);
|
||||
}
|
||||
|
||||
public bool AddCharSet(char chCharSetIdentifier)
|
||||
{
|
||||
bool bResult = true;
|
||||
|
||||
switch(chCharSetIdentifier)
|
||||
{
|
||||
case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break;
|
||||
case 'A': Add(PwCharSet.LowerCase, PwCharSet.UpperCase,
|
||||
PwCharSet.Digits); break;
|
||||
case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break;
|
||||
case 'c': Add(PwCharSet.LowerConsonants); break;
|
||||
case 'C': Add(PwCharSet.LowerConsonants,
|
||||
PwCharSet.UpperConsonants); break;
|
||||
case 'z': Add(PwCharSet.UpperConsonants); break;
|
||||
case 'd': Add(PwCharSet.Digits); break; // Digit
|
||||
case 'h': Add(PwCharSet.LowerHex); break;
|
||||
case 'H': Add(PwCharSet.UpperHex); break;
|
||||
case 'l': Add(PwCharSet.LowerCase); break;
|
||||
case 'L': Add(PwCharSet.LowerCase, PwCharSet.UpperCase); break;
|
||||
case 'u': Add(PwCharSet.UpperCase); break;
|
||||
case 'p': Add(PwCharSet.Punctuation); break;
|
||||
case 'b': Add(PwCharSet.Brackets); break;
|
||||
case 's': Add(PwCharSet.PrintableAsciiSpecial); break;
|
||||
case 'S': Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
|
||||
Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break;
|
||||
case 'v': Add(PwCharSet.LowerVowels); break;
|
||||
case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break;
|
||||
case 'Z': Add(PwCharSet.UpperVowels); break;
|
||||
case 'x': Add(m_strHighAnsi); break;
|
||||
default: bResult = false; break;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
public bool Remove(char ch)
|
||||
{
|
||||
m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8)));
|
||||
return m_vChars.Remove(ch);
|
||||
}
|
||||
|
||||
public bool Remove(string strCharacters)
|
||||
{
|
||||
Debug.Assert(strCharacters != null);
|
||||
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
|
||||
|
||||
bool bResult = true;
|
||||
foreach(char ch in strCharacters)
|
||||
{
|
||||
if(!Remove(ch)) bResult = false;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
public bool RemoveIfAllExist(string strCharacters)
|
||||
{
|
||||
Debug.Assert(strCharacters != null);
|
||||
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
|
||||
|
||||
if(!Contains(strCharacters))
|
||||
return false;
|
||||
|
||||
return Remove(strCharacters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the character set to a string containing all its characters.
|
||||
/// </summary>
|
||||
/// <returns>String containing all character set characters.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach(char ch in m_vChars)
|
||||
sb.Append(ch);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string PackAndRemoveCharRanges()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_');
|
||||
sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_');
|
||||
sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_');
|
||||
sb.Append(RemoveIfAllExist(m_strSpecial) ? 'S' : '_');
|
||||
sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_');
|
||||
sb.Append(RemoveIfAllExist(@"-") ? 'm' : '_');
|
||||
sb.Append(RemoveIfAllExist(@"_") ? 'u' : '_');
|
||||
sb.Append(RemoveIfAllExist(@" ") ? 's' : '_');
|
||||
sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_');
|
||||
sb.Append(RemoveIfAllExist(m_strHighAnsi) ? 'H' : '_');
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void UnpackCharRanges(string strRanges)
|
||||
{
|
||||
if(strRanges == null) { Debug.Assert(false); return; }
|
||||
if(strRanges.Length < 10) { Debug.Assert(false); return; }
|
||||
|
||||
if(strRanges[0] != '_') Add(PwCharSet.UpperCase);
|
||||
if(strRanges[1] != '_') Add(PwCharSet.LowerCase);
|
||||
if(strRanges[2] != '_') Add(PwCharSet.Digits);
|
||||
if(strRanges[3] != '_') Add(m_strSpecial);
|
||||
if(strRanges[4] != '_') Add(PwCharSet.Punctuation);
|
||||
if(strRanges[5] != '_') Add('-');
|
||||
if(strRanges[6] != '_') Add('_');
|
||||
if(strRanges[7] != '_') Add(' ');
|
||||
if(strRanges[8] != '_') Add(PwCharSet.Brackets);
|
||||
if(strRanges[9] != '_') Add(m_strHighAnsi);
|
||||
}
|
||||
}
|
||||
}
|
||||
163
ModernKeePassLib/Cryptography/PasswordGenerator/PwGenerator.cs
Normal file
163
ModernKeePassLib/Cryptography/PasswordGenerator/PwGenerator.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
public enum PwgError
|
||||
{
|
||||
Success = 0,
|
||||
Unknown = 1,
|
||||
TooFewCharacters = 2,
|
||||
UnknownAlgorithm = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility functions for generating random passwords.
|
||||
/// </summary>
|
||||
public static class PwGenerator
|
||||
{
|
||||
public static PwgError Generate(out ProtectedString psOut,
|
||||
PwProfile pwProfile, byte[] pbUserEntropy,
|
||||
CustomPwGeneratorPool pwAlgorithmPool)
|
||||
{
|
||||
Debug.Assert(pwProfile != null);
|
||||
if(pwProfile == null) throw new ArgumentNullException("pwProfile");
|
||||
|
||||
PwgError e = PwgError.Unknown;
|
||||
CryptoRandomStream crs = null;
|
||||
byte[] pbKey = null;
|
||||
try
|
||||
{
|
||||
crs = CreateRandomStream(pbUserEntropy, out pbKey);
|
||||
|
||||
if(pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
|
||||
e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
|
||||
else if(pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
|
||||
e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
|
||||
else if(pwProfile.GeneratorType == PasswordGeneratorType.Custom)
|
||||
e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
|
||||
else { Debug.Assert(false); psOut = ProtectedString.Empty; }
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(crs != null) crs.Dispose();
|
||||
if(pbKey != null) MemUtil.ZeroByteArray(pbKey);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
private static CryptoRandomStream CreateRandomStream(byte[] pbAdditionalEntropy,
|
||||
out byte[] pbKey)
|
||||
{
|
||||
pbKey = CryptoRandom.Instance.GetRandomBytes(128);
|
||||
|
||||
// Mix in additional entropy
|
||||
Debug.Assert(pbKey.Length >= 64);
|
||||
if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
|
||||
{
|
||||
using(SHA512Managed h = new SHA512Managed())
|
||||
{
|
||||
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
|
||||
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
|
||||
}
|
||||
}
|
||||
|
||||
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
|
||||
}
|
||||
|
||||
internal static char GenerateCharacter(PwProfile pwProfile,
|
||||
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
if(pwCharSet.Size == 0) return char.MinValue;
|
||||
|
||||
ulong uIndex = crsRandomSource.GetRandomUInt64();
|
||||
uIndex %= (ulong)pwCharSet.Size;
|
||||
|
||||
char ch = pwCharSet[(uint)uIndex];
|
||||
|
||||
if(pwProfile.NoRepeatingCharacters)
|
||||
pwCharSet.Remove(ch);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
|
||||
{
|
||||
pwCharSet.Remove(PwCharSet.Invalid);
|
||||
|
||||
if(pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
|
||||
|
||||
if(pwProfile.ExcludeCharacters.Length > 0)
|
||||
pwCharSet.Remove(pwProfile.ExcludeCharacters);
|
||||
}
|
||||
|
||||
internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
if(v == null) { Debug.Assert(false); return; }
|
||||
if(crsRandomSource == null) { Debug.Assert(false); return; }
|
||||
|
||||
for(int i = v.Length - 1; i >= 1; --i)
|
||||
{
|
||||
ulong r = crsRandomSource.GetRandomUInt64();
|
||||
int j = (int)(r % (ulong)(i + 1));
|
||||
|
||||
char t = v[i];
|
||||
v[i] = v[j];
|
||||
v[j] = t;
|
||||
}
|
||||
}
|
||||
|
||||
private static PwgError GenerateCustom(out ProtectedString psOut,
|
||||
PwProfile pwProfile, CryptoRandomStream crs,
|
||||
CustomPwGeneratorPool pwAlgorithmPool)
|
||||
{
|
||||
psOut = ProtectedString.Empty;
|
||||
|
||||
Debug.Assert(pwProfile.GeneratorType == PasswordGeneratorType.Custom);
|
||||
if(pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
|
||||
|
||||
string strID = pwProfile.CustomAlgorithmUuid;
|
||||
if(string.IsNullOrEmpty(strID)) return PwgError.UnknownAlgorithm;
|
||||
|
||||
byte[] pbUuid = Convert.FromBase64String(strID);
|
||||
PwUuid uuid = new PwUuid(pbUuid);
|
||||
CustomPwGenerator pwg = pwAlgorithmPool.Find(uuid);
|
||||
if(pwg == null) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
|
||||
|
||||
ProtectedString pwd = pwg.Generate(pwProfile.CloneDeep(), crs);
|
||||
if(pwd == null) return PwgError.Unknown;
|
||||
|
||||
psOut = pwd;
|
||||
return PwgError.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
276
ModernKeePassLib/Cryptography/PasswordGenerator/PwProfile.cs
Normal file
276
ModernKeePassLib/Cryptography/PasswordGenerator/PwProfile.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
using System.Xml.Serialization;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography.PasswordGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the password generator. Different types like generators
|
||||
/// based on given patterns, based on character sets, etc. are
|
||||
/// available.
|
||||
/// </summary>
|
||||
public enum PasswordGeneratorType
|
||||
{
|
||||
/// <summary>
|
||||
/// Generator based on character spaces/sets, i.e. groups
|
||||
/// of characters like lower-case, upper-case or numeric characters.
|
||||
/// </summary>
|
||||
CharSet = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Password generation based on a pattern. The user has provided
|
||||
/// a pattern, which describes how the generated password has to
|
||||
/// look like.
|
||||
/// </summary>
|
||||
Pattern = 1,
|
||||
|
||||
Custom = 2
|
||||
}
|
||||
|
||||
public sealed class PwProfile : IDeepCloneable<PwProfile>
|
||||
{
|
||||
private string m_strName = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string Name
|
||||
{
|
||||
get { return m_strName; }
|
||||
set { m_strName = value; }
|
||||
}
|
||||
|
||||
private PasswordGeneratorType m_type = PasswordGeneratorType.CharSet;
|
||||
public PasswordGeneratorType GeneratorType
|
||||
{
|
||||
get { return m_type; }
|
||||
set { m_type = value; }
|
||||
}
|
||||
|
||||
private bool m_bUserEntropy = false;
|
||||
[DefaultValue(false)]
|
||||
public bool CollectUserEntropy
|
||||
{
|
||||
get { return m_bUserEntropy; }
|
||||
set { m_bUserEntropy = value; }
|
||||
}
|
||||
|
||||
private uint m_uLength = 20;
|
||||
public uint Length
|
||||
{
|
||||
get { return m_uLength; }
|
||||
set { m_uLength = value; }
|
||||
}
|
||||
|
||||
private PwCharSet m_pwCharSet = new PwCharSet(PwCharSet.UpperCase +
|
||||
PwCharSet.LowerCase + PwCharSet.Digits);
|
||||
[XmlIgnore]
|
||||
public PwCharSet CharSet
|
||||
{
|
||||
get { return m_pwCharSet; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_pwCharSet = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strCharSetRanges = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string CharSetRanges
|
||||
{
|
||||
get { this.UpdateCharSet(true); return m_strCharSetRanges; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strCharSetRanges = value;
|
||||
this.UpdateCharSet(false);
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strCharSetAdditional = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string CharSetAdditional
|
||||
{
|
||||
get { this.UpdateCharSet(true); return m_strCharSetAdditional; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strCharSetAdditional = value;
|
||||
this.UpdateCharSet(false);
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strPattern = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string Pattern
|
||||
{
|
||||
get { return m_strPattern; }
|
||||
set { m_strPattern = value; }
|
||||
}
|
||||
|
||||
private bool m_bPatternPermute = false;
|
||||
[DefaultValue(false)]
|
||||
public bool PatternPermutePassword
|
||||
{
|
||||
get { return m_bPatternPermute; }
|
||||
set { m_bPatternPermute = value; }
|
||||
}
|
||||
|
||||
private bool m_bNoLookAlike = false;
|
||||
[DefaultValue(false)]
|
||||
public bool ExcludeLookAlike
|
||||
{
|
||||
get { return m_bNoLookAlike; }
|
||||
set { m_bNoLookAlike = value; }
|
||||
}
|
||||
|
||||
private bool m_bNoRepeat = false;
|
||||
[DefaultValue(false)]
|
||||
public bool NoRepeatingCharacters
|
||||
{
|
||||
get { return m_bNoRepeat; }
|
||||
set { m_bNoRepeat = value; }
|
||||
}
|
||||
|
||||
private string m_strExclude = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string ExcludeCharacters
|
||||
{
|
||||
get { return m_strExclude; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strExclude = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strCustomID = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string CustomAlgorithmUuid
|
||||
{
|
||||
get { return m_strCustomID; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strCustomID = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strCustomOpt = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string CustomAlgorithmOptions
|
||||
{
|
||||
get { return m_strCustomOpt; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strCustomOpt = value;
|
||||
}
|
||||
}
|
||||
|
||||
public PwProfile()
|
||||
{
|
||||
}
|
||||
|
||||
public PwProfile CloneDeep()
|
||||
{
|
||||
PwProfile p = new PwProfile();
|
||||
|
||||
p.m_strName = m_strName;
|
||||
p.m_type = m_type;
|
||||
p.m_bUserEntropy = m_bUserEntropy;
|
||||
p.m_uLength = m_uLength;
|
||||
p.m_pwCharSet = new PwCharSet(m_pwCharSet.ToString());
|
||||
p.m_strCharSetRanges = m_strCharSetRanges;
|
||||
p.m_strCharSetAdditional = m_strCharSetAdditional;
|
||||
p.m_strPattern = m_strPattern;
|
||||
p.m_bPatternPermute = m_bPatternPermute;
|
||||
p.m_bNoLookAlike = m_bNoLookAlike;
|
||||
p.m_bNoRepeat = m_bNoRepeat;
|
||||
p.m_strExclude = m_strExclude;
|
||||
p.m_strCustomID = m_strCustomID;
|
||||
p.m_strCustomOpt = m_strCustomOpt;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private void UpdateCharSet(bool bSetXml)
|
||||
{
|
||||
if(bSetXml)
|
||||
{
|
||||
PwCharSet pcs = new PwCharSet(m_pwCharSet.ToString());
|
||||
m_strCharSetRanges = pcs.PackAndRemoveCharRanges();
|
||||
m_strCharSetAdditional = pcs.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
PwCharSet pcs = new PwCharSet(m_strCharSetAdditional);
|
||||
pcs.UnpackCharRanges(m_strCharSetRanges);
|
||||
m_pwCharSet = pcs;
|
||||
}
|
||||
}
|
||||
|
||||
public static PwProfile DeriveFromPassword(ProtectedString psPassword)
|
||||
{
|
||||
PwProfile pp = new PwProfile();
|
||||
Debug.Assert(psPassword != null); if(psPassword == null) return pp;
|
||||
|
||||
char[] vChars = psPassword.ReadChars();
|
||||
|
||||
pp.GeneratorType = PasswordGeneratorType.CharSet;
|
||||
pp.Length = (uint)vChars.Length;
|
||||
|
||||
PwCharSet pcs = pp.CharSet;
|
||||
pcs.Clear();
|
||||
|
||||
foreach(char ch in vChars)
|
||||
{
|
||||
if((ch >= 'A') && (ch <= 'Z')) pcs.Add(PwCharSet.UpperCase);
|
||||
else if((ch >= 'a') && (ch <= 'z')) pcs.Add(PwCharSet.LowerCase);
|
||||
else if((ch >= '0') && (ch <= '9')) pcs.Add(PwCharSet.Digits);
|
||||
else if(PwCharSet.SpecialChars.IndexOf(ch) >= 0)
|
||||
pcs.Add(PwCharSet.SpecialChars);
|
||||
else if(ch == ' ') pcs.Add(' ');
|
||||
else if(ch == '-') pcs.Add('-');
|
||||
else if(ch == '_') pcs.Add('_');
|
||||
else if(PwCharSet.Brackets.IndexOf(ch) >= 0)
|
||||
pcs.Add(PwCharSet.Brackets);
|
||||
else if(PwCharSet.HighAnsiChars.IndexOf(ch) >= 0)
|
||||
pcs.Add(PwCharSet.HighAnsiChars);
|
||||
else pcs.Add(ch);
|
||||
}
|
||||
|
||||
MemUtil.ZeroArray<char>(vChars);
|
||||
return pp;
|
||||
}
|
||||
|
||||
public bool HasSecurityReducingOption()
|
||||
{
|
||||
return (m_bNoLookAlike || m_bNoRepeat || (m_strExclude.Length > 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
133
ModernKeePassLib/Cryptography/PopularPasswords.cs
Normal file
133
ModernKeePassLib/Cryptography/PopularPasswords.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public static class PopularPasswords
|
||||
{
|
||||
private static Dictionary<int, Dictionary<char[], bool>> m_dicts =
|
||||
new Dictionary<int, Dictionary<char[], bool>>();
|
||||
|
||||
internal static int MaxLength
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(m_dicts.Count > 0); // Should be initialized
|
||||
|
||||
int iMaxLen = 0;
|
||||
foreach(int iLen in m_dicts.Keys)
|
||||
{
|
||||
if(iLen > iMaxLen) iMaxLen = iLen;
|
||||
}
|
||||
|
||||
return iMaxLen;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool ContainsLength(int nLength)
|
||||
{
|
||||
Dictionary<char[], bool> dDummy;
|
||||
return m_dicts.TryGetValue(nLength, out dDummy);
|
||||
}
|
||||
|
||||
public static bool IsPopularPassword(char[] vPassword)
|
||||
{
|
||||
ulong uDummy;
|
||||
return IsPopularPassword(vPassword, out uDummy);
|
||||
}
|
||||
|
||||
public static bool IsPopularPassword(char[] vPassword, out ulong uDictSize)
|
||||
{
|
||||
if(vPassword == null) throw new ArgumentNullException("vPassword");
|
||||
if(vPassword.Length == 0) { uDictSize = 0; return false; }
|
||||
|
||||
#if DEBUG
|
||||
Array.ForEach(vPassword, ch => Debug.Assert(ch == char.ToLower(ch)));
|
||||
#endif
|
||||
|
||||
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
uDictSize = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsPopularPasswordPriv(char[] vPassword, out ulong uDictSize)
|
||||
{
|
||||
Debug.Assert(m_dicts.Count > 0); // Should be initialized with data
|
||||
|
||||
Dictionary<char[], bool> d;
|
||||
if(!m_dicts.TryGetValue(vPassword.Length, out d))
|
||||
{
|
||||
uDictSize = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
uDictSize = (ulong)d.Count;
|
||||
return d.ContainsKey(vPassword);
|
||||
}
|
||||
|
||||
public static void Add(byte[] pbData, bool bGZipped)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(bGZipped)
|
||||
pbData = MemUtil.Decompress(pbData);
|
||||
|
||||
string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length);
|
||||
if(string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i = 0; i <= strData.Length; ++i)
|
||||
{
|
||||
char ch = ((i == strData.Length) ? ' ' : strData[i]);
|
||||
|
||||
if(char.IsWhiteSpace(ch))
|
||||
{
|
||||
int cc = sb.Length;
|
||||
if(cc > 0)
|
||||
{
|
||||
char[] vWord = new char[cc];
|
||||
sb.CopyTo(0, vWord, 0, cc);
|
||||
|
||||
Dictionary<char[], bool> d;
|
||||
if(!m_dicts.TryGetValue(cc, out d))
|
||||
{
|
||||
d = new Dictionary<char[], bool>(MemUtil.ArrayHelperExOfChar);
|
||||
m_dicts[cc] = d;
|
||||
}
|
||||
|
||||
d[vWord] = true;
|
||||
sb.Remove(0, cc);
|
||||
}
|
||||
}
|
||||
else sb.Append(char.ToLower(ch));
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
19
ModernKeePassLib/Cryptography/ProtectedData.cs
Normal file
19
ModernKeePassLib/Cryptography/ProtectedData.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using ModernKeePassLib.Native;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
public static class ProtectedData
|
||||
{
|
||||
public static byte[] Unprotect(byte[] pbEnc, byte[] mPbOptEnt, DataProtectionScope currentUser)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static byte[] Protect(byte[] pbPlain, byte[] mPbOptEnt, DataProtectionScope currentUser)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
779
ModernKeePassLib/Cryptography/QualityEstimation.cs
Normal file
779
ModernKeePassLib/Cryptography/QualityEstimation.cs
Normal file
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Cryptography.PasswordGenerator;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// A class that offers static functions to estimate the quality of
|
||||
/// passwords.
|
||||
/// </summary>
|
||||
public static class QualityEstimation
|
||||
{
|
||||
private static class PatternID
|
||||
{
|
||||
internal const char LowerAlpha = 'L';
|
||||
internal const char UpperAlpha = 'U';
|
||||
internal const char Digit = 'D';
|
||||
internal const char Special = 'S';
|
||||
internal const char High = 'H';
|
||||
internal const char Other = 'X';
|
||||
|
||||
internal const char Dictionary = 'W';
|
||||
internal const char Repetition = 'R';
|
||||
internal const char Number = 'N';
|
||||
internal const char DiffSeq = 'C';
|
||||
|
||||
internal const string All = "LUDSHXWRNC";
|
||||
}
|
||||
|
||||
// private static class CharDistrib
|
||||
// {
|
||||
// public static readonly ulong[] LowerAlpha = new ulong[26] {
|
||||
// 884, 211, 262, 249, 722, 98, 172, 234, 556, 124, 201, 447, 321,
|
||||
// 483, 518, 167, 18, 458, 416, 344, 231, 105, 80, 48, 238, 76
|
||||
// };
|
||||
// public static readonly ulong[] UpperAlpha = new ulong[26] {
|
||||
// 605, 188, 209, 200, 460, 81, 130, 163, 357, 122, 144, 332, 260,
|
||||
// 317, 330, 132, 18, 320, 315, 250, 137, 76, 60, 36, 161, 54
|
||||
// };
|
||||
// public static readonly ulong[] Digit = new ulong[10] {
|
||||
// 574, 673, 524, 377, 339, 336, 312, 310, 357, 386
|
||||
// };
|
||||
// }
|
||||
|
||||
private sealed class QeCharType
|
||||
{
|
||||
private readonly char m_chTypeID;
|
||||
public char TypeID { get { return m_chTypeID; } }
|
||||
|
||||
private readonly string m_strAlph;
|
||||
public string Alphabet { get { return m_strAlph; } }
|
||||
|
||||
private readonly int m_nChars;
|
||||
public int CharCount { get { return m_nChars; } }
|
||||
|
||||
private readonly char m_chFirst;
|
||||
private readonly char m_chLast;
|
||||
|
||||
private readonly double m_dblCharSize;
|
||||
public double CharSize { get { return m_dblCharSize; } }
|
||||
|
||||
public QeCharType(char chTypeID, string strAlphabet, bool bIsConsecutive)
|
||||
{
|
||||
if(strAlphabet == null) throw new ArgumentNullException();
|
||||
if(strAlphabet.Length == 0) throw new ArgumentException();
|
||||
|
||||
m_chTypeID = chTypeID;
|
||||
m_strAlph = strAlphabet;
|
||||
m_nChars = m_strAlph.Length;
|
||||
m_chFirst = (bIsConsecutive ? m_strAlph[0] : char.MinValue);
|
||||
m_chLast = (bIsConsecutive ? m_strAlph[m_nChars - 1] : char.MinValue);
|
||||
|
||||
m_dblCharSize = Log2(m_nChars);
|
||||
|
||||
Debug.Assert(((int)(m_chLast - m_chFirst) == (m_nChars - 1)) ||
|
||||
!bIsConsecutive);
|
||||
}
|
||||
|
||||
public QeCharType(char chTypeID, int nChars) // Catch-none set
|
||||
{
|
||||
if(nChars <= 0) throw new ArgumentOutOfRangeException();
|
||||
|
||||
m_chTypeID = chTypeID;
|
||||
m_strAlph = string.Empty;
|
||||
m_nChars = nChars;
|
||||
m_chFirst = char.MinValue;
|
||||
m_chLast = char.MinValue;
|
||||
|
||||
m_dblCharSize = Log2(m_nChars);
|
||||
}
|
||||
|
||||
public bool Contains(char ch)
|
||||
{
|
||||
if(m_chLast != char.MinValue)
|
||||
return ((ch >= m_chFirst) && (ch <= m_chLast));
|
||||
|
||||
Debug.Assert(m_strAlph.Length > 0); // Don't call for catch-none set
|
||||
return (m_strAlph.IndexOf(ch) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EntropyEncoder
|
||||
{
|
||||
private readonly string m_strAlph;
|
||||
private Dictionary<char, ulong> m_dHisto = new Dictionary<char, ulong>();
|
||||
private readonly ulong m_uBaseWeight;
|
||||
private readonly ulong m_uCharWeight;
|
||||
private readonly ulong m_uOccExclThreshold;
|
||||
|
||||
public EntropyEncoder(string strAlphabet, ulong uBaseWeight,
|
||||
ulong uCharWeight, ulong uOccExclThreshold)
|
||||
{
|
||||
if(strAlphabet == null) throw new ArgumentNullException();
|
||||
if(strAlphabet.Length == 0) throw new ArgumentException();
|
||||
|
||||
m_strAlph = strAlphabet;
|
||||
m_uBaseWeight = uBaseWeight;
|
||||
m_uCharWeight = uCharWeight;
|
||||
m_uOccExclThreshold = uOccExclThreshold;
|
||||
|
||||
#if DEBUG
|
||||
Dictionary<char, bool> d = new Dictionary<char, bool>();
|
||||
foreach(char ch in m_strAlph) { d[ch] = true; }
|
||||
Debug.Assert(d.Count == m_strAlph.Length); // No duplicates
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
m_dHisto.Clear();
|
||||
}
|
||||
|
||||
public void Write(char ch)
|
||||
{
|
||||
Debug.Assert(m_strAlph.IndexOf(ch) >= 0);
|
||||
|
||||
ulong uOcc;
|
||||
m_dHisto.TryGetValue(ch, out uOcc);
|
||||
Debug.Assert(m_dHisto.ContainsKey(ch) || (uOcc == 0));
|
||||
m_dHisto[ch] = uOcc + 1;
|
||||
}
|
||||
|
||||
public double GetOutputSize()
|
||||
{
|
||||
ulong uTotalWeight = m_uBaseWeight * (ulong)m_strAlph.Length;
|
||||
foreach(ulong u in m_dHisto.Values)
|
||||
{
|
||||
Debug.Assert(u >= 1);
|
||||
if(u > m_uOccExclThreshold)
|
||||
uTotalWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
|
||||
}
|
||||
|
||||
double dSize = 0.0, dTotalWeight = (double)uTotalWeight;
|
||||
foreach(ulong u in m_dHisto.Values)
|
||||
{
|
||||
ulong uWeight = m_uBaseWeight;
|
||||
if(u > m_uOccExclThreshold)
|
||||
uWeight += (u - m_uOccExclThreshold) * m_uCharWeight;
|
||||
|
||||
dSize -= (double)u * Log2((double)uWeight / dTotalWeight);
|
||||
}
|
||||
|
||||
return dSize;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MultiEntropyEncoder
|
||||
{
|
||||
private Dictionary<char, EntropyEncoder> m_dEncs =
|
||||
new Dictionary<char, EntropyEncoder>();
|
||||
|
||||
public MultiEntropyEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
public void AddEncoder(char chTypeID, EntropyEncoder ec)
|
||||
{
|
||||
if(ec == null) { Debug.Assert(false); return; }
|
||||
|
||||
Debug.Assert(!m_dEncs.ContainsKey(chTypeID));
|
||||
m_dEncs[chTypeID] = ec;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
foreach(EntropyEncoder ec in m_dEncs.Values) { ec.Reset(); }
|
||||
}
|
||||
|
||||
public bool Write(char chTypeID, char chData)
|
||||
{
|
||||
EntropyEncoder ec;
|
||||
if(!m_dEncs.TryGetValue(chTypeID, out ec))
|
||||
return false;
|
||||
|
||||
ec.Write(chData);
|
||||
return true;
|
||||
}
|
||||
|
||||
public double GetOutputSize()
|
||||
{
|
||||
double d = 0.0;
|
||||
|
||||
foreach(EntropyEncoder ec in m_dEncs.Values)
|
||||
{
|
||||
d += ec.GetOutputSize();
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class QePatternInstance
|
||||
{
|
||||
private readonly int m_iPos;
|
||||
public int Position { get { return m_iPos; } }
|
||||
|
||||
private readonly int m_nLen;
|
||||
public int Length { get { return m_nLen; } }
|
||||
|
||||
private readonly char m_chPatternID;
|
||||
public char PatternID { get { return m_chPatternID; } }
|
||||
|
||||
private readonly double m_dblCost;
|
||||
public double Cost { get { return m_dblCost; } }
|
||||
|
||||
private readonly QeCharType m_ctSingle;
|
||||
public QeCharType SingleCharType { get { return m_ctSingle; } }
|
||||
|
||||
public QePatternInstance(int iPosition, int nLength, char chPatternID,
|
||||
double dblCost)
|
||||
{
|
||||
m_iPos = iPosition;
|
||||
m_nLen = nLength;
|
||||
m_chPatternID = chPatternID;
|
||||
m_dblCost = dblCost;
|
||||
m_ctSingle = null;
|
||||
}
|
||||
|
||||
public QePatternInstance(int iPosition, int nLength, QeCharType ctSingle)
|
||||
{
|
||||
m_iPos = iPosition;
|
||||
m_nLen = nLength;
|
||||
m_chPatternID = ctSingle.TypeID;
|
||||
m_dblCost = ctSingle.CharSize;
|
||||
m_ctSingle = ctSingle;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class QePathState
|
||||
{
|
||||
public readonly int Position;
|
||||
public readonly List<QePatternInstance> Path;
|
||||
|
||||
public QePathState(int iPosition, List<QePatternInstance> lPath)
|
||||
{
|
||||
this.Position = iPosition;
|
||||
this.Path = lPath;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly object m_objSyncInit = new object();
|
||||
private static List<QeCharType> m_lCharTypes = null;
|
||||
|
||||
private static void EnsureInitialized()
|
||||
{
|
||||
lock(m_objSyncInit)
|
||||
{
|
||||
if(m_lCharTypes == null)
|
||||
{
|
||||
string strSpecial = PwCharSet.PrintableAsciiSpecial;
|
||||
if(strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); }
|
||||
else strSpecial = strSpecial + " ";
|
||||
|
||||
int nSp = strSpecial.Length;
|
||||
int nHi = PwCharSet.HighAnsiChars.Length;
|
||||
|
||||
m_lCharTypes = new List<QeCharType>();
|
||||
|
||||
m_lCharTypes.Add(new QeCharType(PatternID.LowerAlpha,
|
||||
PwCharSet.LowerCase, true));
|
||||
m_lCharTypes.Add(new QeCharType(PatternID.UpperAlpha,
|
||||
PwCharSet.UpperCase, true));
|
||||
m_lCharTypes.Add(new QeCharType(PatternID.Digit,
|
||||
PwCharSet.Digits, true));
|
||||
m_lCharTypes.Add(new QeCharType(PatternID.Special,
|
||||
strSpecial, false));
|
||||
m_lCharTypes.Add(new QeCharType(PatternID.High,
|
||||
PwCharSet.HighAnsiChars, false));
|
||||
m_lCharTypes.Add(new QeCharType(PatternID.Other,
|
||||
0x10000 - (2 * 26) - 10 - nSp - nHi));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Estimate the quality of a password.
|
||||
/// </summary>
|
||||
/// <param name="vPassword">Password to check.</param>
|
||||
/// <returns>Estimated bit-strength of the password.</returns>
|
||||
public static uint EstimatePasswordBits(char[] vPassword)
|
||||
{
|
||||
if(vPassword == null) { Debug.Assert(false); return 0; }
|
||||
if(vPassword.Length == 0) return 0;
|
||||
|
||||
EnsureInitialized();
|
||||
|
||||
int n = vPassword.Length;
|
||||
List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n];
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
vPatterns[i] = new List<QePatternInstance>();
|
||||
|
||||
QePatternInstance piChar = new QePatternInstance(i, 1,
|
||||
GetCharType(vPassword[i]));
|
||||
vPatterns[i].Add(piChar);
|
||||
}
|
||||
|
||||
FindRepetitions(vPassword, vPatterns);
|
||||
FindNumbers(vPassword, vPatterns);
|
||||
FindDiffSeqs(vPassword, vPatterns);
|
||||
FindPopularPasswords(vPassword, vPatterns);
|
||||
|
||||
// Encoders must not be static, because the entropy estimation
|
||||
// may run concurrently in multiple threads and the encoders are
|
||||
// not read-only
|
||||
EntropyEncoder ecPattern = new EntropyEncoder(PatternID.All, 0, 1, 0);
|
||||
MultiEntropyEncoder mcData = new MultiEntropyEncoder();
|
||||
for(int i = 0; i < (m_lCharTypes.Count - 1); ++i)
|
||||
{
|
||||
// Let m be the alphabet size. In order to ensure that two same
|
||||
// characters cost at least as much as a single character, for
|
||||
// the probability p and weight w of the character it must hold:
|
||||
// -log(1/m) >= -2*log(p)
|
||||
// <=> log(1/m) <= log(p^2) <=> 1/m <= p^2 <=> p >= sqrt(1/m);
|
||||
// sqrt(1/m) = (1+w)/(m+w)
|
||||
// <=> m+w = (1+w)*sqrt(m) <=> m+w = sqrt(m) + w*sqrt(m)
|
||||
// <=> w*(1-sqrt(m)) = sqrt(m) - m <=> w = (sqrt(m)-m)/(1-sqrt(m))
|
||||
// <=> w = (sqrt(m)-m)*(1+sqrt(m))/(1-m)
|
||||
// <=> w = (sqrt(m)-m+m-m*sqrt(m))/(1-m) <=> w = sqrt(m)
|
||||
ulong uw = (ulong)Math.Sqrt((double)m_lCharTypes[i].CharCount);
|
||||
|
||||
mcData.AddEncoder(m_lCharTypes[i].TypeID, new EntropyEncoder(
|
||||
m_lCharTypes[i].Alphabet, 1, uw, 1));
|
||||
}
|
||||
|
||||
double dblMinCost = (double)int.MaxValue;
|
||||
int tStart = Environment.TickCount;
|
||||
|
||||
Stack<QePathState> sRec = new Stack<QePathState>();
|
||||
sRec.Push(new QePathState(0, new List<QePatternInstance>()));
|
||||
while(sRec.Count > 0)
|
||||
{
|
||||
int tDiff = Environment.TickCount - tStart;
|
||||
if(tDiff > 500) break;
|
||||
|
||||
QePathState s = sRec.Pop();
|
||||
|
||||
if(s.Position >= n)
|
||||
{
|
||||
Debug.Assert(s.Position == n);
|
||||
|
||||
double dblCost = ComputePathCost(s.Path, vPassword,
|
||||
ecPattern, mcData);
|
||||
if(dblCost < dblMinCost) dblMinCost = dblCost;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<QePatternInstance> lSubs = vPatterns[s.Position];
|
||||
for(int i = lSubs.Count - 1; i >= 0; --i)
|
||||
{
|
||||
QePatternInstance pi = lSubs[i];
|
||||
Debug.Assert(pi.Position == s.Position);
|
||||
Debug.Assert(pi.Length >= 1);
|
||||
|
||||
List<QePatternInstance> lNewPath =
|
||||
new List<QePatternInstance>(s.Path.Count + 1);
|
||||
lNewPath.AddRange(s.Path);
|
||||
lNewPath.Add(pi);
|
||||
Debug.Assert(lNewPath.Capacity == (s.Path.Count + 1));
|
||||
|
||||
QePathState sNew = new QePathState(s.Position +
|
||||
pi.Length, lNewPath);
|
||||
sRec.Push(sNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (uint)Math.Ceiling(dblMinCost);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Estimate the quality of a password.
|
||||
/// </summary>
|
||||
/// <param name="pbUnprotectedUtf8">Password to check, UTF-8 encoded.</param>
|
||||
/// <returns>Estimated bit-strength of the password.</returns>
|
||||
public static uint EstimatePasswordBits(byte[] pbUnprotectedUtf8)
|
||||
{
|
||||
if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
|
||||
|
||||
char[] v = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
|
||||
uint r;
|
||||
try { r = EstimatePasswordBits(v); }
|
||||
finally { MemUtil.ZeroArray<char>(v); }
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private static QeCharType GetCharType(char ch)
|
||||
{
|
||||
int nTypes = m_lCharTypes.Count;
|
||||
Debug.Assert((nTypes > 0) && (m_lCharTypes[nTypes - 1].CharCount > 256));
|
||||
|
||||
for(int i = 0; i < (nTypes - 1); ++i)
|
||||
{
|
||||
if(m_lCharTypes[i].Contains(ch))
|
||||
return m_lCharTypes[i];
|
||||
}
|
||||
|
||||
return m_lCharTypes[nTypes - 1];
|
||||
}
|
||||
|
||||
private static double ComputePathCost(List<QePatternInstance> l,
|
||||
char[] vPassword, EntropyEncoder ecPattern, MultiEntropyEncoder mcData)
|
||||
{
|
||||
ecPattern.Reset();
|
||||
for(int i = 0; i < l.Count; ++i)
|
||||
ecPattern.Write(l[i].PatternID);
|
||||
double dblPatternCost = ecPattern.GetOutputSize();
|
||||
|
||||
mcData.Reset();
|
||||
double dblDataCost = 0.0;
|
||||
foreach(QePatternInstance pi in l)
|
||||
{
|
||||
QeCharType tChar = pi.SingleCharType;
|
||||
if(tChar != null)
|
||||
{
|
||||
char ch = vPassword[pi.Position];
|
||||
if(!mcData.Write(tChar.TypeID, ch))
|
||||
dblDataCost += pi.Cost;
|
||||
}
|
||||
else dblDataCost += pi.Cost;
|
||||
}
|
||||
dblDataCost += mcData.GetOutputSize();
|
||||
|
||||
return (dblPatternCost + dblDataCost);
|
||||
}
|
||||
|
||||
private static void FindPopularPasswords(char[] vPassword,
|
||||
List<QePatternInstance>[] vPatterns)
|
||||
{
|
||||
int n = vPassword.Length;
|
||||
|
||||
char[] vLower = new char[n];
|
||||
char[] vLeet = new char[n];
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
char ch = vPassword[i];
|
||||
|
||||
vLower[i] = char.ToLower(ch);
|
||||
vLeet[i] = char.ToLower(DecodeLeetChar(ch));
|
||||
}
|
||||
|
||||
char chErased = default(char); // The value that Array.Clear uses
|
||||
Debug.Assert(chErased == char.MinValue);
|
||||
|
||||
int nMaxLen = Math.Min(n, PopularPasswords.MaxLength);
|
||||
for(int nSubLen = nMaxLen; nSubLen >= 3; --nSubLen)
|
||||
{
|
||||
if(!PopularPasswords.ContainsLength(nSubLen)) continue;
|
||||
|
||||
char[] vSub = new char[nSubLen];
|
||||
|
||||
for(int i = 0; i <= (n - nSubLen); ++i)
|
||||
{
|
||||
if(Array.IndexOf<char>(vLower, chErased, i, nSubLen) >= 0)
|
||||
continue;
|
||||
|
||||
Array.Copy(vLower, i, vSub, 0, nSubLen);
|
||||
if(!EvalAddPopularPasswordPattern(vPatterns, vPassword,
|
||||
i, vSub, 0.0))
|
||||
{
|
||||
Array.Copy(vLeet, i, vSub, 0, nSubLen);
|
||||
if(EvalAddPopularPasswordPattern(vPatterns, vPassword,
|
||||
i, vSub, 1.5))
|
||||
{
|
||||
Array.Clear(vLower, i, nSubLen); // Not vLeet
|
||||
Debug.Assert(vLower[i] == chErased);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Clear(vLower, i, nSubLen);
|
||||
Debug.Assert(vLower[i] == chErased);
|
||||
}
|
||||
}
|
||||
|
||||
MemUtil.ZeroArray<char>(vSub);
|
||||
}
|
||||
|
||||
MemUtil.ZeroArray<char>(vLower);
|
||||
MemUtil.ZeroArray<char>(vLeet);
|
||||
}
|
||||
|
||||
private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns,
|
||||
char[] vPassword, int i, char[] vSub, double dblCostPerMod)
|
||||
{
|
||||
ulong uDictSize;
|
||||
if(!PopularPasswords.IsPopularPassword(vSub, out uDictSize))
|
||||
return false;
|
||||
|
||||
int n = vSub.Length;
|
||||
int d = HammingDist(vSub, 0, vPassword, i, n);
|
||||
|
||||
double dblCost = Log2((double)uDictSize);
|
||||
|
||||
// dblCost += log2(n binom d)
|
||||
int k = Math.Min(d, n - d);
|
||||
for(int j = n; j > (n - k); --j)
|
||||
dblCost += Log2(j);
|
||||
for(int j = k; j >= 2; --j)
|
||||
dblCost -= Log2(j);
|
||||
|
||||
dblCost += dblCostPerMod * (double)d;
|
||||
|
||||
vPatterns[i].Add(new QePatternInstance(i, n, PatternID.Dictionary,
|
||||
dblCost));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static char DecodeLeetChar(char chLeet)
|
||||
{
|
||||
if((chLeet >= '\u00C0') && (chLeet <= '\u00C6')) return 'a';
|
||||
if((chLeet >= '\u00C8') && (chLeet <= '\u00CB')) return 'e';
|
||||
if((chLeet >= '\u00CC') && (chLeet <= '\u00CF')) return 'i';
|
||||
if((chLeet >= '\u00D2') && (chLeet <= '\u00D6')) return 'o';
|
||||
if((chLeet >= '\u00D9') && (chLeet <= '\u00DC')) return 'u';
|
||||
if((chLeet >= '\u00E0') && (chLeet <= '\u00E6')) return 'a';
|
||||
if((chLeet >= '\u00E8') && (chLeet <= '\u00EB')) return 'e';
|
||||
if((chLeet >= '\u00EC') && (chLeet <= '\u00EF')) return 'i';
|
||||
if((chLeet >= '\u00F2') && (chLeet <= '\u00F6')) return 'o';
|
||||
if((chLeet >= '\u00F9') && (chLeet <= '\u00FC')) return 'u';
|
||||
|
||||
char ch;
|
||||
switch(chLeet)
|
||||
{
|
||||
case '4':
|
||||
case '@':
|
||||
case '?':
|
||||
case '^':
|
||||
case '\u00AA': ch = 'a'; break;
|
||||
case '8':
|
||||
case '\u00DF': ch = 'b'; break;
|
||||
case '(':
|
||||
case '{':
|
||||
case '[':
|
||||
case '<':
|
||||
case '\u00A2':
|
||||
case '\u00A9':
|
||||
case '\u00C7':
|
||||
case '\u00E7': ch = 'c'; break;
|
||||
case '\u00D0':
|
||||
case '\u00F0': ch = 'd'; break;
|
||||
case '3':
|
||||
case '\u20AC':
|
||||
case '&':
|
||||
case '\u00A3': ch = 'e'; break;
|
||||
case '6':
|
||||
case '9': ch = 'g'; break;
|
||||
case '#': ch = 'h'; break;
|
||||
case '1':
|
||||
case '!':
|
||||
case '|':
|
||||
case '\u00A1':
|
||||
case '\u00A6': ch = 'i'; break;
|
||||
case '\u00D1':
|
||||
case '\u00F1': ch = 'n'; break;
|
||||
case '0':
|
||||
case '*':
|
||||
case '\u00A4': // Currency
|
||||
case '\u00B0': // Degree
|
||||
case '\u00D8':
|
||||
case '\u00F8': ch = 'o'; break;
|
||||
case '\u00AE': ch = 'r'; break;
|
||||
case '$':
|
||||
case '5':
|
||||
case '\u00A7': ch = 's'; break;
|
||||
case '+':
|
||||
case '7': ch = 't'; break;
|
||||
case '\u00B5': ch = 'u'; break;
|
||||
case '%':
|
||||
case '\u00D7': ch = 'x'; break;
|
||||
case '\u00A5':
|
||||
case '\u00DD':
|
||||
case '\u00FD':
|
||||
case '\u00FF': ch = 'y'; break;
|
||||
case '2': ch = 'z'; break;
|
||||
default: ch = chLeet; break;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
private static int HammingDist(char[] v1, int iOffset1,
|
||||
char[] v2, int iOffset2, int nLength)
|
||||
{
|
||||
int nDist = 0;
|
||||
for(int i = 0; i < nLength; ++i)
|
||||
{
|
||||
if(v1[iOffset1 + i] != v2[iOffset2 + i]) ++nDist;
|
||||
}
|
||||
|
||||
return nDist;
|
||||
}
|
||||
|
||||
private static void FindRepetitions(char[] vPassword,
|
||||
List<QePatternInstance>[] vPatterns)
|
||||
{
|
||||
int n = vPassword.Length;
|
||||
char[] v = new char[n];
|
||||
Array.Copy(vPassword, v, n);
|
||||
|
||||
char chErased = char.MaxValue;
|
||||
for(int m = (n / 2); m >= 3; --m)
|
||||
{
|
||||
for(int x1 = 0; x1 <= (n - (2 * m)); ++x1)
|
||||
{
|
||||
bool bFoundRep = false;
|
||||
|
||||
for(int x2 = (x1 + m); x2 <= (n - m); ++x2)
|
||||
{
|
||||
if(PartsEqual(v, x1, x2, m))
|
||||
{
|
||||
double dblCost = Log2(x1 + 1) + Log2(m);
|
||||
vPatterns[x2].Add(new QePatternInstance(x2, m,
|
||||
PatternID.Repetition, dblCost));
|
||||
|
||||
ErasePart(v, x2, m, ref chErased);
|
||||
bFoundRep = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(bFoundRep) ErasePart(v, x1, m, ref chErased);
|
||||
}
|
||||
}
|
||||
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
}
|
||||
|
||||
private static bool PartsEqual(char[] v, int x1, int x2, int nLength)
|
||||
{
|
||||
for(int i = 0; i < nLength; ++i)
|
||||
{
|
||||
if(v[x1 + i] != v[x2 + i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ErasePart(char[] v, int i, int n, ref char chErased)
|
||||
{
|
||||
for(int j = 0; j < n; ++j)
|
||||
{
|
||||
v[i + j] = chErased;
|
||||
--chErased;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FindNumbers(char[] vPassword,
|
||||
List<QePatternInstance>[] vPatterns)
|
||||
{
|
||||
int n = vPassword.Length;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(int i = 0; i < n; ++i)
|
||||
{
|
||||
char ch = vPassword[i];
|
||||
if((ch >= '0') && (ch <= '9')) sb.Append(ch);
|
||||
else
|
||||
{
|
||||
AddNumberPattern(vPatterns, sb, i - sb.Length);
|
||||
sb.Remove(0, sb.Length);
|
||||
}
|
||||
}
|
||||
AddNumberPattern(vPatterns, sb, n - sb.Length);
|
||||
}
|
||||
|
||||
private static void AddNumberPattern(List<QePatternInstance>[] vPatterns,
|
||||
StringBuilder sb, int i)
|
||||
{
|
||||
if(sb.Length <= 2) return;
|
||||
string strNumber = sb.ToString();
|
||||
|
||||
int nZeros = 0;
|
||||
for(int j = 0; j < strNumber.Length; ++j)
|
||||
{
|
||||
if(strNumber[j] != '0') break;
|
||||
++nZeros;
|
||||
}
|
||||
|
||||
double dblCost = Log2(nZeros + 1);
|
||||
if(nZeros < strNumber.Length)
|
||||
{
|
||||
string strNonZero = strNumber.Substring(nZeros);
|
||||
|
||||
#if KeePassLibSD
|
||||
try { dblCost += Log2(double.Parse(strNonZero)); }
|
||||
catch(Exception) { Debug.Assert(false); return; }
|
||||
#else
|
||||
double d;
|
||||
if(double.TryParse(strNonZero, out d))
|
||||
dblCost += Log2(d);
|
||||
else { Debug.Assert(false); return; }
|
||||
#endif
|
||||
}
|
||||
|
||||
vPatterns[i].Add(new QePatternInstance(i, strNumber.Length,
|
||||
PatternID.Number, dblCost));
|
||||
}
|
||||
|
||||
private static void FindDiffSeqs(char[] vPassword,
|
||||
List<QePatternInstance>[] vPatterns)
|
||||
{
|
||||
int n = vPassword.Length;
|
||||
int d = int.MaxValue, p = 0;
|
||||
|
||||
for(int i = 1; i <= n; ++i)
|
||||
{
|
||||
int dCur = ((i == n) ? int.MinValue :
|
||||
((int)vPassword[i] - (int)vPassword[i - 1]));
|
||||
if(dCur != d)
|
||||
{
|
||||
if((i - p) >= 3) // At least 3 chars involved
|
||||
{
|
||||
QeCharType ct = GetCharType(vPassword[p]);
|
||||
double dblCost = ct.CharSize + Log2(i - p - 1);
|
||||
|
||||
vPatterns[p].Add(new QePatternInstance(p,
|
||||
i - p, PatternID.DiffSeq, dblCost));
|
||||
}
|
||||
|
||||
d = dCur;
|
||||
p = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double Log2(double dblValue)
|
||||
{
|
||||
#if KeePassLibSD
|
||||
return (Math.Log(dblValue) / Math.Log(2.0));
|
||||
#else
|
||||
return Math.Log(dblValue, 2.0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
1169
ModernKeePassLib/Cryptography/SelfTest.cs
Normal file
1169
ModernKeePassLib/Cryptography/SelfTest.cs
Normal file
File diff suppressed because it is too large
Load Diff
66
ModernKeePassLib/Delegates/Handlers.cs
Normal file
66
ModernKeePassLib/Delegates/Handlers.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Delegates
|
||||
{
|
||||
/// <summary>
|
||||
/// Function definition of a method that performs an action on a group.
|
||||
/// When traversing the internal tree, this function will be invoked
|
||||
/// for all visited groups.
|
||||
/// </summary>
|
||||
/// <param name="pg">Currently visited group.</param>
|
||||
/// <returns>You must return <c>true</c> if you want to continue the
|
||||
/// traversal. If you want to immediately stop the whole traversal,
|
||||
/// return <c>false</c>.</returns>
|
||||
public delegate bool GroupHandler(PwGroup pg);
|
||||
|
||||
/// <summary>
|
||||
/// Function definition of a method that performs an action on an entry.
|
||||
/// When traversing the internal tree, this function will be invoked
|
||||
/// for all visited entries.
|
||||
/// </summary>
|
||||
/// <param name="pe">Currently visited entry.</param>
|
||||
/// <returns>You must return <c>true</c> if you want to continue the
|
||||
/// traversal. If you want to immediately stop the whole traversal,
|
||||
/// return <c>false</c>.</returns>
|
||||
public delegate bool EntryHandler(PwEntry pe);
|
||||
|
||||
public delegate void VoidDelegate();
|
||||
public delegate string StrPwEntryDelegate(string str, PwEntry pe);
|
||||
|
||||
// Action<...> with 0 or >= 2 parameters has been introduced in .NET 3.5
|
||||
public delegate void GAction();
|
||||
public delegate void GAction<T>(T o);
|
||||
public delegate void GAction<T1, T2>(T1 o1, T2 o2);
|
||||
public delegate void GAction<T1, T2, T3>(T1 o1, T2 o2, T3 o3);
|
||||
public delegate void GAction<T1, T2, T3, T4>(T1 o1, T2 o2, T3 o3, T4 o4);
|
||||
public delegate void GAction<T1, T2, T3, T4, T5>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5);
|
||||
public delegate void GAction<T1, T2, T3, T4, T5, T6>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5, T6 o6);
|
||||
|
||||
// Func<...> has been introduced in .NET 3.5
|
||||
public delegate TResult GFunc<TResult>();
|
||||
public delegate TResult GFunc<T, TResult>(T o);
|
||||
public delegate TResult GFunc<T1, T2, TResult>(T1 o1, T2 o2);
|
||||
public delegate TResult GFunc<T1, T2, T3, TResult>(T1 o1, T2 o2, T3 o3);
|
||||
public delegate TResult GFunc<T1, T2, T3, T4, TResult>(T1 o1, T2 o2, T3 o3, T4 o4);
|
||||
public delegate TResult GFunc<T1, T2, T3, T4, T5, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5);
|
||||
public delegate TResult GFunc<T1, T2, T3, T4, T5, T6, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5, T6 o6);
|
||||
}
|
||||
37
ModernKeePassLib/Interfaces/IDeepCloneable.cs
Normal file
37
ModernKeePassLib/Interfaces/IDeepCloneable.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for objects that are deeply cloneable.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Reference type.</typeparam>
|
||||
public interface IDeepCloneable<T> where T : class
|
||||
{
|
||||
/// <summary>
|
||||
/// Deeply clone the object.
|
||||
/// </summary>
|
||||
/// <returns>Cloned object.</returns>
|
||||
T CloneDeep();
|
||||
}
|
||||
}
|
||||
105
ModernKeePassLib/Interfaces/IStatusLogger.cs
Normal file
105
ModernKeePassLib/Interfaces/IStatusLogger.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Status message types.
|
||||
/// </summary>
|
||||
public enum LogStatusType
|
||||
{
|
||||
/// <summary>
|
||||
/// Default type: simple information type.
|
||||
/// </summary>
|
||||
Info = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Warning message.
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// Error message.
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// Additional information. Depends on lines above.
|
||||
/// </summary>
|
||||
AdditionalInfo
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status logging interface.
|
||||
/// </summary>
|
||||
public interface IStatusLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Function which needs to be called when logging is started.
|
||||
/// </summary>
|
||||
/// <param name="strOperation">This string should roughly describe
|
||||
/// the operation, of which the status is logged.</param>
|
||||
/// <param name="bWriteOperationToLog">Specifies whether the
|
||||
/// operation is written to the log or not.</param>
|
||||
void StartLogging(string strOperation, bool bWriteOperationToLog);
|
||||
|
||||
/// <summary>
|
||||
/// Function which needs to be called when logging is ended
|
||||
/// (i.e. when no more messages will be logged and when the
|
||||
/// percent value won't change any more).
|
||||
/// </summary>
|
||||
void EndLogging();
|
||||
|
||||
/// <summary>
|
||||
/// Set the current progress in percent.
|
||||
/// </summary>
|
||||
/// <param name="uPercent">Percent of work finished.</param>
|
||||
/// <returns>Returns <c>true</c> if the caller should continue
|
||||
/// the current work.</returns>
|
||||
bool SetProgress(uint uPercent);
|
||||
|
||||
/// <summary>
|
||||
/// Set the current status text.
|
||||
/// </summary>
|
||||
/// <param name="strNewText">Status text.</param>
|
||||
/// <param name="lsType">Type of the message.</param>
|
||||
/// <returns>Returns <c>true</c> if the caller should continue
|
||||
/// the current work.</returns>
|
||||
bool SetText(string strNewText, LogStatusType lsType);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the user cancelled the current work.
|
||||
/// </summary>
|
||||
/// <returns>Returns <c>true</c> if the caller should continue
|
||||
/// the current work.</returns>
|
||||
bool ContinueWork();
|
||||
}
|
||||
|
||||
public sealed class NullStatusLogger : IStatusLogger
|
||||
{
|
||||
public void StartLogging(string strOperation, bool bWriteOperationToLog) { }
|
||||
public void EndLogging() { }
|
||||
public bool SetProgress(uint uPercent) { return true; }
|
||||
public bool SetText(string strNewText, LogStatusType lsType) { return true; }
|
||||
public bool ContinueWork() { return true; }
|
||||
}
|
||||
}
|
||||
37
ModernKeePassLib/Interfaces/IStructureItem.cs
Normal file
37
ModernKeePassLib/Interfaces/IStructureItem.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Interfaces
|
||||
{
|
||||
public interface IStructureItem : ITimeLogger // Provides LocationChanged
|
||||
{
|
||||
PwUuid Uuid
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
PwGroup ParentGroup
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
105
ModernKeePassLib/Interfaces/ITimeLogger.cs
Normal file
105
ModernKeePassLib/Interfaces/ITimeLogger.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for objects that support various times (creation time, last
|
||||
/// access time, last modification time and expiry time). Offers
|
||||
/// several helper functions (for example a function to touch the current
|
||||
/// object).
|
||||
/// </summary>
|
||||
public interface ITimeLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// The date/time when the object was created.
|
||||
/// </summary>
|
||||
DateTime CreationTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when the object was last modified.
|
||||
/// </summary>
|
||||
DateTime LastModificationTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when the object was last accessed.
|
||||
/// </summary>
|
||||
DateTime LastAccessTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when the object expires.
|
||||
/// </summary>
|
||||
DateTime ExpiryTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flag that determines if the object does expire.
|
||||
/// </summary>
|
||||
bool Expires
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the usage count of the object. To increase the usage
|
||||
/// count by one, use the <c>Touch</c> function.
|
||||
/// </summary>
|
||||
ulong UsageCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when the location of the object was last changed.
|
||||
/// </summary>
|
||||
DateTime LocationChanged
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Touch the object. This function updates the internal last access
|
||||
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
|
||||
/// the last modification time gets updated, too. Each time you call
|
||||
/// <c>Touch</c>, the usage count of the object is increased by one.
|
||||
/// </summary>
|
||||
/// <param name="bModified">Update last modification time.</param>
|
||||
void Touch(bool bModified);
|
||||
}
|
||||
}
|
||||
37
ModernKeePassLib/Interfaces/IUIOperations.cs
Normal file
37
ModernKeePassLib/Interfaces/IUIOperations.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
|
||||
namespace ModernKeePassLib.Interfaces
|
||||
{
|
||||
public interface IUIOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Let the user interface save the current database.
|
||||
/// </summary>
|
||||
/// <param name="bForceSave">If <c>true</c>, the UI will not ask for
|
||||
/// whether to synchronize or overwrite, it'll simply overwrite the
|
||||
/// file.</param>
|
||||
/// <returns>Returns <c>true</c> if the file has been saved.</returns>
|
||||
bool UIFileSave(bool bForceSave);
|
||||
}
|
||||
}
|
||||
33
ModernKeePassLib/Interfaces/IXmlSerializerEx.cs
Normal file
33
ModernKeePassLib/Interfaces/IXmlSerializerEx.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
namespace ModernKeePassLib.Interfaces
|
||||
{
|
||||
public interface IXmlSerializerEx
|
||||
{
|
||||
void Serialize(XmlWriter xmlWriter, object o);
|
||||
object Deserialize(Stream s);
|
||||
}
|
||||
}
|
||||
295
ModernKeePassLib/Keys/CompositeKey.cs
Normal file
295
ModernKeePassLib/Keys/CompositeKey.cs
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.KeyDerivation;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a key. A key can be build up using several user key data sources
|
||||
/// like a password, a key file, the currently logged on user credentials,
|
||||
/// the current computer ID, etc.
|
||||
/// </summary>
|
||||
public sealed class CompositeKey
|
||||
{
|
||||
private List<IUserKey> m_vUserKeys = new List<IUserKey>();
|
||||
|
||||
/// <summary>
|
||||
/// List of all user keys contained in the current composite key.
|
||||
/// </summary>
|
||||
public IEnumerable<IUserKey> UserKeys
|
||||
{
|
||||
get { return m_vUserKeys; }
|
||||
}
|
||||
|
||||
public uint UserKeyCount
|
||||
{
|
||||
get { return (uint)m_vUserKeys.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new, empty key object.
|
||||
/// </summary>
|
||||
public CompositeKey()
|
||||
{
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// Deconstructor, clears up the key.
|
||||
// /// </summary>
|
||||
// ~CompositeKey()
|
||||
// {
|
||||
// Clear();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Clears the key. This function also erases all previously stored
|
||||
// /// user key data objects.
|
||||
// /// </summary>
|
||||
// public void Clear()
|
||||
// {
|
||||
// foreach(IUserKey pKey in m_vUserKeys)
|
||||
// pKey.Clear();
|
||||
// m_vUserKeys.Clear();
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Add a user key.
|
||||
/// </summary>
|
||||
/// <param name="pKey">User key to add.</param>
|
||||
public void AddUserKey(IUserKey pKey)
|
||||
{
|
||||
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
|
||||
|
||||
m_vUserKeys.Add(pKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a user key.
|
||||
/// </summary>
|
||||
/// <param name="pKey">User key to remove.</param>
|
||||
/// <returns>Returns <c>true</c> if the key was removed successfully.</returns>
|
||||
public bool RemoveUserKey(IUserKey pKey)
|
||||
{
|
||||
Debug.Assert(pKey != null); if(pKey == null) throw new ArgumentNullException("pKey");
|
||||
|
||||
Debug.Assert(m_vUserKeys.IndexOf(pKey) >= 0);
|
||||
return m_vUserKeys.Remove(pKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the composite key contains a specific type of
|
||||
/// user keys (password, key file, ...). If at least one user
|
||||
/// key of that type is present, the function returns <c>true</c>.
|
||||
/// </summary>
|
||||
/// <param name="tUserKeyType">User key type.</param>
|
||||
/// <returns>Returns <c>true</c>, if the composite key contains
|
||||
/// a user key of the specified type.</returns>
|
||||
public bool ContainsType(Type tUserKeyType)
|
||||
{
|
||||
Debug.Assert(tUserKeyType != null);
|
||||
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
|
||||
|
||||
foreach(IUserKey pKey in m_vUserKeys)
|
||||
{
|
||||
if(pKey == null) { Debug.Assert(false); continue; }
|
||||
|
||||
#if KeePassUAP
|
||||
if(pKey.GetType() == tUserKeyType)
|
||||
return true;
|
||||
#else
|
||||
if(tUserKeyType.IsInstanceOfType(pKey))
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the first user key of a specified type.
|
||||
/// </summary>
|
||||
/// <param name="tUserKeyType">Type of the user key to get.</param>
|
||||
/// <returns>Returns the first user key of the specified type
|
||||
/// or <c>null</c> if no key of that type is found.</returns>
|
||||
public IUserKey GetUserKey(Type tUserKeyType)
|
||||
{
|
||||
Debug.Assert(tUserKeyType != null);
|
||||
if(tUserKeyType == null) throw new ArgumentNullException("tUserKeyType");
|
||||
|
||||
foreach(IUserKey pKey in m_vUserKeys)
|
||||
{
|
||||
if(pKey == null) { Debug.Assert(false); continue; }
|
||||
|
||||
#if KeePassUAP
|
||||
if(pKey.GetType() == tUserKeyType)
|
||||
return pKey;
|
||||
#else
|
||||
if(tUserKeyType.IsInstanceOfType(pKey))
|
||||
return pKey;
|
||||
#endif
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the composite key from the supplied user key sources (password,
|
||||
/// key file, user account, computer ID, etc.).
|
||||
/// </summary>
|
||||
private byte[] CreateRawCompositeKey32()
|
||||
{
|
||||
ValidateUserKeys();
|
||||
|
||||
// Concatenate user key data
|
||||
List<byte[]> lData = new List<byte[]>();
|
||||
int cbData = 0;
|
||||
foreach(IUserKey pKey in m_vUserKeys)
|
||||
{
|
||||
ProtectedBinary b = pKey.KeyData;
|
||||
if(b != null)
|
||||
{
|
||||
byte[] pbKeyData = b.ReadData();
|
||||
lData.Add(pbKeyData);
|
||||
cbData += pbKeyData.Length;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] pbAllData = new byte[cbData];
|
||||
int p = 0;
|
||||
foreach(byte[] pbData in lData)
|
||||
{
|
||||
Array.Copy(pbData, 0, pbAllData, p, pbData.Length);
|
||||
p += pbData.Length;
|
||||
MemUtil.ZeroByteArray(pbData);
|
||||
}
|
||||
Debug.Assert(p == cbData);
|
||||
|
||||
byte[] pbHash = CryptoUtil.HashSha256(pbAllData);
|
||||
MemUtil.ZeroByteArray(pbAllData);
|
||||
return pbHash;
|
||||
}
|
||||
|
||||
public bool EqualsValue(CompositeKey ckOther)
|
||||
{
|
||||
if(ckOther == null) throw new ArgumentNullException("ckOther");
|
||||
|
||||
byte[] pbThis = CreateRawCompositeKey32();
|
||||
byte[] pbOther = ckOther.CreateRawCompositeKey32();
|
||||
bool bResult = MemUtil.ArraysEqual(pbThis, pbOther);
|
||||
MemUtil.ZeroByteArray(pbOther);
|
||||
MemUtil.ZeroByteArray(pbThis);
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public ProtectedBinary GenerateKey32(byte[] pbKeySeed32, ulong uNumRounds)
|
||||
{
|
||||
Debug.Assert(pbKeySeed32 != null);
|
||||
if(pbKeySeed32 == null) throw new ArgumentNullException("pbKeySeed32");
|
||||
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; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void ValidateUserKeys()
|
||||
{
|
||||
int nAccounts = 0;
|
||||
|
||||
foreach(IUserKey uKey in m_vUserKeys)
|
||||
{
|
||||
if(uKey is KcpUserAccount)
|
||||
++nAccounts;
|
||||
}
|
||||
|
||||
if(nAccounts >= 2)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InvalidCompositeKeyException : Exception
|
||||
{
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return KLRes.InvalidCompositeKey + MessageService.NewParagraph +
|
||||
KLRes.InvalidCompositeKeyHint;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new invalid composite key exception.
|
||||
/// </summary>
|
||||
public InvalidCompositeKeyException()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
46
ModernKeePassLib/Keys/IUserKey.cs
Normal file
46
ModernKeePassLib/Keys/IUserKey.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Security;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to a user key, like a password, key file data, etc.
|
||||
/// </summary>
|
||||
public interface IUserKey
|
||||
{
|
||||
/// <summary>
|
||||
/// Get key data. Querying this property is fast (it returns a
|
||||
/// reference to a cached <c>ProtectedBinary</c> object).
|
||||
/// If no key data is available, <c>null</c> is returned.
|
||||
/// </summary>
|
||||
ProtectedBinary KeyData
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// Clear the key and securely erase all security-critical information.
|
||||
// /// </summary>
|
||||
// void Clear();
|
||||
}
|
||||
}
|
||||
68
ModernKeePassLib/Keys/KcpCustomKey.cs
Normal file
68
ModernKeePassLib/Keys/KcpCustomKey.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Security;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
public sealed class KcpCustomKey : IUserKey
|
||||
{
|
||||
private readonly string m_strName;
|
||||
private ProtectedBinary m_pbKey;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the provider that generated the custom key.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return m_strName; }
|
||||
}
|
||||
|
||||
public ProtectedBinary KeyData
|
||||
{
|
||||
get { return m_pbKey; }
|
||||
}
|
||||
|
||||
public KcpCustomKey(string strName, byte[] pbKeyData, bool bPerformHash)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
Debug.Assert(pbKeyData != null); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
|
||||
|
||||
m_strName = strName;
|
||||
|
||||
if(bPerformHash)
|
||||
{
|
||||
byte[] pbRaw = CryptoUtil.HashSha256(pbKeyData);
|
||||
m_pbKey = new ProtectedBinary(true, pbRaw);
|
||||
}
|
||||
else m_pbKey = new ProtectedBinary(true, pbKeyData);
|
||||
}
|
||||
|
||||
// public void Clear()
|
||||
// {
|
||||
// m_pbKey = null;
|
||||
// }
|
||||
}
|
||||
}
|
||||
322
ModernKeePassLib/Keys/KcpKeyFile.cs
Normal file
322
ModernKeePassLib/Keys/KcpKeyFile.cs
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
#if ModernKeePassLib
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Windows.Security.Cryptography;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
using Windows.Storage;
|
||||
#else
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Serialization;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
/// <summary>
|
||||
/// Key files as provided by the user.
|
||||
/// </summary>
|
||||
public sealed class KcpKeyFile : IUserKey
|
||||
{
|
||||
private string m_strPath;
|
||||
private ProtectedBinary m_pbKeyData;
|
||||
|
||||
/// <summary>
|
||||
/// Path to the key file.
|
||||
/// </summary>
|
||||
public string Path
|
||||
{
|
||||
get { return m_strPath; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get key data. Querying this property is fast (it returns a
|
||||
/// reference to a cached <c>ProtectedBinary</c> object).
|
||||
/// If no key data is available, <c>null</c> is returned.
|
||||
/// </summary>
|
||||
public ProtectedBinary KeyData
|
||||
{
|
||||
get { return m_pbKeyData; }
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
public KcpKeyFile(StorageFile strKeyFile)
|
||||
{
|
||||
Construct(IOConnectionInfo.FromFile(strKeyFile), false);
|
||||
}
|
||||
#else
|
||||
public KcpKeyFile(string strKeyFile)
|
||||
{
|
||||
Construct(IOConnectionInfo.FromPath(strKeyFile), false);
|
||||
}
|
||||
|
||||
public KcpKeyFile(string strKeyFile, bool bThrowIfDbFile)
|
||||
{
|
||||
Construct(IOConnectionInfo.FromPath(strKeyFile), bThrowIfDbFile);
|
||||
}
|
||||
#endif
|
||||
|
||||
public KcpKeyFile(IOConnectionInfo iocKeyFile)
|
||||
{
|
||||
Construct(iocKeyFile, false);
|
||||
}
|
||||
|
||||
public KcpKeyFile(IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
|
||||
{
|
||||
Construct(iocKeyFile, bThrowIfDbFile);
|
||||
}
|
||||
|
||||
private void Construct(IOConnectionInfo iocFile, bool bThrowIfDbFile)
|
||||
{
|
||||
byte[] pbFileData = IOConnection.ReadFile(iocFile);
|
||||
if(pbFileData == null) throw new FileNotFoundException();
|
||||
|
||||
if(bThrowIfDbFile && (pbFileData.Length >= 8))
|
||||
{
|
||||
uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
|
||||
uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
|
||||
|
||||
if(((uSig1 == KdbxFile.FileSignature1) &&
|
||||
(uSig2 == KdbxFile.FileSignature2)) ||
|
||||
((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
|
||||
(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
|
||||
((uSig1 == KdbxFile.FileSignatureOld1) &&
|
||||
(uSig2 == KdbxFile.FileSignatureOld2)))
|
||||
#if KeePassLibSD
|
||||
throw new Exception(KLRes.KeyFileDbSel);
|
||||
#else
|
||||
throw new InvalidDataException(KLRes.KeyFileDbSel);
|
||||
#endif
|
||||
}
|
||||
|
||||
byte[] pbKey = LoadXmlKeyFile(pbFileData);
|
||||
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
|
||||
|
||||
if(pbKey == null) throw new InvalidOperationException();
|
||||
|
||||
m_strPath = iocFile.Path;
|
||||
m_pbKeyData = new ProtectedBinary(true, pbKey);
|
||||
|
||||
MemUtil.ZeroByteArray(pbKey);
|
||||
}
|
||||
|
||||
// public void Clear()
|
||||
// {
|
||||
// m_strPath = string.Empty;
|
||||
// m_pbKeyData = null;
|
||||
// }
|
||||
|
||||
private static byte[] LoadKeyFile(byte[] pbFileData)
|
||||
{
|
||||
if(pbFileData == null) { Debug.Assert(false); return null; }
|
||||
|
||||
int iLength = pbFileData.Length;
|
||||
|
||||
byte[] pbKey = null;
|
||||
if(iLength == 32) pbKey = LoadBinaryKey32(pbFileData);
|
||||
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
|
||||
|
||||
if(pbKey == null)
|
||||
pbKey = CryptoUtil.HashSha256(pbFileData);
|
||||
|
||||
return pbKey;
|
||||
}
|
||||
|
||||
private static byte[] LoadBinaryKey32(byte[] pbFileData)
|
||||
{
|
||||
if(pbFileData == null) { Debug.Assert(false); return null; }
|
||||
if(pbFileData.Length != 32) { Debug.Assert(false); return null; }
|
||||
|
||||
return pbFileData;
|
||||
}
|
||||
|
||||
private static byte[] LoadHexKey32(byte[] pbFileData)
|
||||
{
|
||||
if(pbFileData == null) { Debug.Assert(false); return null; }
|
||||
if(pbFileData.Length != 64) { Debug.Assert(false); return null; }
|
||||
|
||||
try
|
||||
{
|
||||
if(!StrUtil.IsHexString(pbFileData, true)) return null;
|
||||
|
||||
string strHex = StrUtil.Utf8.GetString(pbFileData, 0, pbFileData.Length);
|
||||
byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
|
||||
if((pbKey == null) || (pbKey.Length != 32))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
return pbKey;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, random key-file.
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">Path where the key-file should be saved to.
|
||||
/// If the file exists already, it will be overwritten.</param>
|
||||
/// <param name="pbAdditionalEntropy">Additional entropy used to generate
|
||||
/// the random key. May be <c>null</c> (in this case only the KeePass-internal
|
||||
/// random number generator is used).</param>
|
||||
/// <returns>Returns a <c>FileSaveResult</c> error code.</returns>
|
||||
#if ModernKeePassLib
|
||||
public static void Create(StorageFile strFilePath, byte[] pbAdditionalEntropy)
|
||||
#else
|
||||
public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
|
||||
#endif
|
||||
{
|
||||
byte[] pbKey32 = CryptoRandom.Instance.GetRandomBytes(32);
|
||||
if(pbKey32 == null) throw new SecurityException();
|
||||
|
||||
byte[] pbFinalKey32;
|
||||
if((pbAdditionalEntropy == null) || (pbAdditionalEntropy.Length == 0))
|
||||
pbFinalKey32 = pbKey32;
|
||||
else
|
||||
{
|
||||
using(MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
MemUtil.Write(ms, pbAdditionalEntropy);
|
||||
MemUtil.Write(ms, pbKey32);
|
||||
|
||||
pbFinalKey32 = CryptoUtil.HashSha256(ms.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
CreateXmlKeyFile(strFilePath, pbFinalKey32);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// XML Key Files
|
||||
// ================================================================
|
||||
|
||||
// Sample XML file:
|
||||
// <?xml version="1.0" encoding="utf-8"?>
|
||||
// <KeyFile>
|
||||
// <Meta>
|
||||
// <Version>1.00</Version>
|
||||
// </Meta>
|
||||
// <Key>
|
||||
// <Data>ySFoKuCcJblw8ie6RkMBdVCnAf4EedSch7ItujK6bmI=</Data>
|
||||
// </Key>
|
||||
// </KeyFile>
|
||||
|
||||
private const string RootElementName = "KeyFile";
|
||||
private const string MetaElementName = "Meta";
|
||||
private const string VersionElementName = "Version";
|
||||
private const string KeyElementName = "Key";
|
||||
private const string KeyDataElementName = "Data";
|
||||
|
||||
private static byte[] LoadXmlKeyFile(byte[] pbFileData)
|
||||
{
|
||||
if(pbFileData == null) { Debug.Assert(false); return null; }
|
||||
|
||||
MemoryStream ms = new MemoryStream(pbFileData, false);
|
||||
byte[] pbKeyData = null;
|
||||
|
||||
try
|
||||
{
|
||||
XmlDocument doc = XmlUtilEx.CreateXmlDocument();
|
||||
doc.Load(ms);
|
||||
|
||||
XmlElement el = doc.DocumentElement;
|
||||
if((el == null) || !el.Name.Equals(RootElementName)) return null;
|
||||
if(el.ChildNodes.Count < 2) return null;
|
||||
|
||||
foreach(XmlNode xmlChild in el.ChildNodes)
|
||||
{
|
||||
if(xmlChild.Name.Equals(MetaElementName)) { } // Ignore Meta
|
||||
else if(xmlChild.Name == KeyElementName)
|
||||
{
|
||||
foreach(XmlNode xmlKeyChild in xmlChild.ChildNodes)
|
||||
{
|
||||
if(xmlKeyChild.Name == KeyDataElementName)
|
||||
{
|
||||
if(pbKeyData == null)
|
||||
pbKeyData = Convert.FromBase64String(xmlKeyChild.InnerText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception) { pbKeyData = null; }
|
||||
finally { ms.Dispose(); }
|
||||
|
||||
return pbKeyData;
|
||||
}
|
||||
#if ModernKeePassLib
|
||||
private static void CreateXmlKeyFile(StorageFile strFile, byte[] pbKeyData)
|
||||
#else
|
||||
private static void CreateXmlKeyFile(string strFile, byte[] pbKeyData)
|
||||
#endif
|
||||
{
|
||||
Debug.Assert(strFile != null);
|
||||
if(strFile == null) throw new ArgumentNullException("strFile");
|
||||
Debug.Assert(pbKeyData != null);
|
||||
if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
|
||||
|
||||
#if ModernKeePassLib
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromFile(strFile);
|
||||
#else
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
|
||||
#endif
|
||||
using(Stream s = IOConnection.OpenWrite(ioc))
|
||||
{
|
||||
using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(s))
|
||||
{
|
||||
xw.WriteStartDocument();
|
||||
xw.WriteStartElement(RootElementName); // <KeyFile>
|
||||
|
||||
xw.WriteStartElement(MetaElementName); // <Meta>
|
||||
xw.WriteStartElement(VersionElementName); // <Version>
|
||||
xw.WriteString("1.00");
|
||||
xw.WriteEndElement(); // </Version>
|
||||
xw.WriteEndElement(); // </Meta>
|
||||
|
||||
xw.WriteStartElement(KeyElementName); // <Key>
|
||||
|
||||
xw.WriteStartElement(KeyDataElementName); // <Data>
|
||||
xw.WriteString(Convert.ToBase64String(pbKeyData));
|
||||
xw.WriteEndElement(); // </Data>
|
||||
|
||||
xw.WriteEndElement(); // </Key>
|
||||
|
||||
xw.WriteEndElement(); // </KeyFile>
|
||||
xw.WriteEndDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
111
ModernKeePassLib/Keys/KcpPassword.cs
Normal file
111
ModernKeePassLib/Keys/KcpPassword.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
/// <summary>
|
||||
/// Master password/passphrase as provided by the user.
|
||||
/// </summary>
|
||||
public sealed class KcpPassword : IUserKey
|
||||
{
|
||||
private ProtectedString m_psPassword = null; // Optional
|
||||
private ProtectedBinary m_pbKeyData;
|
||||
|
||||
/// <summary>
|
||||
/// Get the password as protected string. This is <c>null</c>
|
||||
/// unless remembering the password has been turned on.
|
||||
/// </summary>
|
||||
public ProtectedString Password
|
||||
{
|
||||
get { return m_psPassword; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get key data. Querying this property is fast (it returns a
|
||||
/// reference to a cached <c>ProtectedBinary</c> object).
|
||||
/// If no key data is available, <c>null</c> is returned.
|
||||
/// </summary>
|
||||
public ProtectedBinary KeyData
|
||||
{
|
||||
get { return m_pbKeyData; }
|
||||
}
|
||||
|
||||
public KcpPassword(byte[] pbPasswordUtf8)
|
||||
{
|
||||
SetKey(pbPasswordUtf8, true);
|
||||
}
|
||||
|
||||
public KcpPassword(byte[] pbPasswordUtf8, bool bRememberPassword)
|
||||
{
|
||||
SetKey(pbPasswordUtf8, bRememberPassword);
|
||||
}
|
||||
|
||||
public KcpPassword(string strPassword)
|
||||
{
|
||||
byte[] pb = StrUtil.Utf8.GetBytes(strPassword);
|
||||
try { SetKey(pb, true); }
|
||||
finally { MemUtil.ZeroByteArray(pb); }
|
||||
}
|
||||
|
||||
private void SetKey(byte[] pbPasswordUtf8, bool bRememberPassword)
|
||||
{
|
||||
Debug.Assert(pbPasswordUtf8 != null);
|
||||
if(pbPasswordUtf8 == null) throw new ArgumentNullException("pbPasswordUtf8");
|
||||
|
||||
#if (DEBUG && !KeePassLibSD)
|
||||
Debug.Assert(ValidatePassword(pbPasswordUtf8));
|
||||
#endif
|
||||
|
||||
byte[] pbRaw = CryptoUtil.HashSha256(pbPasswordUtf8);
|
||||
try { m_pbKeyData = new ProtectedBinary(true, pbRaw); }
|
||||
finally { MemUtil.ZeroByteArray(pbRaw); }
|
||||
|
||||
if(bRememberPassword)
|
||||
m_psPassword = new ProtectedString(true, pbPasswordUtf8);
|
||||
}
|
||||
|
||||
// public void Clear()
|
||||
// {
|
||||
// m_psPassword = null;
|
||||
// m_pbKeyData = null;
|
||||
// }
|
||||
|
||||
#if (DEBUG && !KeePassLibSD)
|
||||
private static bool ValidatePassword(byte[] pb)
|
||||
{
|
||||
try
|
||||
{
|
||||
string str = StrUtil.Utf8.GetString(pb);
|
||||
return str.IsNormalized(NormalizationForm.FormC);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
167
ModernKeePassLib/Keys/KcpUserAccount.cs
Normal file
167
ModernKeePassLib/Keys/KcpUserAccount.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
|
||||
#if ModernKeePassLib
|
||||
using Windows.Storage;
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
/// <summary>
|
||||
/// A user key depending on the currently logged on Windows user account.
|
||||
/// </summary>
|
||||
public sealed class KcpUserAccount : IUserKey
|
||||
{
|
||||
private ProtectedBinary m_pbKeyData = null;
|
||||
|
||||
// Constant initialization vector (unique for KeePass)
|
||||
private static readonly byte[] m_pbEntropy = new byte[] {
|
||||
0xDE, 0x13, 0x5B, 0x5F, 0x18, 0xA3, 0x46, 0x70,
|
||||
0xB2, 0x57, 0x24, 0x29, 0x69, 0x88, 0x98, 0xE6
|
||||
};
|
||||
|
||||
private const string UserKeyFileName = "ProtectedUserKey.bin";
|
||||
|
||||
/// <summary>
|
||||
/// Get key data. Querying this property is fast (it returns a
|
||||
/// reference to a cached <c>ProtectedBinary</c> object).
|
||||
/// If no key data is available, <c>null</c> is returned.
|
||||
/// </summary>
|
||||
public ProtectedBinary KeyData
|
||||
{
|
||||
get { return m_pbKeyData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a user account key.
|
||||
/// </summary>
|
||||
public KcpUserAccount()
|
||||
{
|
||||
if(!CryptoUtil.IsProtectedDataSupported)
|
||||
throw new PlatformNotSupportedException(); // Windows 98/ME
|
||||
|
||||
byte[] pbKey = LoadUserKey(false);
|
||||
if(pbKey == null) pbKey = CreateUserKey();
|
||||
if(pbKey == null) // Should never happen
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new SecurityException(KLRes.UserAccountKeyError);
|
||||
}
|
||||
|
||||
m_pbKeyData = new ProtectedBinary(true, pbKey);
|
||||
MemUtil.ZeroByteArray(pbKey);
|
||||
}
|
||||
|
||||
// public void Clear()
|
||||
// {
|
||||
// m_pbKeyData = null;
|
||||
// }
|
||||
|
||||
private static string GetUserKeyFilePath(bool bCreate)
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
|
||||
#else
|
||||
string strUserDir = Environment.GetFolderPath(
|
||||
Environment.SpecialFolder.ApplicationData);
|
||||
#endif
|
||||
|
||||
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
|
||||
strUserDir += PwDefs.ShortProductName;
|
||||
|
||||
#if !ModernKeePassLib
|
||||
|
||||
if(bCreate && !Directory.Exists(strUserDir))
|
||||
Directory.CreateDirectory(strUserDir);
|
||||
#endif
|
||||
|
||||
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
|
||||
return (strUserDir + UserKeyFileName);
|
||||
}
|
||||
|
||||
private static byte[] LoadUserKey(bool bThrow)
|
||||
{
|
||||
byte[] pbKey = null;
|
||||
|
||||
#if !KeePassLibSD
|
||||
try
|
||||
{
|
||||
string strFilePath = GetUserKeyFilePath(false);
|
||||
|
||||
#if ModernKeePassLib
|
||||
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForReadAsync().GetAwaiter().GetResult();
|
||||
var pbProtectedKey = new byte[(int)fileStream.Length];
|
||||
fileStream.Read(pbProtectedKey, 0, (int)fileStream.Length);
|
||||
fileStream.Dispose();
|
||||
#else
|
||||
byte[] pbProtectedKey = File.ReadAllBytes(strFilePath);
|
||||
#endif
|
||||
|
||||
pbKey = CryptoUtil.UnprotectData(pbProtectedKey, m_pbEntropy,
|
||||
DataProtectionScope.CurrentUser);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
if(bThrow) throw;
|
||||
pbKey = null;
|
||||
}
|
||||
#endif
|
||||
|
||||
return pbKey;
|
||||
}
|
||||
|
||||
private static byte[] CreateUserKey()
|
||||
{
|
||||
#if KeePassLibSD
|
||||
return null;
|
||||
#else
|
||||
string strFilePath = GetUserKeyFilePath(true);
|
||||
|
||||
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
|
||||
byte[] pbProtectedKey = CryptoUtil.ProtectData(pbRandomKey,
|
||||
m_pbEntropy, DataProtectionScope.CurrentUser);
|
||||
#if ModernKeePassLib
|
||||
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult();
|
||||
fileStream.Write(pbProtectedKey, 0, (int)fileStream.Length);
|
||||
fileStream.Dispose();
|
||||
#else
|
||||
File.WriteAllBytes(strFilePath, pbProtectedKey);
|
||||
#endif
|
||||
|
||||
byte[] pbKey = LoadUserKey(true);
|
||||
Debug.Assert(MemUtil.ArraysEqual(pbKey, pbRandomKey));
|
||||
|
||||
MemUtil.ZeroByteArray(pbRandomKey);
|
||||
return pbKey;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
152
ModernKeePassLib/Keys/KeyProvider.cs
Normal file
152
ModernKeePassLib/Keys/KeyProvider.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
|
||||
using ModernKeePassLib.Serialization;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
public sealed class KeyProviderQueryContext
|
||||
{
|
||||
private IOConnectionInfo m_ioInfo;
|
||||
public IOConnectionInfo DatabaseIOInfo
|
||||
{
|
||||
get { return m_ioInfo; }
|
||||
}
|
||||
|
||||
public string DatabasePath
|
||||
{
|
||||
get { return m_ioInfo.Path; }
|
||||
}
|
||||
|
||||
private bool m_bCreatingNewKey;
|
||||
public bool CreatingNewKey
|
||||
{
|
||||
get { return m_bCreatingNewKey; }
|
||||
}
|
||||
|
||||
private bool m_bSecDesktop;
|
||||
public bool IsOnSecureDesktop
|
||||
{
|
||||
get { return m_bSecDesktop; }
|
||||
}
|
||||
|
||||
public KeyProviderQueryContext(IOConnectionInfo ioInfo, bool bCreatingNewKey,
|
||||
bool bOnSecDesktop)
|
||||
{
|
||||
if(ioInfo == null) throw new ArgumentNullException("ioInfo");
|
||||
|
||||
m_ioInfo = ioInfo.CloneDeep();
|
||||
m_bCreatingNewKey = bCreatingNewKey;
|
||||
m_bSecDesktop = bOnSecDesktop;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class KeyProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of your key provider (should be unique).
|
||||
/// </summary>
|
||||
public abstract string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property indicating whether the provider is exclusive.
|
||||
/// If the provider is exclusive, KeePass doesn't allow other
|
||||
/// key sources (master password, Windows user account, ...)
|
||||
/// to be combined with the provider.
|
||||
/// Key providers typically should return <c>false</c>
|
||||
/// (to allow non-exclusive use), i.e. don't override this
|
||||
/// property.
|
||||
/// </summary>
|
||||
public virtual bool Exclusive
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property that specifies whether the returned key data
|
||||
/// gets hashed by KeePass first or is written directly to
|
||||
/// the user key data stream.
|
||||
/// Standard key provider plugins should return <c>false</c>
|
||||
/// (i.e. don't overwrite this property). Returning <c>true</c>
|
||||
/// may cause severe security problems and is highly
|
||||
/// discouraged.
|
||||
/// </summary>
|
||||
public virtual bool DirectKey
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
// public virtual PwIcon ImageIndex
|
||||
// {
|
||||
// get { return PwIcon.UserKey; }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// This property specifies whether the <c>GetKey</c> method might
|
||||
/// show a form or dialog. If there is any chance that the method shows
|
||||
/// one, this property must return <c>true</c>. Only if it's guaranteed
|
||||
/// that the <c>GetKey</c> method doesn't show any form or dialog, this
|
||||
/// property should return <c>false</c>.
|
||||
/// </summary>
|
||||
public virtual bool GetKeyMightShowGui
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This property specifies whether the key provider is compatible
|
||||
/// with the secure desktop mode. This almost never is the case,
|
||||
/// so you usually won't override this property.
|
||||
/// </summary>
|
||||
public virtual bool SecureDesktopCompatible
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public abstract byte[] GetKey(KeyProviderQueryContext ctx);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public sealed class SampleKeyProvider : KeyProvider
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "Built-In Sample Key Provider"; }
|
||||
}
|
||||
|
||||
// Do not just copy this to your own key provider class! See above.
|
||||
public override bool GetKeyMightShowGui
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override byte[] GetKey(KeyProviderQueryContext ctx)
|
||||
{
|
||||
return new byte[]{ 2, 3, 5, 7, 11, 13 };
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
105
ModernKeePassLib/Keys/KeyProviderPool.cs
Normal file
105
ModernKeePassLib/Keys/KeyProviderPool.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
public sealed class KeyProviderPool : IEnumerable<KeyProvider>
|
||||
{
|
||||
private List<KeyProvider> m_vProviders = new List<KeyProvider>();
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_vProviders.Count; }
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_vProviders.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyProvider> GetEnumerator()
|
||||
{
|
||||
return m_vProviders.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(KeyProvider prov)
|
||||
{
|
||||
Debug.Assert(prov != null); if(prov == null) throw new ArgumentNullException("prov");
|
||||
|
||||
m_vProviders.Add(prov);
|
||||
}
|
||||
|
||||
public bool Remove(KeyProvider prov)
|
||||
{
|
||||
Debug.Assert(prov != null); if(prov == null) throw new ArgumentNullException("prov");
|
||||
|
||||
return m_vProviders.Remove(prov);
|
||||
}
|
||||
|
||||
public KeyProvider Get(string strProviderName)
|
||||
{
|
||||
if(strProviderName == null) throw new ArgumentNullException("strProviderName");
|
||||
|
||||
foreach(KeyProvider prov in m_vProviders)
|
||||
{
|
||||
if(prov.Name == strProviderName) return prov;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsKeyProvider(string strName)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
|
||||
foreach(KeyProvider prov in m_vProviders)
|
||||
{
|
||||
if(prov.Name == strName) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal byte[] GetKey(string strProviderName, KeyProviderQueryContext ctx,
|
||||
out bool bPerformHash)
|
||||
{
|
||||
Debug.Assert(strProviderName != null); if(strProviderName == null) throw new ArgumentNullException("strProviderName");
|
||||
|
||||
bPerformHash = true;
|
||||
|
||||
foreach(KeyProvider prov in m_vProviders)
|
||||
{
|
||||
if(prov.Name == strProviderName)
|
||||
{
|
||||
bPerformHash = !prov.DirectKey;
|
||||
return prov.GetKey(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
ModernKeePassLib/Keys/KeyValidator.cs
Normal file
51
ModernKeePassLib/Keys/KeyValidator.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
public enum KeyValidationType
|
||||
{
|
||||
MasterPassword = 0
|
||||
}
|
||||
|
||||
public abstract class KeyValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of your key validator (should be unique).
|
||||
/// </summary>
|
||||
public abstract string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate a key.
|
||||
/// </summary>
|
||||
/// <param name="strKey">Key to validate.</param>
|
||||
/// <param name="t">Type of the validation to perform.</param>
|
||||
/// <returns>Returns <c>null</c>, if the validation is successful.
|
||||
/// If there's a problem with the key, the returned string describes
|
||||
/// the problem.</returns>
|
||||
public abstract string Validate(string strKey, KeyValidationType t);
|
||||
}
|
||||
}
|
||||
86
ModernKeePassLib/Keys/KeyValidatorPool.cs
Normal file
86
ModernKeePassLib/Keys/KeyValidatorPool.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
public sealed class KeyValidatorPool : IEnumerable<KeyValidator>
|
||||
{
|
||||
private List<KeyValidator> m_vValidators = new List<KeyValidator>();
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_vValidators.Count; }
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return m_vValidators.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValidator> GetEnumerator()
|
||||
{
|
||||
return m_vValidators.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(KeyValidator v)
|
||||
{
|
||||
Debug.Assert(v != null); if(v == null) throw new ArgumentNullException("v");
|
||||
|
||||
m_vValidators.Add(v);
|
||||
}
|
||||
|
||||
public bool Remove(KeyValidator v)
|
||||
{
|
||||
Debug.Assert(v != null); if(v == null) throw new ArgumentNullException("v");
|
||||
|
||||
return m_vValidators.Remove(v);
|
||||
}
|
||||
|
||||
public string Validate(string strKey, KeyValidationType t)
|
||||
{
|
||||
Debug.Assert(strKey != null); if(strKey == null) throw new ArgumentNullException("strKey");
|
||||
|
||||
foreach(KeyValidator v in m_vValidators)
|
||||
{
|
||||
string strResult = v.Validate(strKey, t);
|
||||
if(strResult != null) return strResult;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string Validate(byte[] pbKeyUtf8, KeyValidationType t)
|
||||
{
|
||||
Debug.Assert(pbKeyUtf8 != null); if(pbKeyUtf8 == null) throw new ArgumentNullException("pbKeyUtf8");
|
||||
|
||||
if(m_vValidators.Count == 0) return null;
|
||||
|
||||
string strKey = StrUtil.Utf8.GetString(pbKeyUtf8, 0, pbKeyUtf8.Length);
|
||||
return Validate(strKey, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
ModernKeePassLib/Keys/UserKeyType.cs
Normal file
33
ModernKeePassLib/Keys/UserKeyType.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib.Keys
|
||||
{
|
||||
[Flags]
|
||||
public enum UserKeyType
|
||||
{
|
||||
None = 0,
|
||||
Other = 1,
|
||||
Password = 2,
|
||||
KeyFile = 4,
|
||||
UserAccount = 8
|
||||
}
|
||||
}
|
||||
BIN
ModernKeePassLib/Libs/Windows.winmd
Normal file
BIN
ModernKeePassLib/Libs/Windows.winmd
Normal file
Binary file not shown.
74
ModernKeePassLib/ModernKeePassLib.csproj
Normal file
74
ModernKeePassLib/ModernKeePassLib.csproj
Normal file
@@ -0,0 +1,74 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Version>2.41.0</Version>
|
||||
<Authors>Geoffroy Bonneville</Authors>
|
||||
<PackageLicenseUrl>https://www.gnu.org/licenses/gpl-3.0.en.html</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://github.com/wismna/ModernKeePass</PackageProjectUrl>
|
||||
<Description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</Description>
|
||||
<Company>wismna</Company>
|
||||
<Product>ModernKeePassLib</Product>
|
||||
<PackageReleaseNotes>Updated to version 2.41</PackageReleaseNotes>
|
||||
<PackageTags>KeePass KeePassLib Portable PCL NetStandard ModernKeePass</PackageTags>
|
||||
<Copyright>Copyright © 2019 Geoffroy Bonneville</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>TRACE;ModernKeePassLib</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>TRACE;ModernKeePassLib</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Cryptography\Hash\HashAlgorithm.cs" />
|
||||
<Compile Remove="Cryptography\Hash\HMAC.cs" />
|
||||
<Compile Remove="Cryptography\Hash\HMACSHA1.cs" />
|
||||
<Compile Remove="Cryptography\Hash\HMACSHA256.cs" />
|
||||
<Compile Remove="Cryptography\Hash\SHA256Managed.cs" />
|
||||
<Compile Remove="Cryptography\Hash\SHA512Managed.cs" />
|
||||
<Compile Remove="Native\ClipboardU.cs" />
|
||||
<Compile Remove="Native\NativeLib.cs" />
|
||||
<Compile Remove="Native\NativeMethods.cs" />
|
||||
<Compile Remove="Native\NativeMethods.Unix.cs" />
|
||||
<Compile Remove="Translation\KPControlCustomization.cs" />
|
||||
<Compile Remove="Translation\KPFormCustomization.cs" />
|
||||
<Compile Remove="Translation\KPStringTable.cs" />
|
||||
<Compile Remove="Translation\KPStringTableItem.cs" />
|
||||
<Compile Remove="Translation\KPTranslation.cs" />
|
||||
<Compile Remove="Translation\KPTranslationProperties.cs" />
|
||||
<Compile Remove="Utility\GfxUtil.cs" />
|
||||
<Compile Remove="Utility\MonoWorkarounds.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Native\ClipboardU.cs" />
|
||||
<None Include="Native\NativeLib.cs" />
|
||||
<None Include="Native\NativeMethods.cs" />
|
||||
<None Include="Native\NativeMethods.Unix.cs" />
|
||||
<None Include="Translation\KPControlCustomization.cs" />
|
||||
<None Include="Translation\KPFormCustomization.cs" />
|
||||
<None Include="Translation\KPStringTable.cs" />
|
||||
<None Include="Translation\KPStringTableItem.cs" />
|
||||
<None Include="Translation\KPTranslation.cs" />
|
||||
<None Include="Translation\KPTranslationProperties.cs" />
|
||||
<None Include="Utility\GfxUtil.cs" />
|
||||
<None Include="Utility\MonoWorkarounds.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Runtime.WindowsRuntime" Version="4.3.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Windows">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.17134.0\Windows.winmd</HintPath>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
190
ModernKeePassLib/Native/ClipboardU.cs
Normal file
190
ModernKeePassLib/Native/ClipboardU.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace ModernKeePassLib.Native
|
||||
{
|
||||
internal static class ClipboardU
|
||||
{
|
||||
private const string XSel = "xsel";
|
||||
private const string XSelV = "--version";
|
||||
private const string XSelR = "--output --clipboard";
|
||||
private const string XSelC = "--clear --clipboard";
|
||||
private const string XSelW = "--input --clipboard";
|
||||
private const string XSelND = " --nodetach";
|
||||
private const AppRunFlags XSelWF = AppRunFlags.WaitForExit;
|
||||
|
||||
private static bool? g_obXSel = null;
|
||||
|
||||
public static string GetText()
|
||||
{
|
||||
// System.Windows.Forms.Clipboard doesn't work properly,
|
||||
// see Mono workaround 1530
|
||||
|
||||
// string str = GtkGetText();
|
||||
// if(str != null) return str;
|
||||
|
||||
return XSelGetText();
|
||||
}
|
||||
|
||||
public static bool SetText(string strText, bool bMayBlock)
|
||||
{
|
||||
string str = (strText ?? string.Empty);
|
||||
|
||||
// System.Windows.Forms.Clipboard doesn't work properly,
|
||||
// see Mono workaround 1530
|
||||
|
||||
// if(GtkSetText(str)) return true;
|
||||
|
||||
return XSelSetText(str, bMayBlock);
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// LibGTK
|
||||
|
||||
// Even though GTK+ 3 appears to be loaded already, performing a
|
||||
// P/Invoke of LibGTK's gtk_init_check function terminates the
|
||||
// process (!) with the following error message:
|
||||
// "Gtk-ERROR **: GTK+ 2.x symbols detected. Using GTK+ 2.x and
|
||||
// GTK+ 3 in the same process is not supported".
|
||||
|
||||
/* private static bool GtkInit()
|
||||
{
|
||||
try
|
||||
{
|
||||
// GTK requires GLib;
|
||||
// the following throws if and only if GLib is unavailable
|
||||
NativeMethods.g_free(IntPtr.Zero);
|
||||
|
||||
if(NativeMethods.gtk_init_check(IntPtr.Zero, IntPtr.Zero) !=
|
||||
NativeMethods.G_FALSE)
|
||||
return true;
|
||||
|
||||
Debug.Assert(false);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GtkGetText()
|
||||
{
|
||||
IntPtr lpText = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
if(GtkInit())
|
||||
{
|
||||
IntPtr h = NativeMethods.gtk_clipboard_get(
|
||||
NativeMethods.GDK_SELECTION_CLIPBOARD);
|
||||
if(h != IntPtr.Zero)
|
||||
{
|
||||
lpText = NativeMethods.gtk_clipboard_wait_for_text(h);
|
||||
if(lpText != IntPtr.Zero)
|
||||
return NativeMethods.Utf8ZToString(lpText);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
try { NativeMethods.g_free(lpText); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool GtkSetText(string str)
|
||||
{
|
||||
IntPtr lpText = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
if(GtkInit())
|
||||
{
|
||||
lpText = NativeMethods.Utf8ZFromString(str ?? string.Empty);
|
||||
if(lpText == IntPtr.Zero) { Debug.Assert(false); return false; }
|
||||
|
||||
bool b = false;
|
||||
for(int i = 0; i < 2; ++i)
|
||||
{
|
||||
IntPtr h = NativeMethods.gtk_clipboard_get((i == 0) ?
|
||||
NativeMethods.GDK_SELECTION_PRIMARY :
|
||||
NativeMethods.GDK_SELECTION_CLIPBOARD);
|
||||
if(h != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.gtk_clipboard_clear(h);
|
||||
NativeMethods.gtk_clipboard_set_text(h, lpText, -1);
|
||||
NativeMethods.gtk_clipboard_store(h);
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { NativeMethods.Utf8ZFree(lpText); }
|
||||
|
||||
return false;
|
||||
} */
|
||||
|
||||
// =============================================================
|
||||
// XSel
|
||||
|
||||
private static bool XSelInit()
|
||||
{
|
||||
if(g_obXSel.HasValue) return g_obXSel.Value;
|
||||
|
||||
string strTest = NativeLib.RunConsoleApp(XSel, XSelV);
|
||||
|
||||
bool b = (strTest != null);
|
||||
g_obXSel = b;
|
||||
return b;
|
||||
}
|
||||
|
||||
private static string XSelGetText()
|
||||
{
|
||||
if(!XSelInit()) return null;
|
||||
|
||||
return NativeLib.RunConsoleApp(XSel, XSelR);
|
||||
}
|
||||
|
||||
private static bool XSelSetText(string str, bool bMayBlock)
|
||||
{
|
||||
if(!XSelInit()) return false;
|
||||
|
||||
string strOpt = (bMayBlock ? XSelND : string.Empty);
|
||||
|
||||
// xsel with an empty input can hang, thus use --clear
|
||||
if(str.Length == 0)
|
||||
return (NativeLib.RunConsoleApp(XSel, XSelC + strOpt,
|
||||
null, XSelWF) != null);
|
||||
|
||||
// Use --nodetach to prevent clipboard corruption;
|
||||
// https://sourceforge.net/p/keepass/bugs/1603/
|
||||
return (NativeLib.RunConsoleApp(XSel, XSelW + strOpt,
|
||||
str, XSelWF) != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
90
ModernKeePassLib/Native/Native.PCL.cs
Normal file
90
ModernKeePassLib/Native/Native.PCL.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
|
||||
namespace ModernKeePassLib.Native
|
||||
{
|
||||
internal static class NativeLib
|
||||
{
|
||||
public static ulong MonoVersion {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public static bool IsUnix()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TransformKey256(byte[] pbNative, byte[] pbSeed, ulong uRounds)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static System.PlatformID GetPlatformID()
|
||||
{
|
||||
return Environment.OSVersion.Platform;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class NativeMethods
|
||||
{
|
||||
public static bool SupportsStrCmpNaturally => false;
|
||||
|
||||
internal const int GCRY_CIPHER_AES256 = 9;
|
||||
internal const int GCRY_CIPHER_MODE_ECB = 1;
|
||||
|
||||
public static int StrCmpNaturally (string s1, string s2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal static void gcry_check_version(IntPtr zero)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void gcry_cipher_open(ref IntPtr intPtr, object gcryCipherAes256, object gcryCipherModeEcb, int i)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal static int gcry_cipher_setkey(IntPtr h, IntPtr pSeed32, IntPtr n32)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal static void gcry_cipher_close(IntPtr h)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal static int gcry_cipher_encrypt(IntPtr h, IntPtr pData32, IntPtr n32, IntPtr zero1, IntPtr zero2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static string GetUserRuntimeDir()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
internal enum MemoryProtectionScope
|
||||
{
|
||||
CrossProcess,
|
||||
SameLogon,
|
||||
SameProcess
|
||||
}
|
||||
|
||||
internal static class ProtectedMemory
|
||||
{
|
||||
public static byte[] Protect(byte[] userData, MemoryProtectionScope scope)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static byte[] Unprotect(byte[] userData, MemoryProtectionScope scope)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
460
ModernKeePassLib/Native/NativeLib.cs
Normal file
460
ModernKeePassLib/Native/NativeLib.cs
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
#if !ModernKeePassLib
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to native library (library containing fast versions of
|
||||
/// several cryptographic functions).
|
||||
/// </summary>
|
||||
public static class NativeLib
|
||||
{
|
||||
private static bool m_bAllowNative = true;
|
||||
|
||||
/// <summary>
|
||||
/// If this property is set to <c>true</c>, the native library is used.
|
||||
/// If it is <c>false</c>, all calls to functions in this class will fail.
|
||||
/// </summary>
|
||||
public static bool AllowNative
|
||||
{
|
||||
get { return m_bAllowNative; }
|
||||
set { m_bAllowNative = value; }
|
||||
}
|
||||
|
||||
private static ulong? m_ouMonoVersion = null;
|
||||
public static ulong MonoVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_ouMonoVersion.HasValue) return m_ouMonoVersion.Value;
|
||||
|
||||
ulong uVersion = 0;
|
||||
try
|
||||
{
|
||||
Type t = Type.GetType("Mono.Runtime");
|
||||
if(t != null)
|
||||
{
|
||||
MethodInfo mi = t.GetMethod("GetDisplayName",
|
||||
BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if(mi != null)
|
||||
{
|
||||
string strName = (mi.Invoke(null, null) as string);
|
||||
if(!string.IsNullOrEmpty(strName))
|
||||
{
|
||||
Match m = Regex.Match(strName, "\\d+(\\.\\d+)+");
|
||||
if(m.Success)
|
||||
uVersion = StrUtil.ParseVersion(m.Value);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
m_ouMonoVersion = uVersion;
|
||||
return uVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the native library is installed.
|
||||
/// </summary>
|
||||
/// <returns>Returns <c>true</c>, if the native library is installed.</returns>
|
||||
public static bool IsLibraryInstalled()
|
||||
{
|
||||
byte[] pDummy0 = new byte[32];
|
||||
byte[] pDummy1 = new byte[32];
|
||||
|
||||
// Save the native state
|
||||
bool bCachedNativeState = m_bAllowNative;
|
||||
|
||||
// Temporarily allow native functions and try to load the library
|
||||
m_bAllowNative = true;
|
||||
bool bResult = TransformKey256(pDummy0, pDummy1, 16);
|
||||
|
||||
// Pop native state and return result
|
||||
m_bAllowNative = bCachedNativeState;
|
||||
return bResult;
|
||||
}
|
||||
|
||||
private static bool? m_bIsUnix = null;
|
||||
public static bool IsUnix()
|
||||
{
|
||||
if(m_bIsUnix.HasValue) return m_bIsUnix.Value;
|
||||
|
||||
PlatformID p = GetPlatformID();
|
||||
|
||||
// Mono defines Unix as 128 in early .NET versions
|
||||
#if !KeePassLibSD
|
||||
m_bIsUnix = ((p == PlatformID.Unix) || (p == PlatformID.MacOSX) ||
|
||||
((int)p == 128));
|
||||
#else
|
||||
m_bIsUnix = (((int)p == 4) || ((int)p == 6) || ((int)p == 128));
|
||||
#endif
|
||||
return m_bIsUnix.Value;
|
||||
}
|
||||
|
||||
private static PlatformID? m_platID = null;
|
||||
public static PlatformID GetPlatformID()
|
||||
{
|
||||
if(m_platID.HasValue) return m_platID.Value;
|
||||
|
||||
#if KeePassUAP
|
||||
m_platID = EnvironmentExt.OSVersion.Platform;
|
||||
#else
|
||||
m_platID = Environment.OSVersion.Platform;
|
||||
#endif
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
// Mono returns PlatformID.Unix on Mac OS X, workaround this
|
||||
if(m_platID.Value == PlatformID.Unix)
|
||||
{
|
||||
if((RunConsoleApp("uname", null) ?? string.Empty).Trim().Equals(
|
||||
"Darwin", StrUtil.CaseIgnoreCmp))
|
||||
m_platID = PlatformID.MacOSX;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_platID.Value;
|
||||
}
|
||||
|
||||
private static DesktopType? m_tDesktop = null;
|
||||
public static DesktopType GetDesktopType()
|
||||
{
|
||||
if(!m_tDesktop.HasValue)
|
||||
{
|
||||
DesktopType t = DesktopType.None;
|
||||
if(!IsUnix()) t = DesktopType.Windows;
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
string strXdg = (Environment.GetEnvironmentVariable(
|
||||
"XDG_CURRENT_DESKTOP") ?? string.Empty).Trim();
|
||||
string strGdm = (Environment.GetEnvironmentVariable(
|
||||
"GDMSESSION") ?? string.Empty).Trim();
|
||||
StringComparison sc = StrUtil.CaseIgnoreCmp;
|
||||
|
||||
if(strXdg.Equals("Unity", sc))
|
||||
t = DesktopType.Unity;
|
||||
else if(strXdg.Equals("LXDE", sc))
|
||||
t = DesktopType.Lxde;
|
||||
else if(strXdg.Equals("XFCE", sc))
|
||||
t = DesktopType.Xfce;
|
||||
else if(strXdg.Equals("MATE", sc))
|
||||
t = DesktopType.Mate;
|
||||
else if(strXdg.Equals("X-Cinnamon", sc)) // Mint 18.3
|
||||
t = DesktopType.Cinnamon;
|
||||
else if(strXdg.Equals("Pantheon", sc)) // Elementary OS
|
||||
t = DesktopType.Pantheon;
|
||||
else if(strXdg.Equals("KDE", sc) || // Mint 16, Kubuntu 17.10
|
||||
strGdm.Equals("kde-plasma", sc)) // Ubuntu 12.04
|
||||
t = DesktopType.Kde;
|
||||
else if(strXdg.Equals("GNOME", sc))
|
||||
{
|
||||
if(strGdm.Equals("cinnamon", sc)) // Mint 13
|
||||
t = DesktopType.Cinnamon;
|
||||
else t = DesktopType.Gnome; // Fedora 27
|
||||
}
|
||||
else if(strXdg.Equals("ubuntu:GNOME", sc))
|
||||
t = DesktopType.Gnome;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
m_tDesktop = t;
|
||||
}
|
||||
|
||||
return m_tDesktop.Value;
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
public static string RunConsoleApp(string strAppPath, string strParams)
|
||||
{
|
||||
return RunConsoleApp(strAppPath, strParams, null);
|
||||
}
|
||||
|
||||
public static string RunConsoleApp(string strAppPath, string strParams,
|
||||
string strStdInput)
|
||||
{
|
||||
return RunConsoleApp(strAppPath, strParams, strStdInput,
|
||||
(AppRunFlags.GetStdOutput | AppRunFlags.WaitForExit));
|
||||
}
|
||||
|
||||
private delegate string RunProcessDelegate();
|
||||
|
||||
public static string RunConsoleApp(string strAppPath, string strParams,
|
||||
string strStdInput, AppRunFlags f)
|
||||
{
|
||||
if(strAppPath == null) throw new ArgumentNullException("strAppPath");
|
||||
if(strAppPath.Length == 0) throw new ArgumentException("strAppPath");
|
||||
|
||||
bool bStdOut = ((f & AppRunFlags.GetStdOutput) != AppRunFlags.None);
|
||||
|
||||
RunProcessDelegate fnRun = delegate()
|
||||
{
|
||||
Process pToDispose = null;
|
||||
try
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
|
||||
psi.CreateNoWindow = true;
|
||||
psi.FileName = strAppPath;
|
||||
psi.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
psi.UseShellExecute = false;
|
||||
psi.RedirectStandardOutput = bStdOut;
|
||||
|
||||
if(strStdInput != null) psi.RedirectStandardInput = true;
|
||||
|
||||
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
|
||||
|
||||
Process p = Process.Start(psi);
|
||||
pToDispose = p;
|
||||
|
||||
if(strStdInput != null)
|
||||
{
|
||||
EnsureNoBom(p.StandardInput);
|
||||
|
||||
p.StandardInput.Write(strStdInput);
|
||||
p.StandardInput.Close();
|
||||
}
|
||||
|
||||
string strOutput = string.Empty;
|
||||
if(bStdOut) strOutput = p.StandardOutput.ReadToEnd();
|
||||
|
||||
if((f & AppRunFlags.WaitForExit) != AppRunFlags.None)
|
||||
p.WaitForExit();
|
||||
else if((f & AppRunFlags.GCKeepAlive) != AppRunFlags.None)
|
||||
{
|
||||
pToDispose = null; // Thread disposes it
|
||||
|
||||
Thread th = new Thread(delegate()
|
||||
{
|
||||
try { p.WaitForExit(); p.Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
});
|
||||
th.Start();
|
||||
}
|
||||
|
||||
return strOutput;
|
||||
}
|
||||
#if DEBUG
|
||||
catch(Exception ex) { Debug.Assert(ex is ThreadAbortException); }
|
||||
#else
|
||||
catch(Exception) { }
|
||||
#endif
|
||||
finally
|
||||
{
|
||||
try { if(pToDispose != null) pToDispose.Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
#if !ModernKeePassLib
|
||||
if((f & AppRunFlags.DoEvents) != AppRunFlags.None)
|
||||
{
|
||||
List<Form> lDisabledForms = new List<Form>();
|
||||
if((f & AppRunFlags.DisableForms) != AppRunFlags.None)
|
||||
{
|
||||
foreach(Form form in Application.OpenForms)
|
||||
{
|
||||
if(!form.Enabled) continue;
|
||||
|
||||
lDisabledForms.Add(form);
|
||||
form.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
IAsyncResult ar = fnRun.BeginInvoke(null, null);
|
||||
|
||||
while(!ar.AsyncWaitHandle.WaitOne(0))
|
||||
{
|
||||
Application.DoEvents();
|
||||
Thread.Sleep(2);
|
||||
}
|
||||
|
||||
string strRet = fnRun.EndInvoke(ar);
|
||||
|
||||
for(int i = lDisabledForms.Count - 1; i >= 0; --i)
|
||||
lDisabledForms[i].Enabled = true;
|
||||
|
||||
return strRet;
|
||||
}
|
||||
#endif
|
||||
return fnRun();
|
||||
}
|
||||
|
||||
private static void EnsureNoBom(StreamWriter sw)
|
||||
{
|
||||
if(sw == null) { Debug.Assert(false); return; }
|
||||
if(!MonoWorkarounds.IsRequired(1219)) return;
|
||||
|
||||
try
|
||||
{
|
||||
Encoding enc = sw.Encoding;
|
||||
if(enc == null) { Debug.Assert(false); return; }
|
||||
byte[] pbBom = enc.GetPreamble();
|
||||
if((pbBom == null) || (pbBom.Length == 0)) return;
|
||||
|
||||
// For Mono >= 4.0 (using Microsoft's reference source)
|
||||
try
|
||||
{
|
||||
FieldInfo fi = typeof(StreamWriter).GetField("haveWrittenPreamble",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if(fi != null)
|
||||
{
|
||||
fi.SetValue(sw, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
// For Mono < 4.0
|
||||
FieldInfo fiPD = typeof(StreamWriter).GetField("preamble_done",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if(fiPD != null) fiPD.SetValue(sw, true);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Transform a key.
|
||||
/// </summary>
|
||||
/// <param name="pBuf256">Source and destination buffer.</param>
|
||||
/// <param name="pKey256">Key to use in the transformation.</param>
|
||||
/// <param name="uRounds">Number of transformation rounds.</param>
|
||||
/// <returns>Returns <c>true</c>, if the key was transformed successfully.</returns>
|
||||
public static bool TransformKey256(byte[] pBuf256, byte[] pKey256,
|
||||
ulong uRounds)
|
||||
{
|
||||
#if KeePassUAP || ModernKeePassLib
|
||||
return false;
|
||||
#else
|
||||
if(!m_bAllowNative) return false;
|
||||
|
||||
KeyValuePair<IntPtr, IntPtr> kvp = PrepareArrays256(pBuf256, pKey256);
|
||||
bool bResult = false;
|
||||
|
||||
try
|
||||
{
|
||||
bResult = NativeMethods.TransformKey(kvp.Key, kvp.Value, uRounds);
|
||||
}
|
||||
catch(Exception) { bResult = false; }
|
||||
|
||||
if(bResult) GetBuffers256(kvp, pBuf256, pKey256);
|
||||
|
||||
FreeArrays(kvp);
|
||||
return bResult;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark key transformation.
|
||||
/// </summary>
|
||||
/// <param name="uTimeMs">Number of milliseconds to perform the benchmark.</param>
|
||||
/// <param name="puRounds">Number of transformations done.</param>
|
||||
/// <returns>Returns <c>true</c>, if the benchmark was successful.</returns>
|
||||
public static bool TransformKeyBenchmark256(uint uTimeMs, out ulong puRounds)
|
||||
{
|
||||
puRounds = 0;
|
||||
|
||||
#if KeePassUAP || ModernKeePassLib
|
||||
return false;
|
||||
#else
|
||||
if(!m_bAllowNative) return false;
|
||||
|
||||
try { puRounds = NativeMethods.TransformKeyBenchmark(uTimeMs); }
|
||||
catch(Exception) { return false; }
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
private static KeyValuePair<IntPtr, IntPtr> PrepareArrays256(byte[] pBuf256,
|
||||
byte[] pKey256)
|
||||
{
|
||||
Debug.Assert((pBuf256 != null) && (pBuf256.Length == 32));
|
||||
if(pBuf256 == null) throw new ArgumentNullException("pBuf256");
|
||||
if(pBuf256.Length != 32) throw new ArgumentException();
|
||||
|
||||
Debug.Assert((pKey256 != null) && (pKey256.Length == 32));
|
||||
if(pKey256 == null) throw new ArgumentNullException("pKey256");
|
||||
if(pKey256.Length != 32) throw new ArgumentException();
|
||||
|
||||
IntPtr hBuf = Marshal.AllocHGlobal(pBuf256.Length);
|
||||
Marshal.Copy(pBuf256, 0, hBuf, pBuf256.Length);
|
||||
|
||||
IntPtr hKey = Marshal.AllocHGlobal(pKey256.Length);
|
||||
Marshal.Copy(pKey256, 0, hKey, pKey256.Length);
|
||||
|
||||
return new KeyValuePair<IntPtr, IntPtr>(hBuf, hKey);
|
||||
}
|
||||
|
||||
private static void GetBuffers256(KeyValuePair<IntPtr, IntPtr> kvpSource,
|
||||
byte[] pbDestBuf, byte[] pbDestKey)
|
||||
{
|
||||
if(kvpSource.Key != IntPtr.Zero)
|
||||
Marshal.Copy(kvpSource.Key, pbDestBuf, 0, pbDestBuf.Length);
|
||||
|
||||
if(kvpSource.Value != IntPtr.Zero)
|
||||
Marshal.Copy(kvpSource.Value, pbDestKey, 0, pbDestKey.Length);
|
||||
}
|
||||
|
||||
private static void FreeArrays(KeyValuePair<IntPtr, IntPtr> kvpPointers)
|
||||
{
|
||||
if(kvpPointers.Key != IntPtr.Zero)
|
||||
Marshal.FreeHGlobal(kvpPointers.Key);
|
||||
|
||||
if(kvpPointers.Value != IntPtr.Zero)
|
||||
Marshal.FreeHGlobal(kvpPointers.Value);
|
||||
}
|
||||
|
||||
internal static Type GetUwpType(string strType)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strType)) { Debug.Assert(false); return null; }
|
||||
|
||||
// https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/winrtclassactivator.cs
|
||||
return Type.GetType(strType + ", Windows, ContentType=WindowsRuntime", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
216
ModernKeePassLib/Native/NativeMethods.Unix.cs
Normal file
216
ModernKeePassLib/Native/NativeMethods.Unix.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Native
|
||||
{
|
||||
internal static partial class NativeMethods
|
||||
{
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct XClassHint
|
||||
{
|
||||
public IntPtr res_name;
|
||||
public IntPtr res_class;
|
||||
}
|
||||
|
||||
[DllImport("libX11")]
|
||||
private static extern int XSetClassHint(IntPtr display, IntPtr window, IntPtr class_hints);
|
||||
|
||||
private static Type m_tXplatUIX11 = null;
|
||||
private static Type GetXplatUIX11Type(bool bThrowOnError)
|
||||
{
|
||||
if(m_tXplatUIX11 == null)
|
||||
{
|
||||
// CheckState is in System.Windows.Forms
|
||||
string strTypeCS = typeof(CheckState).AssemblyQualifiedName;
|
||||
string strTypeX11 = strTypeCS.Replace("CheckState", "XplatUIX11");
|
||||
m_tXplatUIX11 = Type.GetType(strTypeX11, bThrowOnError, true);
|
||||
}
|
||||
|
||||
return m_tXplatUIX11;
|
||||
}
|
||||
|
||||
private static Type m_tHwnd = null;
|
||||
private static Type GetHwndType(bool bThrowOnError)
|
||||
{
|
||||
if(m_tHwnd == null)
|
||||
{
|
||||
// CheckState is in System.Windows.Forms
|
||||
string strTypeCS = typeof(CheckState).AssemblyQualifiedName;
|
||||
string strTypeHwnd = strTypeCS.Replace("CheckState", "Hwnd");
|
||||
m_tHwnd = Type.GetType(strTypeHwnd, bThrowOnError, true);
|
||||
}
|
||||
|
||||
return m_tHwnd;
|
||||
}
|
||||
|
||||
internal static void SetWmClass(Form f, string strName, string strClass)
|
||||
{
|
||||
if(f == null) { Debug.Assert(false); return; }
|
||||
|
||||
// The following crashes under Mac OS X (SIGSEGV in native code,
|
||||
// not just an exception), thus skip it when we're on Mac OS X;
|
||||
// https://sourceforge.net/projects/keepass/forums/forum/329221/topic/5860588
|
||||
if(NativeLib.GetPlatformID() == PlatformID.MacOSX) return;
|
||||
|
||||
try
|
||||
{
|
||||
Type tXplatUIX11 = GetXplatUIX11Type(true);
|
||||
FieldInfo fiDisplayHandle = tXplatUIX11.GetField("DisplayHandle",
|
||||
BindingFlags.NonPublic | BindingFlags.Static);
|
||||
IntPtr hDisplay = (IntPtr)fiDisplayHandle.GetValue(null);
|
||||
|
||||
Type tHwnd = GetHwndType(true);
|
||||
MethodInfo miObjectFromHandle = tHwnd.GetMethod("ObjectFromHandle",
|
||||
BindingFlags.Public | BindingFlags.Static);
|
||||
object oHwnd = miObjectFromHandle.Invoke(null, new object[] { f.Handle });
|
||||
|
||||
FieldInfo fiWholeWindow = tHwnd.GetField("whole_window",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
IntPtr hWindow = (IntPtr)fiWholeWindow.GetValue(oHwnd);
|
||||
|
||||
XClassHint xch = new XClassHint();
|
||||
xch.res_name = Marshal.StringToCoTaskMemAnsi(strName ?? string.Empty);
|
||||
xch.res_class = Marshal.StringToCoTaskMemAnsi(strClass ?? string.Empty);
|
||||
IntPtr pXch = Marshal.AllocCoTaskMem(Marshal.SizeOf(xch));
|
||||
Marshal.StructureToPtr(xch, pXch, false);
|
||||
|
||||
XSetClassHint(hDisplay, hWindow, pXch);
|
||||
|
||||
Marshal.FreeCoTaskMem(pXch);
|
||||
Marshal.FreeCoTaskMem(xch.res_name);
|
||||
Marshal.FreeCoTaskMem(xch.res_class);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
|
||||
// =============================================================
|
||||
// LibGCrypt 1.8.1
|
||||
|
||||
private const string LibGCrypt = "libgcrypt.so.20";
|
||||
|
||||
internal const int GCRY_CIPHER_AES256 = 9;
|
||||
internal const int GCRY_CIPHER_MODE_ECB = 1;
|
||||
|
||||
[DllImport(LibGCrypt)]
|
||||
internal static extern IntPtr gcry_check_version(IntPtr lpReqVersion);
|
||||
|
||||
[DllImport(LibGCrypt)]
|
||||
internal static extern uint gcry_cipher_open(ref IntPtr ph, int nAlgo,
|
||||
int nMode, uint uFlags);
|
||||
|
||||
[DllImport(LibGCrypt)]
|
||||
internal static extern void gcry_cipher_close(IntPtr h);
|
||||
|
||||
[DllImport(LibGCrypt)]
|
||||
internal static extern uint gcry_cipher_setkey(IntPtr h, IntPtr pbKey,
|
||||
IntPtr cbKey); // cbKey is size_t
|
||||
|
||||
[DllImport(LibGCrypt)]
|
||||
internal static extern uint gcry_cipher_encrypt(IntPtr h, IntPtr pbOut,
|
||||
IntPtr cbOut, IntPtr pbIn, IntPtr cbIn); // cb* are size_t
|
||||
|
||||
/* internal static IntPtr Utf8ZFromString(string str)
|
||||
{
|
||||
byte[] pb = StrUtil.Utf8.GetBytes(str ?? string.Empty);
|
||||
|
||||
IntPtr p = Marshal.AllocCoTaskMem(pb.Length + 1);
|
||||
if(p != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Copy(pb, 0, p, pb.Length);
|
||||
Marshal.WriteByte(p, pb.Length, 0);
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
internal static string Utf8ZToString(IntPtr p)
|
||||
{
|
||||
if(p == IntPtr.Zero) { Debug.Assert(false); return null; }
|
||||
|
||||
List<byte> l = new List<byte>();
|
||||
for(int i = 0; i < int.MaxValue; ++i)
|
||||
{
|
||||
byte bt = Marshal.ReadByte(p, i);
|
||||
if(bt == 0) break;
|
||||
|
||||
l.Add(bt);
|
||||
}
|
||||
|
||||
return StrUtil.Utf8.GetString(l.ToArray());
|
||||
}
|
||||
|
||||
internal static void Utf8ZFree(IntPtr p)
|
||||
{
|
||||
if(p != IntPtr.Zero) Marshal.FreeCoTaskMem(p);
|
||||
} */
|
||||
|
||||
/* // =============================================================
|
||||
// LibGLib 2
|
||||
|
||||
private const string LibGLib = "libglib-2.0.so.0";
|
||||
|
||||
internal const int G_FALSE = 0;
|
||||
|
||||
// https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html
|
||||
[DllImport(LibGLib)]
|
||||
internal static extern void g_free(IntPtr pMem); // pMem may be null
|
||||
|
||||
// =============================================================
|
||||
// LibGTK 3 (3.22.11 / 3.22.24)
|
||||
|
||||
private const string LibGtk = "libgtk-3.so.0";
|
||||
|
||||
internal static readonly IntPtr GDK_SELECTION_PRIMARY = new IntPtr(1);
|
||||
internal static readonly IntPtr GDK_SELECTION_CLIPBOARD = new IntPtr(69);
|
||||
|
||||
[DllImport(LibGtk)]
|
||||
internal static extern int gtk_init_check(IntPtr pArgc, IntPtr pArgv);
|
||||
|
||||
[DllImport(LibGtk)]
|
||||
// The returned handle is owned by GTK and must not be freed
|
||||
internal static extern IntPtr gtk_clipboard_get(IntPtr pSelection);
|
||||
|
||||
[DllImport(LibGtk)]
|
||||
internal static extern void gtk_clipboard_clear(IntPtr hClipboard);
|
||||
|
||||
[DllImport(LibGtk)]
|
||||
internal static extern IntPtr gtk_clipboard_wait_for_text(IntPtr hClipboard);
|
||||
|
||||
[DllImport(LibGtk)]
|
||||
internal static extern void gtk_clipboard_set_text(IntPtr hClipboard,
|
||||
IntPtr lpText, int cbLen);
|
||||
|
||||
[DllImport(LibGtk)]
|
||||
internal static extern void gtk_clipboard_store(IntPtr hClipboard); */
|
||||
}
|
||||
}
|
||||
260
ModernKeePassLib/Native/NativeMethods.cs
Normal file
260
ModernKeePassLib/Native/NativeMethods.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Native
|
||||
{
|
||||
internal static partial class NativeMethods
|
||||
{
|
||||
internal const int MAX_PATH = 260;
|
||||
|
||||
internal const long INVALID_HANDLE_VALUE = -1;
|
||||
|
||||
internal const uint MOVEFILE_REPLACE_EXISTING = 0x00000001;
|
||||
internal const uint MOVEFILE_COPY_ALLOWED = 0x00000002;
|
||||
|
||||
internal const uint FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
|
||||
internal const int MAX_TRANSACTION_DESCRIPTION_LENGTH = 64;
|
||||
|
||||
// internal const uint TF_SFT_SHOWNORMAL = 0x00000001;
|
||||
// internal const uint TF_SFT_HIDDEN = 0x00000008;
|
||||
|
||||
/* [DllImport("KeePassNtv32.dll", EntryPoint = "TransformKey")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TransformKey32(IntPtr pBuf256,
|
||||
IntPtr pKey256, UInt64 uRounds);
|
||||
|
||||
[DllImport("KeePassNtv64.dll", EntryPoint = "TransformKey")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TransformKey64(IntPtr pBuf256,
|
||||
IntPtr pKey256, UInt64 uRounds);
|
||||
|
||||
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
|
||||
UInt64 uRounds)
|
||||
{
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKey32(pBuf256, pKey256, uRounds);
|
||||
return TransformKey64(pBuf256, pKey256, uRounds);
|
||||
}
|
||||
|
||||
[DllImport("KeePassNtv32.dll", EntryPoint = "TransformKeyTimed")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TransformKeyTimed32(IntPtr pBuf256,
|
||||
IntPtr pKey256, ref UInt64 puRounds, UInt32 uSeconds);
|
||||
|
||||
[DllImport("KeePassNtv64.dll", EntryPoint = "TransformKeyTimed")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TransformKeyTimed64(IntPtr pBuf256,
|
||||
IntPtr pKey256, ref UInt64 puRounds, UInt32 uSeconds);
|
||||
|
||||
internal static bool TransformKeyTimed(IntPtr pBuf256, IntPtr pKey256,
|
||||
ref UInt64 puRounds, UInt32 uSeconds)
|
||||
{
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds);
|
||||
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
|
||||
} */
|
||||
|
||||
#if !KeePassUAP
|
||||
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKey256")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TransformKey32(IntPtr pBuf256,
|
||||
IntPtr pKey256, UInt64 uRounds);
|
||||
|
||||
[DllImport("KeePassLibC64.dll", EntryPoint = "TransformKey256")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TransformKey64(IntPtr pBuf256,
|
||||
IntPtr pKey256, UInt64 uRounds);
|
||||
|
||||
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
|
||||
UInt64 uRounds)
|
||||
{
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKey32(pBuf256, pKey256, uRounds);
|
||||
return TransformKey64(pBuf256, pKey256, uRounds);
|
||||
}
|
||||
|
||||
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKeyBenchmark256")]
|
||||
private static extern UInt64 TransformKeyBenchmark32(UInt32 uTimeMs);
|
||||
|
||||
[DllImport("KeePassLibC64.dll", EntryPoint = "TransformKeyBenchmark256")]
|
||||
private static extern UInt64 TransformKeyBenchmark64(UInt32 uTimeMs);
|
||||
|
||||
internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs)
|
||||
{
|
||||
if(IntPtr.Size == 4)
|
||||
return TransformKeyBenchmark32(uTimeMs);
|
||||
return TransformKeyBenchmark64(uTimeMs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* [DllImport("KeePassLibC32.dll", EntryPoint = "TF_ShowLangBar")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TF_ShowLangBar32(UInt32 dwFlags);
|
||||
|
||||
[DllImport("KeePassLibC64.dll", EntryPoint = "TF_ShowLangBar")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool TF_ShowLangBar64(UInt32 dwFlags);
|
||||
|
||||
internal static bool TfShowLangBar(uint dwFlags)
|
||||
{
|
||||
if(IntPtr.Size == 4) return TF_ShowLangBar32(dwFlags);
|
||||
return TF_ShowLangBar64(dwFlags);
|
||||
} */
|
||||
|
||||
[DllImport("KeePassLibC32.dll", EntryPoint = "ProtectProcessWithDacl")]
|
||||
private static extern void ProtectProcessWithDacl32();
|
||||
|
||||
[DllImport("KeePassLibC64.dll", EntryPoint = "ProtectProcessWithDacl")]
|
||||
private static extern void ProtectProcessWithDacl64();
|
||||
|
||||
internal static void ProtectProcessWithDacl()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(NativeLib.IsUnix()) return;
|
||||
|
||||
if(IntPtr.Size == 4) ProtectProcessWithDacl32();
|
||||
else ProtectProcessWithDacl64();
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
|
||||
SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetVolumeInformation(string lpRootPathName,
|
||||
StringBuilder lpVolumeNameBuffer, UInt32 nVolumeNameSize,
|
||||
ref UInt32 lpVolumeSerialNumber, ref UInt32 lpMaximumComponentLength,
|
||||
ref UInt32 lpFileSystemFlags, StringBuilder lpFileSystemNameBuffer,
|
||||
UInt32 nFileSystemNameSize);
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
|
||||
SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool MoveFileEx(string lpExistingFileName,
|
||||
string lpNewFileName, UInt32 dwFlags);
|
||||
|
||||
[DllImport("KtmW32.dll", CharSet = CharSet.Unicode, ExactSpelling = true,
|
||||
SetLastError = true)]
|
||||
internal static extern IntPtr CreateTransaction(IntPtr lpTransactionAttributes,
|
||||
IntPtr lpUOW, UInt32 dwCreateOptions, UInt32 dwIsolationLevel,
|
||||
UInt32 dwIsolationFlags, UInt32 dwTimeout, string lpDescription);
|
||||
|
||||
[DllImport("KtmW32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool CommitTransaction(IntPtr hTransaction);
|
||||
|
||||
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
|
||||
SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool MoveFileTransacted(string lpExistingFileName,
|
||||
string lpNewFileName, IntPtr lpProgressRoutine, IntPtr lpData,
|
||||
UInt32 dwFlags, IntPtr hTransaction);
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool PathRelativePathTo([Out] StringBuilder pszPath,
|
||||
[In] string pszFrom, uint dwAttrFrom, [In] string pszTo, uint dwAttrTo);
|
||||
|
||||
[DllImport("ShlWApi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||
private static extern int StrCmpLogicalW(string x, string y);
|
||||
|
||||
private static bool? m_obSupportsLogicalCmp = null;
|
||||
|
||||
private static void TestNaturalComparisonsSupport()
|
||||
{
|
||||
try
|
||||
{
|
||||
StrCmpLogicalW("0", "0"); // Throws exception if unsupported
|
||||
m_obSupportsLogicalCmp = true;
|
||||
}
|
||||
catch(Exception) { m_obSupportsLogicalCmp = false; }
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static bool SupportsStrCmpNaturally
|
||||
{
|
||||
get
|
||||
{
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
if(!m_obSupportsLogicalCmp.HasValue)
|
||||
TestNaturalComparisonsSupport();
|
||||
|
||||
return m_obSupportsLogicalCmp.Value;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
internal static int StrCmpNaturally(string x, string y)
|
||||
{
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
if(!NativeMethods.SupportsStrCmpNaturally)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return string.Compare(x, y, true);
|
||||
}
|
||||
|
||||
return StrCmpLogicalW(x, y);
|
||||
#else
|
||||
Debug.Assert(false);
|
||||
return string.Compare(x, y, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static string GetUserRuntimeDir()
|
||||
{
|
||||
#if KeePassLibSD
|
||||
return Path.GetTempPath();
|
||||
#else
|
||||
#if KeePassUAP
|
||||
string strRtDir = EnvironmentExt.AppDataLocalFolderPath;
|
||||
#else
|
||||
string strRtDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
|
||||
if(string.IsNullOrEmpty(strRtDir))
|
||||
strRtDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
if(string.IsNullOrEmpty(strRtDir))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return Path.GetTempPath(); // Not UrlUtil (otherwise cyclic)
|
||||
}
|
||||
#endif
|
||||
|
||||
strRtDir = UrlUtil.EnsureTerminatingSeparator(strRtDir, false);
|
||||
strRtDir += PwDefs.ShortProductName;
|
||||
|
||||
return strRtDir;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
127
ModernKeePassLib/PwCustomIcon.cs
Normal file
127
ModernKeePassLib/PwCustomIcon.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
#if !ModernKeePassLib
|
||||
using System.Drawing;
|
||||
#else
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom icon. <c>PwCustomIcon</c> objects are immutable.
|
||||
/// </summary>
|
||||
public sealed class PwCustomIcon
|
||||
{
|
||||
private readonly PwUuid m_pwUuid;
|
||||
private readonly byte[] m_pbImageDataPng;
|
||||
|
||||
private readonly Image m_imgOrg;
|
||||
private Dictionary<long, Image> m_dImageCache = new Dictionary<long, Image>();
|
||||
|
||||
// Recommended maximum sizes, not obligatory
|
||||
internal const int MaxWidth = 128;
|
||||
internal const int MaxHeight = 128;
|
||||
|
||||
public PwUuid Uuid
|
||||
{
|
||||
get { return m_pwUuid; }
|
||||
}
|
||||
|
||||
public byte[] ImageDataPng
|
||||
{
|
||||
get { return m_pbImageDataPng; }
|
||||
}
|
||||
|
||||
[Obsolete("Use GetImage instead.")]
|
||||
public Image Image
|
||||
{
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
get { return GetImage(16, 16); } // Backward compatibility
|
||||
#else
|
||||
get { return GetImage(); } // Backward compatibility
|
||||
#endif
|
||||
}
|
||||
|
||||
public PwCustomIcon(PwUuid pwUuid, byte[] pbImageDataPng)
|
||||
{
|
||||
Debug.Assert(pwUuid != null);
|
||||
if(pwUuid == null) throw new ArgumentNullException("pwUuid");
|
||||
Debug.Assert(!pwUuid.Equals(PwUuid.Zero));
|
||||
if(pwUuid.Equals(PwUuid.Zero)) throw new ArgumentException("pwUuid == 0.");
|
||||
Debug.Assert(pbImageDataPng != null);
|
||||
if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng");
|
||||
|
||||
m_pwUuid = pwUuid;
|
||||
m_pbImageDataPng = pbImageDataPng;
|
||||
|
||||
// MemoryStream ms = new MemoryStream(m_pbImageDataPng, false);
|
||||
// m_imgOrg = Image.FromStream(ms);
|
||||
// ms.Close();
|
||||
try { m_imgOrg = GfxUtil.LoadImage(m_pbImageDataPng); }
|
||||
catch(Exception) { Debug.Assert(false); m_imgOrg = null; }
|
||||
|
||||
if(m_imgOrg != null)
|
||||
m_dImageCache[GetID((int)m_imgOrg.Width, (int)m_imgOrg.Height)] =
|
||||
m_imgOrg;
|
||||
}
|
||||
|
||||
private static long GetID(int w, int h)
|
||||
{
|
||||
return (((long)w << 32) ^ (long)h);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the icon as an <c>Image</c> (original size).
|
||||
/// </summary>
|
||||
public Image GetImage()
|
||||
{
|
||||
return m_imgOrg;
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
/// <summary>
|
||||
/// Get the icon as an <c>Image</c> (with the specified size).
|
||||
/// </summary>
|
||||
/// <param name="w">Width of the returned image.</param>
|
||||
/// <param name="h">Height of the returned image.</param>
|
||||
public Image GetImage(int w, int h)
|
||||
{
|
||||
if(w < 0) { Debug.Assert(false); return m_imgOrg; }
|
||||
if(h < 0) { Debug.Assert(false); return m_imgOrg; }
|
||||
if(m_imgOrg == null) return null;
|
||||
|
||||
long lID = GetID(w, h);
|
||||
|
||||
Image img;
|
||||
if(m_dImageCache.TryGetValue(lID, out img)) return img;
|
||||
|
||||
img = GfxUtil.ScaleImage(m_imgOrg, w, h, ScaleTransformFlags.UIIcon);
|
||||
m_dImageCache[lID] = img;
|
||||
return img;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
2077
ModernKeePassLib/PwDatabase.cs
Normal file
2077
ModernKeePassLib/PwDatabase.cs
Normal file
File diff suppressed because it is too large
Load Diff
517
ModernKeePassLib/PwDefs.cs
Normal file
517
ModernKeePassLib/PwDefs.cs
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Serialization;
|
||||
|
||||
namespace ModernKeePassLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains KeePassLib-global definitions and enums.
|
||||
/// </summary>
|
||||
public static class PwDefs
|
||||
{
|
||||
/// <summary>
|
||||
/// The product name.
|
||||
/// </summary>
|
||||
public static readonly string ProductName = "KeePass Password Safe";
|
||||
|
||||
/// <summary>
|
||||
/// A short, simple string representing the product name. The string
|
||||
/// should contain no spaces, directory separator characters, etc.
|
||||
/// </summary>
|
||||
public static readonly string ShortProductName = "KeePass";
|
||||
|
||||
internal const string UnixName = "keepass2";
|
||||
internal const string ResClass = "KeePass2"; // With initial capital
|
||||
|
||||
/// <summary>
|
||||
/// Version, encoded as 32-bit unsigned integer.
|
||||
/// 2.00 = 0x02000000, 2.01 = 0x02000100, ..., 2.18 = 0x02010800.
|
||||
/// As of 2.19, the version is encoded component-wise per byte,
|
||||
/// e.g. 2.19 = 0x02130000.
|
||||
/// It is highly recommended to use <c>FileVersion64</c> instead.
|
||||
/// </summary>
|
||||
public static readonly uint Version32 = 0x02290000;
|
||||
|
||||
/// <summary>
|
||||
/// Version, encoded as 64-bit unsigned integer
|
||||
/// (component-wise, 16 bits per component).
|
||||
/// </summary>
|
||||
public static readonly ulong FileVersion64 = 0x0002002900000000UL;
|
||||
|
||||
/// <summary>
|
||||
/// Version, encoded as string.
|
||||
/// </summary>
|
||||
public static readonly string VersionString = "2.41";
|
||||
|
||||
public static readonly string Copyright = @"Copyright © 2003-2019 Dominik Reichl";
|
||||
|
||||
/// <summary>
|
||||
/// Product website URL. Terminated by a forward slash.
|
||||
/// </summary>
|
||||
public static readonly string HomepageUrl = "https://keepass.info/";
|
||||
|
||||
/// <summary>
|
||||
/// URL to the online translations page.
|
||||
/// </summary>
|
||||
public static readonly string TranslationsUrl = "https://keepass.info/translations.html";
|
||||
|
||||
/// <summary>
|
||||
/// URL to the online plugins page.
|
||||
/// </summary>
|
||||
public static readonly string PluginsUrl = "https://keepass.info/plugins.html";
|
||||
|
||||
/// <summary>
|
||||
/// Product donations URL.
|
||||
/// </summary>
|
||||
public static readonly string DonationsUrl = "https://keepass.info/donate.html";
|
||||
|
||||
/// <summary>
|
||||
/// URL to the root path of the online KeePass help. Terminated by
|
||||
/// a forward slash.
|
||||
/// </summary>
|
||||
public static readonly string HelpUrl = "https://keepass.info/help/";
|
||||
|
||||
/// <summary>
|
||||
/// URL to a TXT file (eventually compressed) that contains information
|
||||
/// about the latest KeePass version available on the website.
|
||||
/// </summary>
|
||||
public static readonly string VersionUrl = "https://www.dominik-reichl.de/update/version2x.txt.gz";
|
||||
|
||||
/// <summary>
|
||||
/// A <c>DateTime</c> object that represents the time when the assembly
|
||||
/// was loaded.
|
||||
/// </summary>
|
||||
public static readonly DateTime DtDefaultNow = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Default number of master key encryption/transformation rounds
|
||||
/// (making dictionary attacks harder).
|
||||
/// </summary>
|
||||
public static readonly ulong DefaultKeyEncryptionRounds = 60000;
|
||||
|
||||
/// <summary>
|
||||
/// Default identifier string for the title field.
|
||||
/// Should not contain spaces, tabs or other whitespace.
|
||||
/// </summary>
|
||||
public const string TitleField = "Title";
|
||||
// Const instead of static readonly for backward compatibility with plugins
|
||||
|
||||
/// <summary>
|
||||
/// Default identifier string for the user name field.
|
||||
/// Should not contain spaces, tabs or other whitespace.
|
||||
/// </summary>
|
||||
public const string UserNameField = "UserName";
|
||||
// Const instead of static readonly for backward compatibility with plugins
|
||||
|
||||
/// <summary>
|
||||
/// Default identifier string for the password field.
|
||||
/// Should not contain spaces, tabs or other whitespace.
|
||||
/// </summary>
|
||||
public const string PasswordField = "Password";
|
||||
// Const instead of static readonly for backward compatibility with plugins
|
||||
|
||||
/// <summary>
|
||||
/// Default identifier string for the URL field.
|
||||
/// Should not contain spaces, tabs or other whitespace.
|
||||
/// </summary>
|
||||
public const string UrlField = "URL";
|
||||
// Const instead of static readonly for backward compatibility with plugins
|
||||
|
||||
/// <summary>
|
||||
/// Default identifier string for the notes field.
|
||||
/// Should not contain spaces, tabs or other whitespace.
|
||||
/// </summary>
|
||||
public const string NotesField = "Notes";
|
||||
// Const instead of static readonly for backward compatibility with plugins
|
||||
|
||||
/// <summary>
|
||||
/// Default identifier string for the field which will contain TAN indices.
|
||||
/// </summary>
|
||||
public static readonly string TanIndexField = UserNameField;
|
||||
|
||||
/// <summary>
|
||||
/// Default title of an entry that is really a TAN entry.
|
||||
/// </summary>
|
||||
public static readonly string TanTitle = @"<TAN>";
|
||||
|
||||
/// <summary>
|
||||
/// Prefix of a custom auto-type string field.
|
||||
/// </summary>
|
||||
public static readonly string AutoTypeStringPrefix = "S:";
|
||||
|
||||
/// <summary>
|
||||
/// Default string representing a hidden password.
|
||||
/// </summary>
|
||||
public static readonly string HiddenPassword = "********";
|
||||
|
||||
/// <summary>
|
||||
/// Default auto-type keystroke sequence. If no custom sequence is
|
||||
/// specified, this sequence is used.
|
||||
/// </summary>
|
||||
public static readonly string DefaultAutoTypeSequence = @"{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
||||
|
||||
/// <summary>
|
||||
/// Default auto-type keystroke sequence for TAN entries. If no custom
|
||||
/// sequence is specified, this sequence is used.
|
||||
/// </summary>
|
||||
public static readonly string DefaultAutoTypeSequenceTan = @"{PASSWORD}";
|
||||
|
||||
/// <summary>
|
||||
/// Check if a name is a standard field name.
|
||||
/// </summary>
|
||||
/// <param name="strFieldName">Input field name.</param>
|
||||
/// <returns>Returns <c>true</c>, if the field name is a standard
|
||||
/// field name (title, user name, password, ...), otherwise <c>false</c>.</returns>
|
||||
public static bool IsStandardField(string strFieldName)
|
||||
{
|
||||
Debug.Assert(strFieldName != null); if(strFieldName == null) return false;
|
||||
|
||||
if(strFieldName.Equals(TitleField)) return true;
|
||||
if(strFieldName.Equals(UserNameField)) return true;
|
||||
if(strFieldName.Equals(PasswordField)) return true;
|
||||
if(strFieldName.Equals(UrlField)) return true;
|
||||
if(strFieldName.Equals(NotesField)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> GetStandardFields()
|
||||
{
|
||||
List<string> l = new List<string>();
|
||||
|
||||
l.Add(TitleField);
|
||||
l.Add(UserNameField);
|
||||
l.Add(PasswordField);
|
||||
l.Add(UrlField);
|
||||
l.Add(NotesField);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether an entry is a TAN entry.
|
||||
/// </summary>
|
||||
public static bool IsTanEntry(PwEntry pe)
|
||||
{
|
||||
if(pe == null) { Debug.Assert(false); return false; }
|
||||
|
||||
return (pe.Strings.ReadSafe(PwDefs.TitleField) == TanTitle);
|
||||
}
|
||||
|
||||
internal static string GetTranslationDisplayVersion(string strFileVersion)
|
||||
{
|
||||
if(strFileVersion == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
if(strFileVersion == "2.39") return "2.39 / 2.39.1";
|
||||
|
||||
return strFileVersion;
|
||||
}
|
||||
}
|
||||
|
||||
// #pragma warning disable 1591 // Missing XML comments warning
|
||||
/// <summary>
|
||||
/// Search parameters for group and entry searches.
|
||||
/// </summary>
|
||||
public sealed class SearchParameters
|
||||
{
|
||||
private string m_strText = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string SearchString
|
||||
{
|
||||
get { return m_strText; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strText = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_bRegex = false;
|
||||
[DefaultValue(false)]
|
||||
public bool RegularExpression
|
||||
{
|
||||
get { return m_bRegex; }
|
||||
set { m_bRegex = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInTitles = true;
|
||||
[DefaultValue(true)]
|
||||
public bool SearchInTitles
|
||||
{
|
||||
get { return m_bSearchInTitles; }
|
||||
set { m_bSearchInTitles = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInUserNames = true;
|
||||
[DefaultValue(true)]
|
||||
public bool SearchInUserNames
|
||||
{
|
||||
get { return m_bSearchInUserNames; }
|
||||
set { m_bSearchInUserNames = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInPasswords = false;
|
||||
[DefaultValue(false)]
|
||||
public bool SearchInPasswords
|
||||
{
|
||||
get { return m_bSearchInPasswords; }
|
||||
set { m_bSearchInPasswords = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInUrls = true;
|
||||
[DefaultValue(true)]
|
||||
public bool SearchInUrls
|
||||
{
|
||||
get { return m_bSearchInUrls; }
|
||||
set { m_bSearchInUrls = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInNotes = true;
|
||||
[DefaultValue(true)]
|
||||
public bool SearchInNotes
|
||||
{
|
||||
get { return m_bSearchInNotes; }
|
||||
set { m_bSearchInNotes = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInOther = true;
|
||||
[DefaultValue(true)]
|
||||
public bool SearchInOther
|
||||
{
|
||||
get { return m_bSearchInOther; }
|
||||
set { m_bSearchInOther = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInStringNames = false;
|
||||
[DefaultValue(false)]
|
||||
public bool SearchInStringNames
|
||||
{
|
||||
get { return m_bSearchInStringNames; }
|
||||
set { m_bSearchInStringNames = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInTags = true;
|
||||
[DefaultValue(true)]
|
||||
public bool SearchInTags
|
||||
{
|
||||
get { return m_bSearchInTags; }
|
||||
set { m_bSearchInTags = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInUuids = false;
|
||||
[DefaultValue(false)]
|
||||
public bool SearchInUuids
|
||||
{
|
||||
get { return m_bSearchInUuids; }
|
||||
set { m_bSearchInUuids = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInGroupPaths = false;
|
||||
[DefaultValue(false)]
|
||||
public bool SearchInGroupPaths
|
||||
{
|
||||
get { return m_bSearchInGroupPaths; }
|
||||
set { m_bSearchInGroupPaths = value; }
|
||||
}
|
||||
|
||||
private bool m_bSearchInGroupNames = false;
|
||||
[DefaultValue(false)]
|
||||
public bool SearchInGroupNames
|
||||
{
|
||||
get { return m_bSearchInGroupNames; }
|
||||
set { m_bSearchInGroupNames = value; }
|
||||
}
|
||||
|
||||
#if ModernKeePassLib || KeePassUAP
|
||||
private StringComparison m_scType = StringComparison.OrdinalIgnoreCase;
|
||||
#else
|
||||
private StringComparison m_scType = StringComparison.InvariantCultureIgnoreCase;
|
||||
#endif
|
||||
/// <summary>
|
||||
/// String comparison type. Specifies the condition when the specified
|
||||
/// text matches a group/entry string.
|
||||
/// </summary>
|
||||
public StringComparison ComparisonMode
|
||||
{
|
||||
get { return m_scType; }
|
||||
set { m_scType = value; }
|
||||
}
|
||||
|
||||
private bool m_bExcludeExpired = false;
|
||||
[DefaultValue(false)]
|
||||
public bool ExcludeExpired
|
||||
{
|
||||
get { return m_bExcludeExpired; }
|
||||
set { m_bExcludeExpired = value; }
|
||||
}
|
||||
|
||||
private bool m_bRespectEntrySearchingDisabled = true;
|
||||
[DefaultValue(true)]
|
||||
public bool RespectEntrySearchingDisabled
|
||||
{
|
||||
get { return m_bRespectEntrySearchingDisabled; }
|
||||
set { m_bRespectEntrySearchingDisabled = value; }
|
||||
}
|
||||
|
||||
private StrPwEntryDelegate m_fnDataTrf = null;
|
||||
[XmlIgnore]
|
||||
public StrPwEntryDelegate DataTransformationFn
|
||||
{
|
||||
get { return m_fnDataTrf; }
|
||||
set { m_fnDataTrf = value; }
|
||||
}
|
||||
|
||||
private string m_strDataTrf = string.Empty;
|
||||
/// <summary>
|
||||
/// Only for serialization.
|
||||
/// </summary>
|
||||
[DefaultValue("")]
|
||||
public string DataTransformation
|
||||
{
|
||||
get { return m_strDataTrf; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strDataTrf = value;
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public static SearchParameters None
|
||||
{
|
||||
get
|
||||
{
|
||||
SearchParameters sp = new SearchParameters();
|
||||
|
||||
Debug.Assert(sp.m_strText.Length == 0);
|
||||
Debug.Assert(!sp.m_bRegex);
|
||||
sp.m_bSearchInTitles = false;
|
||||
sp.m_bSearchInUserNames = false;
|
||||
Debug.Assert(!sp.m_bSearchInPasswords);
|
||||
sp.m_bSearchInUrls = false;
|
||||
sp.m_bSearchInNotes = false;
|
||||
sp.m_bSearchInOther = false;
|
||||
Debug.Assert(!sp.m_bSearchInStringNames);
|
||||
sp.m_bSearchInTags = false;
|
||||
Debug.Assert(!sp.m_bSearchInUuids);
|
||||
Debug.Assert(!sp.m_bSearchInGroupPaths);
|
||||
Debug.Assert(!sp.m_bSearchInGroupNames);
|
||||
// Debug.Assert(sp.m_scType == StringComparison.InvariantCultureIgnoreCase);
|
||||
Debug.Assert(!sp.m_bExcludeExpired);
|
||||
Debug.Assert(sp.m_bRespectEntrySearchingDisabled);
|
||||
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new search parameters object.
|
||||
/// </summary>
|
||||
public SearchParameters()
|
||||
{
|
||||
}
|
||||
|
||||
public SearchParameters Clone()
|
||||
{
|
||||
return (SearchParameters)this.MemberwiseClone();
|
||||
}
|
||||
}
|
||||
// #pragma warning restore 1591 // Missing XML comments warning
|
||||
|
||||
// #pragma warning disable 1591 // Missing XML comments warning
|
||||
/// <summary>
|
||||
/// Memory protection configuration structure (for default fields).
|
||||
/// </summary>
|
||||
public sealed class MemoryProtectionConfig : IDeepCloneable<MemoryProtectionConfig>
|
||||
{
|
||||
public bool ProtectTitle = false;
|
||||
public bool ProtectUserName = false;
|
||||
public bool ProtectPassword = true;
|
||||
public bool ProtectUrl = false;
|
||||
public bool ProtectNotes = false;
|
||||
|
||||
// public bool AutoEnableVisualHiding = false;
|
||||
|
||||
public MemoryProtectionConfig CloneDeep()
|
||||
{
|
||||
return (MemoryProtectionConfig)this.MemberwiseClone();
|
||||
}
|
||||
|
||||
public bool GetProtection(string strField)
|
||||
{
|
||||
if(strField == PwDefs.TitleField) return this.ProtectTitle;
|
||||
if(strField == PwDefs.UserNameField) return this.ProtectUserName;
|
||||
if(strField == PwDefs.PasswordField) return this.ProtectPassword;
|
||||
if(strField == PwDefs.UrlField) return this.ProtectUrl;
|
||||
if(strField == PwDefs.NotesField) return this.ProtectNotes;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// #pragma warning restore 1591 // Missing XML comments warning
|
||||
|
||||
public sealed class ObjectTouchedEventArgs : EventArgs
|
||||
{
|
||||
private object m_o;
|
||||
public object Object { get { return m_o; } }
|
||||
|
||||
private bool m_bModified;
|
||||
public bool Modified { get { return m_bModified; } }
|
||||
|
||||
private bool m_bParentsTouched;
|
||||
public bool ParentsTouched { get { return m_bParentsTouched; } }
|
||||
|
||||
public ObjectTouchedEventArgs(object o, bool bModified,
|
||||
bool bParentsTouched)
|
||||
{
|
||||
m_o = o;
|
||||
m_bModified = bModified;
|
||||
m_bParentsTouched = bParentsTouched;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class IOAccessEventArgs : EventArgs
|
||||
{
|
||||
private IOConnectionInfo m_ioc;
|
||||
public IOConnectionInfo IOConnectionInfo { get { return m_ioc; } }
|
||||
|
||||
private IOConnectionInfo m_ioc2;
|
||||
public IOConnectionInfo IOConnectionInfo2 { get { return m_ioc2; } }
|
||||
|
||||
private IOAccessType m_t;
|
||||
public IOAccessType Type { get { return m_t; } }
|
||||
|
||||
public IOAccessEventArgs(IOConnectionInfo ioc, IOConnectionInfo ioc2,
|
||||
IOAccessType t)
|
||||
{
|
||||
m_ioc = ioc;
|
||||
m_ioc2 = ioc2;
|
||||
m_t = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
86
ModernKeePassLib/PwDeletedObject.cs
Normal file
86
ModernKeePassLib/PwDeletedObject.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Interfaces;
|
||||
|
||||
namespace ModernKeePassLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an object that has been deleted.
|
||||
/// </summary>
|
||||
public sealed class PwDeletedObject : IDeepCloneable<PwDeletedObject>
|
||||
{
|
||||
private PwUuid m_uuid = PwUuid.Zero;
|
||||
/// <summary>
|
||||
/// UUID of the entry that has been deleted.
|
||||
/// </summary>
|
||||
public PwUuid Uuid
|
||||
{
|
||||
get { return m_uuid; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_uuid = value;
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime m_dtDeletionTime = PwDefs.DtDefaultNow;
|
||||
/// <summary>
|
||||
/// The date/time when the entry has been deleted.
|
||||
/// </summary>
|
||||
public DateTime DeletionTime
|
||||
{
|
||||
get { return m_dtDeletionTime; }
|
||||
set { m_dtDeletionTime = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new <c>PwDeletedObject</c> object.
|
||||
/// </summary>
|
||||
public PwDeletedObject()
|
||||
{
|
||||
}
|
||||
|
||||
public PwDeletedObject(PwUuid uuid, DateTime dtDeletionTime)
|
||||
{
|
||||
if(uuid == null) throw new ArgumentNullException("uuid");
|
||||
|
||||
m_uuid = uuid;
|
||||
m_dtDeletionTime = dtDeletionTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone the object.
|
||||
/// </summary>
|
||||
/// <returns>Value copy of the current object.</returns>
|
||||
public PwDeletedObject CloneDeep()
|
||||
{
|
||||
PwDeletedObject pdo = new PwDeletedObject();
|
||||
|
||||
pdo.m_uuid = m_uuid; // PwUuid objects are immutable
|
||||
pdo.m_dtDeletionTime = m_dtDeletionTime;
|
||||
|
||||
return pdo;
|
||||
}
|
||||
}
|
||||
}
|
||||
947
ModernKeePassLib/PwEntry.cs
Normal file
947
ModernKeePassLib/PwEntry.cs
Normal file
@@ -0,0 +1,947 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Drawing;
|
||||
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A class representing a password entry. A password entry consists of several
|
||||
/// fields like title, user name, password, etc. Each password entry has a
|
||||
/// unique ID (UUID).
|
||||
/// </summary>
|
||||
public sealed class PwEntry : ITimeLogger, IStructureItem, IDeepCloneable<PwEntry>
|
||||
{
|
||||
private PwUuid m_uuid = PwUuid.Zero;
|
||||
private PwGroup m_pParentGroup = null;
|
||||
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
||||
|
||||
private ProtectedStringDictionary m_listStrings = new ProtectedStringDictionary();
|
||||
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
|
||||
private AutoTypeConfig m_listAutoType = new AutoTypeConfig();
|
||||
private PwObjectList<PwEntry> m_listHistory = new PwObjectList<PwEntry>();
|
||||
|
||||
private PwIcon m_pwIcon = PwIcon.Key;
|
||||
private PwUuid m_pwCustomIconID = PwUuid.Zero;
|
||||
|
||||
private Color m_clrForeground = Color.Empty;
|
||||
private Color m_clrBackground = Color.Empty;
|
||||
|
||||
private DateTime m_tCreation = PwDefs.DtDefaultNow;
|
||||
private DateTime m_tLastMod = PwDefs.DtDefaultNow;
|
||||
private DateTime m_tLastAccess = PwDefs.DtDefaultNow;
|
||||
private DateTime m_tExpire = PwDefs.DtDefaultNow;
|
||||
private bool m_bExpires = false;
|
||||
private ulong m_uUsageCount = 0;
|
||||
|
||||
private string m_strOverrideUrl = string.Empty;
|
||||
|
||||
private List<string> m_vTags = new List<string>();
|
||||
|
||||
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
|
||||
|
||||
/// <summary>
|
||||
/// UUID of this entry.
|
||||
/// </summary>
|
||||
public PwUuid Uuid
|
||||
{
|
||||
get { return m_uuid; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_uuid = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to a group which contains the current entry.
|
||||
/// </summary>
|
||||
public PwGroup ParentGroup
|
||||
{
|
||||
get { return m_pParentGroup; }
|
||||
|
||||
// Plugins: use <c>PwGroup.AddEntry</c> instead.
|
||||
internal set { m_pParentGroup = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when the location of the object was last changed.
|
||||
/// </summary>
|
||||
public DateTime LocationChanged
|
||||
{
|
||||
get { return m_tParentGroupLastMod; }
|
||||
set { m_tParentGroupLastMod = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set all entry strings.
|
||||
/// </summary>
|
||||
public ProtectedStringDictionary Strings
|
||||
{
|
||||
get { return m_listStrings; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_listStrings = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set all entry binaries.
|
||||
/// </summary>
|
||||
public ProtectedBinaryDictionary Binaries
|
||||
{
|
||||
get { return m_listBinaries; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_listBinaries = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set all auto-type window/keystroke sequence associations.
|
||||
/// </summary>
|
||||
public AutoTypeConfig AutoType
|
||||
{
|
||||
get { return m_listAutoType; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_listAutoType = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all previous versions of this entry (backups).
|
||||
/// </summary>
|
||||
public PwObjectList<PwEntry> History
|
||||
{
|
||||
get { return m_listHistory; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_listHistory = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image ID specifying the icon that will be used for this entry.
|
||||
/// </summary>
|
||||
public PwIcon IconId
|
||||
{
|
||||
get { return m_pwIcon; }
|
||||
set { m_pwIcon = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the custom icon ID. This value is 0, if no custom icon is
|
||||
/// being used (i.e. the icon specified by the <c>IconID</c> property
|
||||
/// should be displayed).
|
||||
/// </summary>
|
||||
public PwUuid CustomIconUuid
|
||||
{
|
||||
get { return m_pwCustomIconID; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
m_pwCustomIconID = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the foreground color of this entry.
|
||||
/// </summary>
|
||||
public Color ForegroundColor
|
||||
{
|
||||
get { return m_clrForeground; }
|
||||
set { m_clrForeground = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the background color of this entry.
|
||||
/// </summary>
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get { return m_clrBackground; }
|
||||
set { m_clrBackground = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when this entry was created.
|
||||
/// </summary>
|
||||
public DateTime CreationTime
|
||||
{
|
||||
get { return m_tCreation; }
|
||||
set { m_tCreation = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when this entry was last modified.
|
||||
/// </summary>
|
||||
public DateTime LastModificationTime
|
||||
{
|
||||
get { return m_tLastMod; }
|
||||
set { m_tLastMod = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when this entry was last accessed (read).
|
||||
/// </summary>
|
||||
public DateTime LastAccessTime
|
||||
{
|
||||
get { return m_tLastAccess; }
|
||||
set { m_tLastAccess = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when this entry expires. Use the <c>Expires</c> property
|
||||
/// to specify if the entry does actually expire or not.
|
||||
/// </summary>
|
||||
public DateTime ExpiryTime
|
||||
{
|
||||
get { return m_tExpire; }
|
||||
set { m_tExpire = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether the entry expires or not.
|
||||
/// </summary>
|
||||
public bool Expires
|
||||
{
|
||||
get { return m_bExpires; }
|
||||
set { m_bExpires = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the usage count of the entry. To increase the usage
|
||||
/// count by one, use the <c>Touch</c> function.
|
||||
/// </summary>
|
||||
public ulong UsageCount
|
||||
{
|
||||
get { return m_uUsageCount; }
|
||||
set { m_uUsageCount = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry-specific override URL. If this string is non-empty,
|
||||
/// </summary>
|
||||
public string OverrideUrl
|
||||
{
|
||||
get { return m_strOverrideUrl; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strOverrideUrl = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of tags associated with this entry.
|
||||
/// </summary>
|
||||
public List<string> Tags
|
||||
{
|
||||
get { return m_vTags; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_vTags = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new, empty password entry. Member variables will be initialized
|
||||
/// to their default values.
|
||||
/// </summary>
|
||||
/// <param name="bCreateNewUuid">If <c>true</c>, a new UUID will be created
|
||||
/// for this entry. If <c>false</c>, the UUID is zero and you must set it
|
||||
/// manually later.</param>
|
||||
/// <param name="bSetTimes">If <c>true</c>, the creation, last modification
|
||||
/// and last access times will be set to the current system time.</param>
|
||||
public PwEntry(bool bCreateNewUuid, bool bSetTimes)
|
||||
{
|
||||
if(bCreateNewUuid) m_uuid = new PwUuid(true);
|
||||
|
||||
if(bSetTimes)
|
||||
{
|
||||
DateTime dtNow = DateTime.UtcNow;
|
||||
m_tCreation = dtNow;
|
||||
m_tLastMod = dtNow;
|
||||
m_tLastAccess = dtNow;
|
||||
m_tParentGroupLastMod = dtNow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new, empty password entry. Member variables will be initialized
|
||||
/// to their default values.
|
||||
/// </summary>
|
||||
/// <param name="pwParentGroup">Reference to the containing group, this
|
||||
/// parameter may be <c>null</c> and set later manually.</param>
|
||||
/// <param name="bCreateNewUuid">If <c>true</c>, a new UUID will be created
|
||||
/// for this entry. If <c>false</c>, the UUID is zero and you must set it
|
||||
/// manually later.</param>
|
||||
/// <param name="bSetTimes">If <c>true</c>, the creation, last modification
|
||||
/// and last access times will be set to the current system time.</param>
|
||||
[Obsolete("Use a different constructor. To add an entry to a group, use AddEntry of PwGroup.")]
|
||||
public PwEntry(PwGroup pwParentGroup, bool bCreateNewUuid, bool bSetTimes)
|
||||
{
|
||||
m_pParentGroup = pwParentGroup;
|
||||
|
||||
if(bCreateNewUuid) m_uuid = new PwUuid(true);
|
||||
|
||||
if(bSetTimes)
|
||||
{
|
||||
DateTime dtNow = DateTime.UtcNow;
|
||||
m_tCreation = dtNow;
|
||||
m_tLastMod = dtNow;
|
||||
m_tLastAccess = dtNow;
|
||||
m_tParentGroupLastMod = dtNow;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// For display in debugger
|
||||
public override string ToString()
|
||||
{
|
||||
return (@"PwEntry '" + m_listStrings.ReadSafe(PwDefs.TitleField) + @"'");
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Clone the current entry. The returned entry is an exact value copy
|
||||
/// of the current entry (including UUID and parent group reference).
|
||||
/// All mutable members are cloned.
|
||||
/// </summary>
|
||||
/// <returns>Exact value clone. All references to mutable values changed.</returns>
|
||||
public PwEntry CloneDeep()
|
||||
{
|
||||
PwEntry peNew = new PwEntry(false, false);
|
||||
|
||||
peNew.m_uuid = m_uuid; // PwUuid is immutable
|
||||
peNew.m_pParentGroup = m_pParentGroup;
|
||||
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
|
||||
|
||||
peNew.m_listStrings = m_listStrings.CloneDeep();
|
||||
peNew.m_listBinaries = m_listBinaries.CloneDeep();
|
||||
peNew.m_listAutoType = m_listAutoType.CloneDeep();
|
||||
peNew.m_listHistory = m_listHistory.CloneDeep();
|
||||
|
||||
peNew.m_pwIcon = m_pwIcon;
|
||||
peNew.m_pwCustomIconID = m_pwCustomIconID;
|
||||
|
||||
peNew.m_clrForeground = m_clrForeground;
|
||||
peNew.m_clrBackground = m_clrBackground;
|
||||
|
||||
peNew.m_tCreation = m_tCreation;
|
||||
peNew.m_tLastMod = m_tLastMod;
|
||||
peNew.m_tLastAccess = m_tLastAccess;
|
||||
peNew.m_tExpire = m_tExpire;
|
||||
peNew.m_bExpires = m_bExpires;
|
||||
peNew.m_uUsageCount = m_uUsageCount;
|
||||
|
||||
peNew.m_strOverrideUrl = m_strOverrideUrl;
|
||||
|
||||
peNew.m_vTags = new List<string>(m_vTags);
|
||||
|
||||
peNew.m_dCustomData = m_dCustomData.CloneDeep();
|
||||
|
||||
return peNew;
|
||||
}
|
||||
|
||||
public PwEntry CloneStructure()
|
||||
{
|
||||
PwEntry peNew = new PwEntry(false, false);
|
||||
|
||||
peNew.m_uuid = m_uuid; // PwUuid is immutable
|
||||
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
|
||||
// Do not assign m_pParentGroup
|
||||
|
||||
return peNew;
|
||||
}
|
||||
|
||||
private static PwCompareOptions BuildCmpOpt(bool bIgnoreParentGroup,
|
||||
bool bIgnoreLastMod, bool bIgnoreLastAccess, bool bIgnoreHistory,
|
||||
bool bIgnoreThisLastBackup)
|
||||
{
|
||||
PwCompareOptions pwOpt = PwCompareOptions.None;
|
||||
if(bIgnoreParentGroup) pwOpt |= PwCompareOptions.IgnoreParentGroup;
|
||||
if(bIgnoreLastMod) pwOpt |= PwCompareOptions.IgnoreLastMod;
|
||||
if(bIgnoreLastAccess) pwOpt |= PwCompareOptions.IgnoreLastAccess;
|
||||
if(bIgnoreHistory) pwOpt |= PwCompareOptions.IgnoreHistory;
|
||||
if(bIgnoreThisLastBackup) pwOpt |= PwCompareOptions.IgnoreLastBackup;
|
||||
return pwOpt;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public bool EqualsEntry(PwEntry pe, bool bIgnoreParentGroup, bool bIgnoreLastMod,
|
||||
bool bIgnoreLastAccess, bool bIgnoreHistory, bool bIgnoreThisLastBackup)
|
||||
{
|
||||
return EqualsEntry(pe, BuildCmpOpt(bIgnoreParentGroup, bIgnoreLastMod,
|
||||
bIgnoreLastAccess, bIgnoreHistory, bIgnoreThisLastBackup),
|
||||
MemProtCmpMode.None);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public bool EqualsEntry(PwEntry pe, bool bIgnoreParentGroup, bool bIgnoreLastMod,
|
||||
bool bIgnoreLastAccess, bool bIgnoreHistory, bool bIgnoreThisLastBackup,
|
||||
MemProtCmpMode mpCmpStr)
|
||||
{
|
||||
return EqualsEntry(pe, BuildCmpOpt(bIgnoreParentGroup, bIgnoreLastMod,
|
||||
bIgnoreLastAccess, bIgnoreHistory, bIgnoreThisLastBackup), mpCmpStr);
|
||||
}
|
||||
|
||||
public bool EqualsEntry(PwEntry pe, PwCompareOptions pwOpt,
|
||||
MemProtCmpMode mpCmpStr)
|
||||
{
|
||||
if(pe == null) { Debug.Assert(false); return false; }
|
||||
|
||||
bool bNeEqStd = ((pwOpt & PwCompareOptions.NullEmptyEquivStd) !=
|
||||
PwCompareOptions.None);
|
||||
bool bIgnoreLastAccess = ((pwOpt & PwCompareOptions.IgnoreLastAccess) !=
|
||||
PwCompareOptions.None);
|
||||
bool bIgnoreLastMod = ((pwOpt & PwCompareOptions.IgnoreLastMod) !=
|
||||
PwCompareOptions.None);
|
||||
|
||||
if(!m_uuid.Equals(pe.m_uuid)) return false;
|
||||
if((pwOpt & PwCompareOptions.IgnoreParentGroup) == PwCompareOptions.None)
|
||||
{
|
||||
if(m_pParentGroup != pe.m_pParentGroup) return false;
|
||||
if(!bIgnoreLastMod && (m_tParentGroupLastMod != pe.m_tParentGroupLastMod))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!m_listStrings.EqualsDictionary(pe.m_listStrings, pwOpt, mpCmpStr))
|
||||
return false;
|
||||
if(!m_listBinaries.EqualsDictionary(pe.m_listBinaries)) return false;
|
||||
|
||||
if(!m_listAutoType.Equals(pe.m_listAutoType)) return false;
|
||||
|
||||
if((pwOpt & PwCompareOptions.IgnoreHistory) == PwCompareOptions.None)
|
||||
{
|
||||
bool bIgnoreLastBackup = ((pwOpt & PwCompareOptions.IgnoreLastBackup) !=
|
||||
PwCompareOptions.None);
|
||||
|
||||
if(!bIgnoreLastBackup && (m_listHistory.UCount != pe.m_listHistory.UCount))
|
||||
return false;
|
||||
if(bIgnoreLastBackup && (m_listHistory.UCount == 0))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
}
|
||||
if(bIgnoreLastBackup && ((m_listHistory.UCount - 1) != pe.m_listHistory.UCount))
|
||||
return false;
|
||||
|
||||
PwCompareOptions cmpSub = PwCompareOptions.IgnoreParentGroup;
|
||||
if(bNeEqStd) cmpSub |= PwCompareOptions.NullEmptyEquivStd;
|
||||
if(bIgnoreLastMod) cmpSub |= PwCompareOptions.IgnoreLastMod;
|
||||
if(bIgnoreLastAccess) cmpSub |= PwCompareOptions.IgnoreLastAccess;
|
||||
|
||||
for(uint uHist = 0; uHist < pe.m_listHistory.UCount; ++uHist)
|
||||
{
|
||||
if(!m_listHistory.GetAt(uHist).EqualsEntry(pe.m_listHistory.GetAt(
|
||||
uHist), cmpSub, MemProtCmpMode.None))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_pwIcon != pe.m_pwIcon) return false;
|
||||
if(!m_pwCustomIconID.Equals(pe.m_pwCustomIconID)) return false;
|
||||
|
||||
if(m_clrForeground != pe.m_clrForeground) return false;
|
||||
if(m_clrBackground != pe.m_clrBackground) return false;
|
||||
|
||||
if(m_tCreation != pe.m_tCreation) return false;
|
||||
if(!bIgnoreLastMod && (m_tLastMod != pe.m_tLastMod)) return false;
|
||||
if(!bIgnoreLastAccess && (m_tLastAccess != pe.m_tLastAccess)) return false;
|
||||
if(m_tExpire != pe.m_tExpire) return false;
|
||||
if(m_bExpires != pe.m_bExpires) return false;
|
||||
if(!bIgnoreLastAccess && (m_uUsageCount != pe.m_uUsageCount)) return false;
|
||||
|
||||
if(m_strOverrideUrl != pe.m_strOverrideUrl) return false;
|
||||
|
||||
if(m_vTags.Count != pe.m_vTags.Count) return false;
|
||||
for(int iTag = 0; iTag < m_vTags.Count; ++iTag)
|
||||
{
|
||||
if(m_vTags[iTag] != pe.m_vTags[iTag]) return false;
|
||||
}
|
||||
|
||||
if(!m_dCustomData.Equals(pe.m_dCustomData)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign properties to the current entry based on a template entry.
|
||||
/// </summary>
|
||||
/// <param name="peTemplate">Template entry. Must not be <c>null</c>.</param>
|
||||
/// <param name="bOnlyIfNewer">Only set the properties of the template entry
|
||||
/// if it is newer than the current one.</param>
|
||||
/// <param name="bIncludeHistory">If <c>true</c>, the history will be
|
||||
/// copied, too.</param>
|
||||
/// <param name="bAssignLocationChanged">If <c>true</c>, the
|
||||
/// <c>LocationChanged</c> property is copied, otherwise not.</param>
|
||||
public void AssignProperties(PwEntry peTemplate, bool bOnlyIfNewer,
|
||||
bool bIncludeHistory, bool bAssignLocationChanged)
|
||||
{
|
||||
if(peTemplate == null) { Debug.Assert(false); throw new ArgumentNullException("peTemplate"); }
|
||||
|
||||
if(bOnlyIfNewer && (TimeUtil.Compare(peTemplate.m_tLastMod,
|
||||
m_tLastMod, true) < 0))
|
||||
return;
|
||||
|
||||
// Template UUID should be the same as the current one
|
||||
Debug.Assert(m_uuid.Equals(peTemplate.m_uuid));
|
||||
m_uuid = peTemplate.m_uuid;
|
||||
|
||||
if(bAssignLocationChanged)
|
||||
m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
|
||||
|
||||
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
|
||||
|
||||
m_clrForeground = peTemplate.m_clrForeground;
|
||||
m_clrBackground = peTemplate.m_clrBackground;
|
||||
|
||||
m_tCreation = peTemplate.m_tCreation;
|
||||
m_tLastMod = peTemplate.m_tLastMod;
|
||||
m_tLastAccess = peTemplate.m_tLastAccess;
|
||||
m_tExpire = peTemplate.m_tExpire;
|
||||
m_bExpires = peTemplate.m_bExpires;
|
||||
m_uUsageCount = peTemplate.m_uUsageCount;
|
||||
|
||||
m_strOverrideUrl = peTemplate.m_strOverrideUrl;
|
||||
|
||||
m_vTags = new List<string>(peTemplate.m_vTags);
|
||||
|
||||
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Touch the entry. This function updates the internal last access
|
||||
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
|
||||
/// the last modification time gets updated, too.
|
||||
/// </summary>
|
||||
/// <param name="bModified">Modify last modification time.</param>
|
||||
public void Touch(bool bModified)
|
||||
{
|
||||
Touch(bModified, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Touch the entry. This function updates the internal last access
|
||||
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
|
||||
/// the last modification time gets updated, too.
|
||||
/// </summary>
|
||||
/// <param name="bModified">Modify last modification time.</param>
|
||||
/// <param name="bTouchParents">If <c>true</c>, all parent objects
|
||||
/// get touched, too.</param>
|
||||
public void Touch(bool bModified, bool bTouchParents)
|
||||
{
|
||||
m_tLastAccess = DateTime.UtcNow;
|
||||
++m_uUsageCount;
|
||||
|
||||
if(bModified) m_tLastMod = m_tLastAccess;
|
||||
|
||||
if(this.Touched != null)
|
||||
this.Touched(this, new ObjectTouchedEventArgs(this,
|
||||
bModified, bTouchParents));
|
||||
if(PwEntry.EntryTouched != null)
|
||||
PwEntry.EntryTouched(this, new ObjectTouchedEventArgs(this,
|
||||
bModified, bTouchParents));
|
||||
|
||||
if(bTouchParents && (m_pParentGroup != null))
|
||||
m_pParentGroup.Touch(bModified, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a backup of this entry. The backup item doesn't contain any
|
||||
/// history items.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public void CreateBackup()
|
||||
{
|
||||
CreateBackup(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a backup of this entry. The backup item doesn't contain any
|
||||
/// history items.
|
||||
/// <param name="pwHistMntcSettings">If this parameter isn't <c>null</c>,
|
||||
/// the history list is maintained automatically (i.e. old backups are
|
||||
/// deleted if there are too many or the history size is too large).
|
||||
/// This parameter may be <c>null</c> (no maintenance then).</param>
|
||||
/// </summary>
|
||||
public void CreateBackup(PwDatabase pwHistMntcSettings)
|
||||
{
|
||||
PwEntry peCopy = CloneDeep();
|
||||
peCopy.History = new PwObjectList<PwEntry>(); // Remove history
|
||||
|
||||
m_listHistory.Add(peCopy); // Must be added at end, see EqualsEntry
|
||||
|
||||
if(pwHistMntcSettings != null) MaintainBackups(pwHistMntcSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore an entry snapshot from backups.
|
||||
/// </summary>
|
||||
/// <param name="uBackupIndex">Index of the backup item, to which
|
||||
/// should be reverted.</param>
|
||||
[Obsolete]
|
||||
public void RestoreFromBackup(uint uBackupIndex)
|
||||
{
|
||||
RestoreFromBackup(uBackupIndex, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore an entry snapshot from backups.
|
||||
/// </summary>
|
||||
/// <param name="uBackupIndex">Index of the backup item, to which
|
||||
/// should be reverted.</param>
|
||||
/// <param name="pwHistMntcSettings">If this parameter isn't <c>null</c>,
|
||||
/// the history list is maintained automatically (i.e. old backups are
|
||||
/// deleted if there are too many or the history size is too large).
|
||||
/// This parameter may be <c>null</c> (no maintenance then).</param>
|
||||
public void RestoreFromBackup(uint uBackupIndex, PwDatabase pwHistMntcSettings)
|
||||
{
|
||||
Debug.Assert(uBackupIndex < m_listHistory.UCount);
|
||||
if(uBackupIndex >= m_listHistory.UCount)
|
||||
throw new ArgumentOutOfRangeException("uBackupIndex");
|
||||
|
||||
PwEntry pe = m_listHistory.GetAt(uBackupIndex);
|
||||
Debug.Assert(pe != null); if(pe == null) throw new InvalidOperationException();
|
||||
|
||||
CreateBackup(pwHistMntcSettings); // Backup current data before restoring
|
||||
AssignProperties(pe, false, false, false);
|
||||
}
|
||||
|
||||
public bool HasBackupOfData(PwEntry peData, bool bIgnoreLastMod,
|
||||
bool bIgnoreLastAccess)
|
||||
{
|
||||
if(peData == null) { Debug.Assert(false); return false; }
|
||||
|
||||
PwCompareOptions cmpOpt = (PwCompareOptions.IgnoreParentGroup |
|
||||
PwCompareOptions.IgnoreHistory | PwCompareOptions.NullEmptyEquivStd);
|
||||
if(bIgnoreLastMod) cmpOpt |= PwCompareOptions.IgnoreLastMod;
|
||||
if(bIgnoreLastAccess) cmpOpt |= PwCompareOptions.IgnoreLastAccess;
|
||||
|
||||
foreach(PwEntry pe in m_listHistory)
|
||||
{
|
||||
if(pe.EqualsEntry(peData, cmpOpt, MemProtCmpMode.None)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete old history items if there are too many or the history
|
||||
/// size is too large.
|
||||
/// <returns>If one or more history items have been deleted, <c>true</c>
|
||||
/// is returned. Otherwise <c>false</c>.</returns>
|
||||
/// </summary>
|
||||
public bool MaintainBackups(PwDatabase pwSettings)
|
||||
{
|
||||
if(pwSettings == null) { Debug.Assert(false); return false; }
|
||||
|
||||
bool bDeleted = false;
|
||||
|
||||
int nMaxItems = pwSettings.HistoryMaxItems;
|
||||
if(nMaxItems >= 0)
|
||||
{
|
||||
while(m_listHistory.UCount > (uint)nMaxItems)
|
||||
{
|
||||
RemoveOldestBackup();
|
||||
bDeleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
long lMaxSize = pwSettings.HistoryMaxSize;
|
||||
if(lMaxSize >= 0)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
ulong uHistSize = 0;
|
||||
foreach(PwEntry pe in m_listHistory) { uHistSize += pe.GetSize(); }
|
||||
|
||||
if(uHistSize > (ulong)lMaxSize)
|
||||
{
|
||||
RemoveOldestBackup();
|
||||
bDeleted = true;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
return bDeleted;
|
||||
}
|
||||
|
||||
private void RemoveOldestBackup()
|
||||
{
|
||||
DateTime dtMin = TimeUtil.SafeMaxValueUtc;
|
||||
uint idxRemove = uint.MaxValue;
|
||||
|
||||
for(uint u = 0; u < m_listHistory.UCount; ++u)
|
||||
{
|
||||
PwEntry pe = m_listHistory.GetAt(u);
|
||||
if(TimeUtil.Compare(pe.LastModificationTime, dtMin, true) < 0)
|
||||
{
|
||||
idxRemove = u;
|
||||
dtMin = pe.LastModificationTime;
|
||||
}
|
||||
}
|
||||
|
||||
if(idxRemove != uint.MaxValue) m_listHistory.RemoveAt(idxRemove);
|
||||
}
|
||||
|
||||
public bool GetAutoTypeEnabled()
|
||||
{
|
||||
if(!m_listAutoType.Enabled) return false;
|
||||
|
||||
if(m_pParentGroup != null)
|
||||
return m_pParentGroup.GetAutoTypeEnabledInherited();
|
||||
|
||||
return PwGroup.DefaultAutoTypeEnabled;
|
||||
}
|
||||
|
||||
public string GetAutoTypeSequence()
|
||||
{
|
||||
string strSeq = m_listAutoType.DefaultSequence;
|
||||
|
||||
PwGroup pg = m_pParentGroup;
|
||||
while(pg != null)
|
||||
{
|
||||
if(strSeq.Length != 0) break;
|
||||
|
||||
strSeq = pg.DefaultAutoTypeSequence;
|
||||
pg = pg.ParentGroup;
|
||||
}
|
||||
|
||||
if(strSeq.Length != 0) return strSeq;
|
||||
|
||||
if(PwDefs.IsTanEntry(this)) return PwDefs.DefaultAutoTypeSequenceTan;
|
||||
return PwDefs.DefaultAutoTypeSequence;
|
||||
}
|
||||
|
||||
public bool GetSearchingEnabled()
|
||||
{
|
||||
if(m_pParentGroup != null)
|
||||
return m_pParentGroup.GetSearchingEnabledInherited();
|
||||
|
||||
return PwGroup.DefaultSearchingEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approximate the total size (in process memory) of this entry
|
||||
/// in bytes (including strings, binaries and history entries).
|
||||
/// </summary>
|
||||
/// <returns>Size in bytes.</returns>
|
||||
public ulong GetSize()
|
||||
{
|
||||
// This method assumes 64-bit pointers/references and Unicode
|
||||
// strings (i.e. 2 bytes per character)
|
||||
|
||||
ulong cb = 248; // Number of bytes; approx. fixed length data
|
||||
ulong cc = 0; // Number of characters
|
||||
|
||||
cb += (ulong)m_listStrings.UCount * 40;
|
||||
foreach(KeyValuePair<string, ProtectedString> kvpStr in m_listStrings)
|
||||
cc += (ulong)kvpStr.Key.Length + (ulong)kvpStr.Value.Length;
|
||||
|
||||
cb += (ulong)m_listBinaries.UCount * 65;
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
|
||||
{
|
||||
cc += (ulong)kvpBin.Key.Length;
|
||||
cb += (ulong)kvpBin.Value.Length;
|
||||
}
|
||||
|
||||
cc += (ulong)m_listAutoType.DefaultSequence.Length;
|
||||
cb += (ulong)m_listAutoType.AssociationsCount * 24;
|
||||
foreach(AutoTypeAssociation a in m_listAutoType.Associations)
|
||||
cc += (ulong)a.WindowName.Length + (ulong)a.Sequence.Length;
|
||||
|
||||
cb += (ulong)m_listHistory.UCount * 8;
|
||||
foreach(PwEntry peHistory in m_listHistory)
|
||||
cb += peHistory.GetSize();
|
||||
|
||||
cc += (ulong)m_strOverrideUrl.Length;
|
||||
|
||||
cb += (ulong)m_vTags.Count * 8;
|
||||
foreach(string strTag in m_vTags)
|
||||
cc += (ulong)strTag.Length;
|
||||
|
||||
cb += (ulong)m_dCustomData.Count * 16;
|
||||
foreach(KeyValuePair<string, string> kvp in m_dCustomData)
|
||||
cc += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
|
||||
|
||||
return (cb + (cc << 1));
|
||||
}
|
||||
|
||||
public bool HasTag(string strTag)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
|
||||
|
||||
for(int i = 0; i < m_vTags.Count; ++i)
|
||||
{
|
||||
if(m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool AddTag(string strTag)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
|
||||
|
||||
for(int i = 0; i < m_vTags.Count; ++i)
|
||||
{
|
||||
if(m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return false;
|
||||
}
|
||||
|
||||
m_vTags.Add(strTag);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveTag(string strTag)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
|
||||
|
||||
for(int i = 0; i < m_vTags.Count; ++i)
|
||||
{
|
||||
if(m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp))
|
||||
{
|
||||
m_vTags.RemoveAt(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsContainedIn(PwGroup pgContainer)
|
||||
{
|
||||
PwGroup pgCur = m_pParentGroup;
|
||||
while(pgCur != null)
|
||||
{
|
||||
if(pgCur == pgContainer) return true;
|
||||
|
||||
pgCur = pgCur.ParentGroup;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetUuid(PwUuid pwNewUuid, bool bAlsoChangeHistoryUuids)
|
||||
{
|
||||
this.Uuid = pwNewUuid;
|
||||
|
||||
if(bAlsoChangeHistoryUuids)
|
||||
{
|
||||
foreach(PwEntry peHist in m_listHistory)
|
||||
{
|
||||
peHist.Uuid = pwNewUuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCreatedNow()
|
||||
{
|
||||
DateTime dt = DateTime.UtcNow;
|
||||
|
||||
m_tCreation = dt;
|
||||
m_tLastAccess = dt;
|
||||
}
|
||||
|
||||
public PwEntry Duplicate()
|
||||
{
|
||||
PwEntry pe = CloneDeep();
|
||||
|
||||
pe.SetUuid(new PwUuid(true), true);
|
||||
pe.SetCreatedNow();
|
||||
|
||||
return pe;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PwEntryComparer : IComparer<PwEntry>
|
||||
{
|
||||
private string m_strFieldName;
|
||||
private bool m_bCaseInsensitive;
|
||||
private bool m_bCompareNaturally;
|
||||
|
||||
public PwEntryComparer(string strFieldName, bool bCaseInsensitive,
|
||||
bool bCompareNaturally)
|
||||
{
|
||||
if(strFieldName == null) throw new ArgumentNullException("strFieldName");
|
||||
|
||||
m_strFieldName = strFieldName;
|
||||
m_bCaseInsensitive = bCaseInsensitive;
|
||||
m_bCompareNaturally = bCompareNaturally;
|
||||
}
|
||||
|
||||
public int Compare(PwEntry a, PwEntry b)
|
||||
{
|
||||
string strA = a.Strings.ReadSafe(m_strFieldName);
|
||||
string strB = b.Strings.ReadSafe(m_strFieldName);
|
||||
|
||||
if(m_bCompareNaturally) return StrUtil.CompareNaturally(strA, strB);
|
||||
|
||||
#if ModernKeePassLib || KeePassRT
|
||||
return string.Compare(strA, strB, m_bCaseInsensitive ?
|
||||
StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture);
|
||||
#else
|
||||
return string.Compare(strA, strB, m_bCaseInsensitive);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
319
ModernKeePassLib/PwEnums.cs
Normal file
319
ModernKeePassLib/PwEnums.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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;
|
||||
|
||||
namespace ModernKeePassLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Compression algorithm specifiers.
|
||||
/// </summary>
|
||||
public enum PwCompressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// No compression.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// GZip compression.
|
||||
/// </summary>
|
||||
GZip = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Virtual field: currently known number of algorithms. Should not be used
|
||||
/// by plugins or libraries -- it's used internally only.
|
||||
/// </summary>
|
||||
Count = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tree traversal methods.
|
||||
/// </summary>
|
||||
public enum TraversalMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't traverse the tree.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Traverse the tree in pre-order mode, i.e. first visit all items
|
||||
/// in the current node, then visit all subnodes.
|
||||
/// </summary>
|
||||
PreOrder = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Methods for merging password databases/entries.
|
||||
/// </summary>
|
||||
public enum PwMergeMethod
|
||||
{
|
||||
// Do not change the explicitly assigned values, otherwise
|
||||
// serialization (e.g. of Ecas triggers) breaks
|
||||
None = 0,
|
||||
OverwriteExisting = 1,
|
||||
KeepExisting = 2,
|
||||
OverwriteIfNewer = 3,
|
||||
CreateNewUuids = 4,
|
||||
Synchronize = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Icon identifiers for groups and password entries.
|
||||
/// </summary>
|
||||
public enum PwIcon
|
||||
{
|
||||
Key = 0,
|
||||
World,
|
||||
Warning,
|
||||
NetworkServer,
|
||||
MarkedDirectory,
|
||||
UserCommunication,
|
||||
Parts,
|
||||
Notepad,
|
||||
WorldSocket,
|
||||
Identity,
|
||||
PaperReady,
|
||||
Digicam,
|
||||
IRCommunication,
|
||||
MultiKeys,
|
||||
Energy,
|
||||
Scanner,
|
||||
WorldStar,
|
||||
CDRom,
|
||||
Monitor,
|
||||
EMail,
|
||||
Configuration,
|
||||
ClipboardReady,
|
||||
PaperNew,
|
||||
Screen,
|
||||
EnergyCareful,
|
||||
EMailBox,
|
||||
Disk,
|
||||
Drive,
|
||||
PaperQ,
|
||||
TerminalEncrypted,
|
||||
Console,
|
||||
Printer,
|
||||
ProgramIcons,
|
||||
Run,
|
||||
Settings,
|
||||
WorldComputer,
|
||||
Archive,
|
||||
Homebanking,
|
||||
DriveWindows,
|
||||
Clock,
|
||||
EMailSearch,
|
||||
PaperFlag,
|
||||
Memory,
|
||||
TrashBin,
|
||||
Note,
|
||||
Expired,
|
||||
Info,
|
||||
Package,
|
||||
Folder,
|
||||
FolderOpen,
|
||||
FolderPackage,
|
||||
LockOpen,
|
||||
PaperLocked,
|
||||
Checked,
|
||||
Pen,
|
||||
Thumbnail,
|
||||
Book,
|
||||
List,
|
||||
UserKey,
|
||||
Tool,
|
||||
Home,
|
||||
Star,
|
||||
Tux,
|
||||
Feather,
|
||||
Apple,
|
||||
Wiki,
|
||||
Money,
|
||||
Certificate,
|
||||
BlackBerry,
|
||||
|
||||
/// <summary>
|
||||
/// Virtual identifier -- represents the number of icons.
|
||||
/// </summary>
|
||||
Count
|
||||
}
|
||||
|
||||
public enum ProxyServerType
|
||||
{
|
||||
None = 0,
|
||||
System = 1,
|
||||
Manual = 2
|
||||
}
|
||||
|
||||
public enum ProxyAuthType
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Use default user credentials (provided by the system).
|
||||
/// </summary>
|
||||
Default = 1,
|
||||
|
||||
Manual = 2,
|
||||
|
||||
/// <summary>
|
||||
/// <c>Default</c> or <c>Manual</c>, depending on whether
|
||||
/// manual credentials are available.
|
||||
/// This type exists for supporting upgrading from KeePass
|
||||
/// 2.28 to 2.29; the user cannot select this type.
|
||||
/// </summary>
|
||||
Auto = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Comparison modes for in-memory protected objects.
|
||||
/// </summary>
|
||||
public enum MemProtCmpMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignore the in-memory protection states.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore the in-memory protection states of standard
|
||||
/// objects; do compare in-memory protection states of
|
||||
/// custom objects.
|
||||
/// </summary>
|
||||
CustomOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Compare in-memory protection states.
|
||||
/// </summary>
|
||||
Full
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum PwCompareOptions
|
||||
{
|
||||
None = 0x0,
|
||||
|
||||
/// <summary>
|
||||
/// Empty standard string fields are considered to be the
|
||||
/// same as non-existing standard string fields.
|
||||
/// This doesn't affect custom string comparisons.
|
||||
/// </summary>
|
||||
NullEmptyEquivStd = 0x1,
|
||||
|
||||
IgnoreParentGroup = 0x2,
|
||||
IgnoreLastAccess = 0x4,
|
||||
IgnoreLastMod = 0x8,
|
||||
IgnoreHistory = 0x10,
|
||||
IgnoreLastBackup = 0x20,
|
||||
|
||||
// For groups:
|
||||
PropertiesOnly = 0x40,
|
||||
|
||||
IgnoreTimes = (IgnoreLastAccess | IgnoreLastMod)
|
||||
}
|
||||
|
||||
public enum IOAccessType
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The IO connection is being opened for reading.
|
||||
/// </summary>
|
||||
Read = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The IO connection is being opened for writing.
|
||||
/// </summary>
|
||||
Write = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The IO connection is being opened for testing
|
||||
/// whether a file/object exists.
|
||||
/// </summary>
|
||||
Exists = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The IO connection is being opened for deleting a file/object.
|
||||
/// </summary>
|
||||
Delete = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The IO connection is being opened for renaming/moving a file/object.
|
||||
/// </summary>
|
||||
Move = 5
|
||||
}
|
||||
|
||||
// public enum PwLogicalOp
|
||||
// {
|
||||
// None = 0,
|
||||
// Or = 1,
|
||||
// And = 2,
|
||||
// NOr = 3,
|
||||
// NAnd = 4
|
||||
// }
|
||||
|
||||
[Flags]
|
||||
public enum AppRunFlags
|
||||
{
|
||||
None = 0,
|
||||
GetStdOutput = 1,
|
||||
WaitForExit = 2,
|
||||
|
||||
// https://sourceforge.net/p/keepass/patches/84/
|
||||
/// <summary>
|
||||
/// This flag prevents any handles being garbage-collected
|
||||
/// before the started process has terminated, without
|
||||
/// blocking the current thread.
|
||||
/// </summary>
|
||||
GCKeepAlive = 4,
|
||||
|
||||
// https://sourceforge.net/p/keepass/patches/85/
|
||||
DoEvents = 8,
|
||||
DisableForms = 16
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ScaleTransformFlags
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// <c>UIIcon</c> indicates that the returned image is going
|
||||
/// to be displayed as icon in the UI and that it is not
|
||||
/// subject to future changes in size.
|
||||
/// </summary>
|
||||
UIIcon = 1
|
||||
}
|
||||
|
||||
public enum DesktopType
|
||||
{
|
||||
None = 0,
|
||||
Windows,
|
||||
Gnome,
|
||||
Kde,
|
||||
Unity,
|
||||
Lxde,
|
||||
Xfce,
|
||||
Mate,
|
||||
Cinnamon,
|
||||
Pantheon
|
||||
}
|
||||
}
|
||||
1750
ModernKeePassLib/PwGroup.cs
Normal file
1750
ModernKeePassLib/PwGroup.cs
Normal file
File diff suppressed because it is too large
Load Diff
215
ModernKeePassLib/PwUuid.cs
Normal file
215
ModernKeePassLib/PwUuid.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Xml;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib
|
||||
{
|
||||
// [ImmutableObject(true)]
|
||||
/// <summary>
|
||||
/// Represents an UUID of a password entry or group. Once created,
|
||||
/// <c>PwUuid</c> objects aren't modifyable anymore (immutable).
|
||||
/// </summary>
|
||||
public sealed class PwUuid : IComparable<PwUuid>, IEquatable<PwUuid>
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard size in bytes of a UUID.
|
||||
/// </summary>
|
||||
public const uint UuidSize = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Zero UUID (all bytes are zero).
|
||||
/// </summary>
|
||||
public static readonly PwUuid Zero = new PwUuid(false);
|
||||
|
||||
private byte[] m_pbUuid = null; // Never null after constructor
|
||||
|
||||
/// <summary>
|
||||
/// Get the 16 UUID bytes.
|
||||
/// </summary>
|
||||
public byte[] UuidBytes
|
||||
{
|
||||
get { return m_pbUuid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new UUID object.
|
||||
/// </summary>
|
||||
/// <param name="bCreateNew">If this parameter is <c>true</c>, a new
|
||||
/// UUID is generated. If it is <c>false</c>, the UUID is initialized
|
||||
/// to zero.</param>
|
||||
public PwUuid(bool bCreateNew)
|
||||
{
|
||||
if(bCreateNew) CreateNew();
|
||||
else SetZero();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new UUID object.
|
||||
/// </summary>
|
||||
/// <param name="uuidBytes">Initial value of the <c>PwUuid</c> object.</param>
|
||||
public PwUuid(byte[] uuidBytes)
|
||||
{
|
||||
SetValue(uuidBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, random UUID.
|
||||
/// </summary>
|
||||
/// <returns>Returns <c>true</c> if a random UUID has been generated,
|
||||
/// otherwise it returns <c>false</c>.</returns>
|
||||
private void CreateNew()
|
||||
{
|
||||
Debug.Assert(m_pbUuid == null); // Only call from constructor
|
||||
while(true)
|
||||
{
|
||||
m_pbUuid = Guid.NewGuid().ToByteArray();
|
||||
|
||||
if((m_pbUuid == null) || (m_pbUuid.Length != (int)UuidSize))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
// Zero is a reserved value -- do not generate Zero
|
||||
if(!Equals(PwUuid.Zero)) break;
|
||||
Debug.Assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetValue(byte[] uuidBytes)
|
||||
{
|
||||
Debug.Assert((uuidBytes != null) && (uuidBytes.Length == (int)UuidSize));
|
||||
if(uuidBytes == null) throw new ArgumentNullException("uuidBytes");
|
||||
if(uuidBytes.Length != (int)UuidSize) throw new ArgumentException();
|
||||
|
||||
Debug.Assert(m_pbUuid == null); // Only call from constructor
|
||||
m_pbUuid = new byte[UuidSize];
|
||||
|
||||
Array.Copy(uuidBytes, m_pbUuid, (int)UuidSize);
|
||||
}
|
||||
|
||||
private void SetZero()
|
||||
{
|
||||
Debug.Assert(m_pbUuid == null); // Only call from constructor
|
||||
m_pbUuid = new byte[UuidSize];
|
||||
|
||||
// Array.Clear(m_pbUuid, 0, (int)UuidSize);
|
||||
#if DEBUG
|
||||
List<byte> l = new List<byte>(m_pbUuid);
|
||||
Debug.Assert(l.TrueForAll(bt => (bt == 0)));
|
||||
#endif
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public bool EqualsValue(PwUuid uuid)
|
||||
{
|
||||
return Equals(uuid);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as PwUuid);
|
||||
}
|
||||
|
||||
public bool Equals(PwUuid other)
|
||||
{
|
||||
if(other == null) { Debug.Assert(false); return false; }
|
||||
|
||||
for(int i = 0; i < (int)UuidSize; ++i)
|
||||
{
|
||||
if(m_pbUuid[i] != other.m_pbUuid[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int m_h = 0;
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if(m_h == 0)
|
||||
m_h = (int)MemUtil.Hash32(m_pbUuid, 0, m_pbUuid.Length);
|
||||
return m_h;
|
||||
}
|
||||
|
||||
public int CompareTo(PwUuid other)
|
||||
{
|
||||
if(other == null)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ArgumentNullException("other");
|
||||
}
|
||||
|
||||
for(int i = 0; i < (int)UuidSize; ++i)
|
||||
{
|
||||
if(m_pbUuid[i] < other.m_pbUuid[i]) return -1;
|
||||
if(m_pbUuid[i] > other.m_pbUuid[i]) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the UUID to its string representation.
|
||||
/// </summary>
|
||||
/// <returns>String containing the UUID value.</returns>
|
||||
public string ToHexString()
|
||||
{
|
||||
return MemUtil.ByteArrayToHexString(m_pbUuid);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString()
|
||||
{
|
||||
return ToHexString();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public sealed class PwUuidComparable : IComparable<PwUuidComparable>
|
||||
{
|
||||
private byte[] m_pbUuid = new byte[PwUuid.UuidSize];
|
||||
|
||||
public PwUuidComparable(PwUuid pwUuid)
|
||||
{
|
||||
if(pwUuid == null) throw new ArgumentNullException("pwUuid");
|
||||
|
||||
Array.Copy(pwUuid.UuidBytes, m_pbUuid, (int)PwUuid.UuidSize);
|
||||
}
|
||||
|
||||
public int CompareTo(PwUuidComparable other)
|
||||
{
|
||||
if(other == null) throw new ArgumentNullException("other");
|
||||
|
||||
for(int i = 0; i < (int)PwUuid.UuidSize; ++i)
|
||||
{
|
||||
if(m_pbUuid[i] < other.m_pbUuid[i]) return -1;
|
||||
if(m_pbUuid[i] > other.m_pbUuid[i]) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
ModernKeePassLib/README.md
Normal file
13
ModernKeePassLib/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# ModernKeePassLib
|
||||
|
||||
This is my adaptation of the KeePassLib (KeePass library) for the Universal Windows Platform and Windows Runtime (WinRT).
|
||||
It aims at introducing as little change as possible to the original library: overall, except for namespace changes and some added classes (see below), there is almost no change.
|
||||
|
||||
Download the Nuget package [here](https://www.nuget.org/packages/ModernKeePassLib)
|
||||
|
||||
# Features
|
||||
- Custom implementation of the System.Security.Cryptography.HashAlgoritm class by using WinRT equivalents
|
||||
- Use of BouncyCastle PCL to implement AES key derivation features
|
||||
- Lots of small changes in .NET methods (UTF8 instead of ASCII, string.)
|
||||
- Disabled native functions (because not compatible with WinRT)
|
||||
- Use of Splat for GfxUtil
|
||||
546
ModernKeePassLib/Resources/KLRes.Generated.cs
Normal file
546
ModernKeePassLib/Resources/KLRes.Generated.cs
Normal file
@@ -0,0 +1,546 @@
|
||||
// This is a generated file!
|
||||
// Do not edit manually, changes will be overwritten.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ModernKeePassLib.Resources
|
||||
{
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
public static class KLRes
|
||||
{
|
||||
private static string TryGetEx(Dictionary<string, string> dictNew,
|
||||
string strName, string strDefault)
|
||||
{
|
||||
string strTemp;
|
||||
|
||||
if(dictNew.TryGetValue(strName, out strTemp))
|
||||
return strTemp;
|
||||
|
||||
return strDefault;
|
||||
}
|
||||
|
||||
public static void SetTranslatedStrings(Dictionary<string, string> dictNew)
|
||||
{
|
||||
if(dictNew == null) throw new ArgumentNullException("dictNew");
|
||||
|
||||
m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed);
|
||||
m_strEncDataTooLarge = TryGetEx(dictNew, "EncDataTooLarge", m_strEncDataTooLarge);
|
||||
m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
|
||||
m_strExpect100Continue = TryGetEx(dictNew, "Expect100Continue", m_strExpect100Continue);
|
||||
m_strFatalError = TryGetEx(dictNew, "FatalError", m_strFatalError);
|
||||
m_strFatalErrorText = TryGetEx(dictNew, "FatalErrorText", m_strFatalErrorText);
|
||||
m_strFileCorrupted = TryGetEx(dictNew, "FileCorrupted", m_strFileCorrupted);
|
||||
m_strFileHeaderCorrupted = TryGetEx(dictNew, "FileHeaderCorrupted", m_strFileHeaderCorrupted);
|
||||
m_strFileIncomplete = TryGetEx(dictNew, "FileIncomplete", m_strFileIncomplete);
|
||||
m_strFileIncompleteExpc = TryGetEx(dictNew, "FileIncompleteExpc", m_strFileIncompleteExpc);
|
||||
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);
|
||||
m_strFileSigInvalid = TryGetEx(dictNew, "FileSigInvalid", m_strFileSigInvalid);
|
||||
m_strFileUnknownCipher = TryGetEx(dictNew, "FileUnknownCipher", m_strFileUnknownCipher);
|
||||
m_strFileUnknownCompression = TryGetEx(dictNew, "FileUnknownCompression", m_strFileUnknownCompression);
|
||||
m_strFileVersionUnsupported = TryGetEx(dictNew, "FileVersionUnsupported", m_strFileVersionUnsupported);
|
||||
m_strFinalKeyCreationFailed = TryGetEx(dictNew, "FinalKeyCreationFailed", m_strFinalKeyCreationFailed);
|
||||
m_strFrameworkNotImplExcp = TryGetEx(dictNew, "FrameworkNotImplExcp", m_strFrameworkNotImplExcp);
|
||||
m_strGeneral = TryGetEx(dictNew, "General", m_strGeneral);
|
||||
m_strInvalidCompositeKey = TryGetEx(dictNew, "InvalidCompositeKey", m_strInvalidCompositeKey);
|
||||
m_strInvalidCompositeKeyHint = TryGetEx(dictNew, "InvalidCompositeKeyHint", m_strInvalidCompositeKeyHint);
|
||||
m_strInvalidDataWhileDecoding = TryGetEx(dictNew, "InvalidDataWhileDecoding", m_strInvalidDataWhileDecoding);
|
||||
m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
|
||||
m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
|
||||
m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
|
||||
m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
|
||||
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
|
||||
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
|
||||
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
|
||||
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
|
||||
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
|
||||
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
|
||||
m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
|
||||
m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
|
||||
m_strUserAgent = TryGetEx(dictNew, "UserAgent", m_strUserAgent);
|
||||
}
|
||||
|
||||
private static readonly string[] m_vKeyNames = {
|
||||
"CryptoStreamFailed",
|
||||
"EncDataTooLarge",
|
||||
"ErrorInClipboard",
|
||||
"Expect100Continue",
|
||||
"FatalError",
|
||||
"FatalErrorText",
|
||||
"FileCorrupted",
|
||||
"FileHeaderCorrupted",
|
||||
"FileIncomplete",
|
||||
"FileIncompleteExpc",
|
||||
"FileLoadFailed",
|
||||
"FileLockedWrite",
|
||||
"FileNewVerOrPlgReq",
|
||||
"FileNewVerReq",
|
||||
"FileSaveCorruptionWarning",
|
||||
"FileSaveFailed",
|
||||
"FileSigInvalid",
|
||||
"FileUnknownCipher",
|
||||
"FileUnknownCompression",
|
||||
"FileVersionUnsupported",
|
||||
"FinalKeyCreationFailed",
|
||||
"FrameworkNotImplExcp",
|
||||
"General",
|
||||
"InvalidCompositeKey",
|
||||
"InvalidCompositeKeyHint",
|
||||
"InvalidDataWhileDecoding",
|
||||
"KeePass1xHint",
|
||||
"KeyBits",
|
||||
"KeyFileDbSel",
|
||||
"MasterSeedLengthInvalid",
|
||||
"OldFormat",
|
||||
"Passive",
|
||||
"PreAuth",
|
||||
"Timeout",
|
||||
"TryAgainSecs",
|
||||
"UnknownHeaderId",
|
||||
"UnknownKdf",
|
||||
"UserAccountKeyError",
|
||||
"UserAgent"
|
||||
};
|
||||
|
||||
public static string[] GetKeyNames()
|
||||
{
|
||||
return m_vKeyNames;
|
||||
}
|
||||
|
||||
private static string m_strCryptoStreamFailed =
|
||||
@"Failed to initialize encryption/decryption stream!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Failed to initialize encryption/decryption stream!'.
|
||||
/// </summary>
|
||||
public static string CryptoStreamFailed
|
||||
{
|
||||
get { return m_strCryptoStreamFailed; }
|
||||
}
|
||||
|
||||
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
|
||||
/// 'The data is too large to be encrypted/decrypted securely using {PARAM}.'.
|
||||
/// </summary>
|
||||
public static string EncDataTooLarge
|
||||
{
|
||||
get { return m_strEncDataTooLarge; }
|
||||
}
|
||||
|
||||
private static string m_strErrorInClipboard =
|
||||
@"An extended error report has been copied to the clipboard.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'An extended error report has been copied to the clipboard.'.
|
||||
/// </summary>
|
||||
public static string ErrorInClipboard
|
||||
{
|
||||
get { return m_strErrorInClipboard; }
|
||||
}
|
||||
|
||||
private static string m_strExpect100Continue =
|
||||
@"Expect 100-Continue responses";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Expect 100-Continue responses'.
|
||||
/// </summary>
|
||||
public static string Expect100Continue
|
||||
{
|
||||
get { return m_strExpect100Continue; }
|
||||
}
|
||||
|
||||
private static string m_strFatalError =
|
||||
@"Fatal Error";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Fatal Error'.
|
||||
/// </summary>
|
||||
public static string FatalError
|
||||
{
|
||||
get { return m_strFatalError; }
|
||||
}
|
||||
|
||||
private static string m_strFatalErrorText =
|
||||
@"A fatal error has occurred!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'A fatal error has occurred!'.
|
||||
/// </summary>
|
||||
public static string FatalErrorText
|
||||
{
|
||||
get { return m_strFatalErrorText; }
|
||||
}
|
||||
|
||||
private static string m_strFileCorrupted =
|
||||
@"The file is corrupted.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The file is corrupted.'.
|
||||
/// </summary>
|
||||
public static string FileCorrupted
|
||||
{
|
||||
get { return m_strFileCorrupted; }
|
||||
}
|
||||
|
||||
private static string m_strFileHeaderCorrupted =
|
||||
@"The file header is corrupted.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The file header is corrupted.'.
|
||||
/// </summary>
|
||||
public static string FileHeaderCorrupted
|
||||
{
|
||||
get { return m_strFileHeaderCorrupted; }
|
||||
}
|
||||
|
||||
private static string m_strFileIncomplete =
|
||||
@"Data is missing at the end of the file, i.e. the file is incomplete.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Data is missing at the end of the file, i.e. the file is incomplete.'.
|
||||
/// </summary>
|
||||
public static string FileIncomplete
|
||||
{
|
||||
get { return m_strFileIncomplete; }
|
||||
}
|
||||
|
||||
private static string m_strFileIncompleteExpc =
|
||||
@"Less data than expected could be read from the file.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Less data than expected could be read from the file.'.
|
||||
/// </summary>
|
||||
public static string FileIncompleteExpc
|
||||
{
|
||||
get { return m_strFileIncompleteExpc; }
|
||||
}
|
||||
|
||||
private static string m_strFileLoadFailed =
|
||||
@"Failed to load the specified file!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Failed to load the specified file!'.
|
||||
/// </summary>
|
||||
public static string FileLoadFailed
|
||||
{
|
||||
get { return m_strFileLoadFailed; }
|
||||
}
|
||||
|
||||
private static string m_strFileLockedWrite =
|
||||
@"The file is locked, because the following user is currently writing to it:";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The file is locked, because the following user is currently writing to it:'.
|
||||
/// </summary>
|
||||
public static string FileLockedWrite
|
||||
{
|
||||
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>
|
||||
/// Look up a localized string similar to
|
||||
/// 'A newer KeePass version is required to open this file.'.
|
||||
/// </summary>
|
||||
public static string FileNewVerReq
|
||||
{
|
||||
get { return m_strFileNewVerReq; }
|
||||
}
|
||||
|
||||
private static string m_strFileSaveCorruptionWarning =
|
||||
@"The target file might be corrupted. Please try saving again. If that fails, save the database to a different location.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The target file might be corrupted. Please try saving again. If that fails, save the database to a different location.'.
|
||||
/// </summary>
|
||||
public static string FileSaveCorruptionWarning
|
||||
{
|
||||
get { return m_strFileSaveCorruptionWarning; }
|
||||
}
|
||||
|
||||
private static string m_strFileSaveFailed =
|
||||
@"Failed to save the current database to the specified location!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Failed to save the current database to the specified location!'.
|
||||
/// </summary>
|
||||
public static string FileSaveFailed
|
||||
{
|
||||
get { return m_strFileSaveFailed; }
|
||||
}
|
||||
|
||||
private static string m_strFileSigInvalid =
|
||||
@"The file signature is invalid. Either the file isn't a KeePass database file at all or it is corrupted.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The file signature is invalid. Either the file isn't a KeePass database file at all or it is corrupted.'.
|
||||
/// </summary>
|
||||
public static string FileSigInvalid
|
||||
{
|
||||
get { return m_strFileSigInvalid; }
|
||||
}
|
||||
|
||||
private static string m_strFileUnknownCipher =
|
||||
@"The file is encrypted using an unknown encryption algorithm!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The file is encrypted using an unknown encryption algorithm!'.
|
||||
/// </summary>
|
||||
public static string FileUnknownCipher
|
||||
{
|
||||
get { return m_strFileUnknownCipher; }
|
||||
}
|
||||
|
||||
private static string m_strFileUnknownCompression =
|
||||
@"The file is compressed using an unknown compression algorithm!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The file is compressed using an unknown compression algorithm!'.
|
||||
/// </summary>
|
||||
public static string FileUnknownCompression
|
||||
{
|
||||
get { return m_strFileUnknownCompression; }
|
||||
}
|
||||
|
||||
private static string m_strFileVersionUnsupported =
|
||||
@"The file version is unsupported.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The file version is unsupported.'.
|
||||
/// </summary>
|
||||
public static string FileVersionUnsupported
|
||||
{
|
||||
get { return m_strFileVersionUnsupported; }
|
||||
}
|
||||
|
||||
private static string m_strFinalKeyCreationFailed =
|
||||
@"Failed to create the final encryption/decryption key!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Failed to create the final encryption/decryption key!'.
|
||||
/// </summary>
|
||||
public static string FinalKeyCreationFailed
|
||||
{
|
||||
get { return m_strFinalKeyCreationFailed; }
|
||||
}
|
||||
|
||||
private static string m_strFrameworkNotImplExcp =
|
||||
@"The .NET framework/runtime under which KeePass is currently running does not support this operation.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The .NET framework/runtime under which KeePass is currently running does not support this operation.'.
|
||||
/// </summary>
|
||||
public static string FrameworkNotImplExcp
|
||||
{
|
||||
get { return m_strFrameworkNotImplExcp; }
|
||||
}
|
||||
|
||||
private static string m_strGeneral =
|
||||
@"General";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'General'.
|
||||
/// </summary>
|
||||
public static string General
|
||||
{
|
||||
get { return m_strGeneral; }
|
||||
}
|
||||
|
||||
private static string m_strInvalidCompositeKey =
|
||||
@"The composite key is invalid!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The composite key is invalid!'.
|
||||
/// </summary>
|
||||
public static string InvalidCompositeKey
|
||||
{
|
||||
get { return m_strInvalidCompositeKey; }
|
||||
}
|
||||
|
||||
private static string m_strInvalidCompositeKeyHint =
|
||||
@"Make sure the composite key is correct and try again.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Make sure the composite key is correct and try again.'.
|
||||
/// </summary>
|
||||
public static string InvalidCompositeKeyHint
|
||||
{
|
||||
get { return m_strInvalidCompositeKeyHint; }
|
||||
}
|
||||
|
||||
private static string m_strInvalidDataWhileDecoding =
|
||||
@"Found invalid data while decoding.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Found invalid data while decoding.'.
|
||||
/// </summary>
|
||||
public static string InvalidDataWhileDecoding
|
||||
{
|
||||
get { return m_strInvalidDataWhileDecoding; }
|
||||
}
|
||||
|
||||
private static string m_strKeePass1xHint =
|
||||
@"In order to import KeePass 1.x KDB files, create a new 2.x database file and click 'File' -> 'Import' in the main menu. In the import dialog, choose 'KeePass KDB (1.x)' as file format.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'In order to import KeePass 1.x KDB files, create a new 2.x database file and click 'File' -> 'Import' in the main menu. In the import dialog, choose 'KeePass KDB (1.x)' as file format.'.
|
||||
/// </summary>
|
||||
public static string KeePass1xHint
|
||||
{
|
||||
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>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Database files cannot be used as key files.'.
|
||||
/// </summary>
|
||||
public static string KeyFileDbSel
|
||||
{
|
||||
get { return m_strKeyFileDbSel; }
|
||||
}
|
||||
|
||||
private static string m_strMasterSeedLengthInvalid =
|
||||
@"The length of the master key seed is invalid!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The length of the master key seed is invalid!'.
|
||||
/// </summary>
|
||||
public static string MasterSeedLengthInvalid
|
||||
{
|
||||
get { return m_strMasterSeedLengthInvalid; }
|
||||
}
|
||||
|
||||
private static string m_strOldFormat =
|
||||
@"The selected file appears to be an old format";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The selected file appears to be an old format'.
|
||||
/// </summary>
|
||||
public static string OldFormat
|
||||
{
|
||||
get { return m_strOldFormat; }
|
||||
}
|
||||
|
||||
private static string m_strPassive =
|
||||
@"Passive";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Passive'.
|
||||
/// </summary>
|
||||
public static string Passive
|
||||
{
|
||||
get { return m_strPassive; }
|
||||
}
|
||||
|
||||
private static string m_strPreAuth =
|
||||
@"Pre-authenticate";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Pre-authenticate'.
|
||||
/// </summary>
|
||||
public static string PreAuth
|
||||
{
|
||||
get { return m_strPreAuth; }
|
||||
}
|
||||
|
||||
private static string m_strTimeout =
|
||||
@"Timeout";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Timeout'.
|
||||
/// </summary>
|
||||
public static string Timeout
|
||||
{
|
||||
get { return m_strTimeout; }
|
||||
}
|
||||
|
||||
private static string m_strTryAgainSecs =
|
||||
@"Please try it again in a few seconds.";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Please try it again in a few seconds.'.
|
||||
/// </summary>
|
||||
public static string TryAgainSecs
|
||||
{
|
||||
get { return m_strTryAgainSecs; }
|
||||
}
|
||||
|
||||
private static string m_strUnknownHeaderId =
|
||||
@"Unknown header ID!";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Unknown header ID!'.
|
||||
/// </summary>
|
||||
public static string UnknownHeaderId
|
||||
{
|
||||
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>
|
||||
/// Look up a localized string similar to
|
||||
/// 'The operating system did not grant KeePass read/write access to the user profile folder, where the protected user key is stored.'.
|
||||
/// </summary>
|
||||
public static string UserAccountKeyError
|
||||
{
|
||||
get { return m_strUserAccountKeyError; }
|
||||
}
|
||||
|
||||
private static string m_strUserAgent =
|
||||
@"User agent";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'User agent'.
|
||||
/// </summary>
|
||||
public static string UserAgent
|
||||
{
|
||||
get { return m_strUserAgent; }
|
||||
}
|
||||
}
|
||||
}
|
||||
52
ModernKeePassLib/Resources/KSRes.Generated.cs
Normal file
52
ModernKeePassLib/Resources/KSRes.Generated.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// This is a generated file!
|
||||
// Do not edit manually, changes will be overwritten.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ModernKeePassLib.Resources
|
||||
{
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
public static class KSRes
|
||||
{
|
||||
private static string TryGetEx(Dictionary<string, string> dictNew,
|
||||
string strName, string strDefault)
|
||||
{
|
||||
string strTemp;
|
||||
|
||||
if(dictNew.TryGetValue(strName, out strTemp))
|
||||
return strTemp;
|
||||
|
||||
return strDefault;
|
||||
}
|
||||
|
||||
public static void SetTranslatedStrings(Dictionary<string, string> dictNew)
|
||||
{
|
||||
if(dictNew == null) throw new ArgumentNullException("dictNew");
|
||||
|
||||
m_strTest = TryGetEx(dictNew, "Test", m_strTest);
|
||||
}
|
||||
|
||||
private static readonly string[] m_vKeyNames = {
|
||||
"Test"
|
||||
};
|
||||
|
||||
public static string[] GetKeyNames()
|
||||
{
|
||||
return m_vKeyNames;
|
||||
}
|
||||
|
||||
private static string m_strTest =
|
||||
@"Test";
|
||||
/// <summary>
|
||||
/// Look up a localized string similar to
|
||||
/// 'Test'.
|
||||
/// </summary>
|
||||
public static string Test
|
||||
{
|
||||
get { return m_strTest; }
|
||||
}
|
||||
}
|
||||
}
|
||||
465
ModernKeePassLib/Security/ProtectedBinary.cs
Normal file
465
ModernKeePassLib/Security/ProtectedBinary.cs
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Security
|
||||
{
|
||||
[Flags]
|
||||
public enum PbCryptFlags
|
||||
{
|
||||
None = 0,
|
||||
Encrypt = 1,
|
||||
Decrypt = 2
|
||||
}
|
||||
|
||||
public delegate void PbCryptDelegate(byte[] pbData, PbCryptFlags cf,
|
||||
long lID);
|
||||
|
||||
/// <summary>
|
||||
/// A protected binary, i.e. a byte array that is encrypted in memory.
|
||||
/// A <c>ProtectedBinary</c> object is immutable and thread-safe.
|
||||
/// </summary>
|
||||
public sealed class ProtectedBinary : IEquatable<ProtectedBinary>
|
||||
{
|
||||
private const int BlockSize = 16;
|
||||
|
||||
private static PbCryptDelegate g_fExtCrypt = null;
|
||||
/// <summary>
|
||||
/// A plugin can provide a custom memory protection method
|
||||
/// by assigning a non-null delegate to this property.
|
||||
/// </summary>
|
||||
public static PbCryptDelegate ExtCrypt
|
||||
{
|
||||
get { return g_fExtCrypt; }
|
||||
set { g_fExtCrypt = value; }
|
||||
}
|
||||
|
||||
// Local copy of the delegate that was used for encryption,
|
||||
// in order to allow correct decryption even when the global
|
||||
// delegate changes
|
||||
private PbCryptDelegate m_fExtCrypt = null;
|
||||
|
||||
private enum PbMemProt
|
||||
{
|
||||
None = 0,
|
||||
ProtectedMemory, // DPAPI on Windows
|
||||
ChaCha20,
|
||||
ExtCrypt
|
||||
}
|
||||
|
||||
// ProtectedMemory is supported only on Windows 2000 SP3 and higher
|
||||
#if !KeePassLibSD
|
||||
private static bool? g_obProtectedMemorySupported = null;
|
||||
#endif
|
||||
private static bool ProtectedMemorySupported
|
||||
{
|
||||
get
|
||||
{
|
||||
#if KeePassLibSD
|
||||
return false;
|
||||
#else
|
||||
bool? ob = g_obProtectedMemorySupported;
|
||||
if(ob.HasValue) return ob.Value;
|
||||
|
||||
// Mono does not implement any encryption for ProtectedMemory
|
||||
// on Linux (Mono uses DPAPI on Windows);
|
||||
// https://sourceforge.net/p/keepass/feature-requests/1907/
|
||||
if(NativeLib.IsUnix())
|
||||
{
|
||||
g_obProtectedMemorySupported = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
ob = false;
|
||||
try // Test whether ProtectedMemory is supported
|
||||
{
|
||||
// BlockSize * 3 in order to test encryption for multiple
|
||||
// blocks, but not introduce a power of 2 as factor
|
||||
byte[] pb = new byte[ProtectedBinary.BlockSize * 3];
|
||||
for(int i = 0; i < pb.Length; ++i) pb[i] = (byte)i;
|
||||
|
||||
ProtectedMemory.Protect(pb, MemoryProtectionScope.SameProcess);
|
||||
|
||||
for(int i = 0; i < pb.Length; ++i)
|
||||
{
|
||||
if(pb[i] != (byte)i) { ob = true; break; }
|
||||
}
|
||||
}
|
||||
catch(Exception) { } // Windows 98 / ME
|
||||
|
||||
g_obProtectedMemorySupported = ob;
|
||||
return ob.Value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private static long g_lCurID = 0;
|
||||
private long m_lID;
|
||||
|
||||
private byte[] m_pbData; // Never null
|
||||
|
||||
// The real length of the data; this value can be different from
|
||||
// m_pbData.Length, as the length of m_pbData always is a multiple
|
||||
// of BlockSize (required for ProtectedMemory)
|
||||
private uint m_uDataLen;
|
||||
|
||||
private bool m_bProtected; // Protection requested by the caller
|
||||
|
||||
private PbMemProt m_mp = PbMemProt.None; // Actual protection
|
||||
|
||||
private readonly object m_objSync = new object();
|
||||
|
||||
private static byte[] g_pbKey32 = null;
|
||||
|
||||
/// <summary>
|
||||
/// A flag specifying whether the <c>ProtectedBinary</c> object has
|
||||
/// turned on memory protection or not.
|
||||
/// </summary>
|
||||
public bool IsProtected
|
||||
{
|
||||
get { return m_bProtected; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Length of the stored data.
|
||||
/// </summary>
|
||||
public uint Length
|
||||
{
|
||||
get { return m_uDataLen; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new, empty protected binary data object.
|
||||
/// Protection is disabled.
|
||||
/// </summary>
|
||||
public ProtectedBinary()
|
||||
{
|
||||
Init(false, MemUtil.EmptyByteArray, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new protected binary data object.
|
||||
/// </summary>
|
||||
/// <param name="bEnableProtection">If this paremeter is <c>true</c>,
|
||||
/// the data will be encrypted in memory. If it is <c>false</c>, the
|
||||
/// data is stored in plain-text in the process memory.</param>
|
||||
/// <param name="pbData">Value of the protected object.
|
||||
/// The input parameter is not modified and
|
||||
/// <c>ProtectedBinary</c> doesn't take ownership of the data,
|
||||
/// i.e. the caller is responsible for clearing it.</param>
|
||||
public ProtectedBinary(bool bEnableProtection, byte[] pbData)
|
||||
{
|
||||
if(pbData == null) throw new ArgumentNullException("pbData"); // For .Length
|
||||
|
||||
Init(bEnableProtection, pbData, 0, pbData.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new protected binary data object.
|
||||
/// </summary>
|
||||
/// <param name="bEnableProtection">If this paremeter is <c>true</c>,
|
||||
/// the data will be encrypted in memory. If it is <c>false</c>, the
|
||||
/// data is stored in plain-text in the process memory.</param>
|
||||
/// <param name="pbData">Value of the protected object.
|
||||
/// The input parameter is not modified and
|
||||
/// <c>ProtectedBinary</c> doesn't take ownership of the data,
|
||||
/// i.e. the caller is responsible for clearing it.</param>
|
||||
/// <param name="iOffset">Offset for <paramref name="pbData" />.</param>
|
||||
/// <param name="cbSize">Size for <paramref name="pbData" />.</param>
|
||||
public ProtectedBinary(bool bEnableProtection, byte[] pbData,
|
||||
int iOffset, int cbSize)
|
||||
{
|
||||
Init(bEnableProtection, pbData, iOffset, cbSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new protected binary data object.
|
||||
/// Copy the data from a <c>XorredBuffer</c> object.
|
||||
/// </summary>
|
||||
/// <param name="bEnableProtection">Enable protection or not.</param>
|
||||
/// <param name="xb"><c>XorredBuffer</c> object containing the data.</param>
|
||||
public ProtectedBinary(bool bEnableProtection, XorredBuffer xb)
|
||||
{
|
||||
if(xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
|
||||
|
||||
byte[] pb = xb.ReadPlainText();
|
||||
try { Init(bEnableProtection, pb, 0, pb.Length); }
|
||||
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
|
||||
}
|
||||
|
||||
private void Init(bool bEnableProtection, byte[] pbData, int iOffset,
|
||||
int cbSize)
|
||||
{
|
||||
if(pbData == null) throw new ArgumentNullException("pbData");
|
||||
if(iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
|
||||
if(cbSize < 0) throw new ArgumentOutOfRangeException("cbSize");
|
||||
if(iOffset > (pbData.Length - cbSize))
|
||||
throw new ArgumentOutOfRangeException("cbSize");
|
||||
|
||||
#if KeePassLibSD
|
||||
m_lID = ++g_lCurID;
|
||||
#else
|
||||
m_lID = Interlocked.Increment(ref g_lCurID);
|
||||
#endif
|
||||
|
||||
m_bProtected = bEnableProtection;
|
||||
m_uDataLen = (uint)cbSize;
|
||||
|
||||
const int bs = ProtectedBinary.BlockSize;
|
||||
int nBlocks = cbSize / bs;
|
||||
if((nBlocks * bs) < cbSize) ++nBlocks;
|
||||
Debug.Assert((nBlocks * bs) >= cbSize);
|
||||
|
||||
m_pbData = new byte[nBlocks * bs];
|
||||
Array.Copy(pbData, iOffset, m_pbData, 0, cbSize);
|
||||
|
||||
Encrypt();
|
||||
}
|
||||
|
||||
private void Encrypt()
|
||||
{
|
||||
Debug.Assert(m_mp == PbMemProt.None);
|
||||
|
||||
// Nothing to do if caller didn't request protection
|
||||
if(!m_bProtected) return;
|
||||
|
||||
// ProtectedMemory.Protect throws for data size == 0
|
||||
if(m_pbData.Length == 0) return;
|
||||
|
||||
PbCryptDelegate f = g_fExtCrypt;
|
||||
if(f != null)
|
||||
{
|
||||
f(m_pbData, PbCryptFlags.Encrypt, m_lID);
|
||||
|
||||
m_fExtCrypt = f;
|
||||
m_mp = PbMemProt.ExtCrypt;
|
||||
return;
|
||||
}
|
||||
|
||||
if(ProtectedBinary.ProtectedMemorySupported)
|
||||
{
|
||||
ProtectedMemory.Protect(m_pbData, MemoryProtectionScope.SameProcess);
|
||||
|
||||
m_mp = PbMemProt.ProtectedMemory;
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] pbKey32 = g_pbKey32;
|
||||
if(pbKey32 == null)
|
||||
{
|
||||
pbKey32 = GetRandom32();
|
||||
|
||||
byte[] pbUpd = Interlocked.Exchange<byte[]>(ref g_pbKey32, pbKey32);
|
||||
if(pbUpd != null) pbKey32 = pbUpd;
|
||||
}
|
||||
|
||||
byte[] pbIV = new byte[12];
|
||||
MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
|
||||
using(ChaCha20Cipher c = new ChaCha20Cipher(pbKey32, pbIV, true))
|
||||
{
|
||||
c.Encrypt(m_pbData, 0, m_pbData.Length);
|
||||
}
|
||||
m_mp = PbMemProt.ChaCha20;
|
||||
}
|
||||
|
||||
private void Decrypt()
|
||||
{
|
||||
if(m_pbData.Length == 0) return;
|
||||
|
||||
if(m_mp == PbMemProt.ProtectedMemory)
|
||||
ProtectedMemory.Unprotect(m_pbData, MemoryProtectionScope.SameProcess);
|
||||
else if(m_mp == PbMemProt.ChaCha20)
|
||||
{
|
||||
byte[] pbIV = new byte[12];
|
||||
MemUtil.UInt64ToBytesEx((ulong)m_lID, pbIV, 4);
|
||||
using(ChaCha20Cipher c = new ChaCha20Cipher(g_pbKey32, pbIV, true))
|
||||
{
|
||||
c.Decrypt(m_pbData, 0, m_pbData.Length);
|
||||
}
|
||||
}
|
||||
else if(m_mp == PbMemProt.ExtCrypt)
|
||||
m_fExtCrypt(m_pbData, PbCryptFlags.Decrypt, m_lID);
|
||||
else { Debug.Assert(m_mp == PbMemProt.None); }
|
||||
|
||||
m_mp = PbMemProt.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a copy of the protected data as a byte array.
|
||||
/// Please note that the returned byte array is not protected and
|
||||
/// can therefore been read by any other application.
|
||||
/// Make sure that your clear it properly after usage.
|
||||
/// </summary>
|
||||
/// <returns>Unprotected byte array. This is always a copy of the internal
|
||||
/// protected data and can therefore be cleared safely.</returns>
|
||||
public byte[] ReadData()
|
||||
{
|
||||
if(m_uDataLen == 0) return MemUtil.EmptyByteArray;
|
||||
|
||||
byte[] pbReturn = new byte[m_uDataLen];
|
||||
|
||||
lock(m_objSync)
|
||||
{
|
||||
Decrypt();
|
||||
Array.Copy(m_pbData, pbReturn, (int)m_uDataLen);
|
||||
Encrypt();
|
||||
}
|
||||
|
||||
return pbReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the data xorred with bytes from a <c>CryptoRandomStream</c>.
|
||||
/// </summary>
|
||||
public byte[] ReadXorredData(CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
if(crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
|
||||
|
||||
byte[] pbData = ReadData();
|
||||
int cb = pbData.Length;
|
||||
|
||||
byte[] pbPad = crsRandomSource.GetRandomBytes((uint)cb);
|
||||
Debug.Assert(pbPad.Length == cb);
|
||||
|
||||
for(int i = 0; i < cb; ++i)
|
||||
pbData[i] ^= pbPad[i];
|
||||
|
||||
MemUtil.ZeroByteArray(pbPad);
|
||||
return pbData;
|
||||
}
|
||||
|
||||
private int? m_hash = null;
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if(m_hash.HasValue) return m_hash.Value;
|
||||
|
||||
int h = (m_bProtected ? 0x7B11D289 : 0);
|
||||
|
||||
byte[] pb = ReadData();
|
||||
unchecked
|
||||
{
|
||||
for(int i = 0; i < pb.Length; ++i)
|
||||
h = (h << 3) + h + (int)pb[i];
|
||||
}
|
||||
if(m_bProtected) MemUtil.ZeroByteArray(pb);
|
||||
|
||||
m_hash = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return this.Equals(obj as ProtectedBinary, true);
|
||||
}
|
||||
|
||||
public bool Equals(ProtectedBinary other)
|
||||
{
|
||||
return this.Equals(other, true);
|
||||
}
|
||||
|
||||
public bool Equals(ProtectedBinary other, bool bCheckProtEqual)
|
||||
{
|
||||
if(other == null) return false; // No assert
|
||||
if(object.ReferenceEquals(this, other)) return true; // Perf. opt.
|
||||
|
||||
if(bCheckProtEqual && (m_bProtected != other.m_bProtected))
|
||||
return false;
|
||||
|
||||
if(m_uDataLen != other.m_uDataLen) return false;
|
||||
|
||||
byte[] pbL = ReadData(), pbR = null;
|
||||
bool bEq;
|
||||
try
|
||||
{
|
||||
pbR = other.ReadData();
|
||||
bEq = MemUtil.ArraysEqual(pbL, pbR);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(m_bProtected) MemUtil.ZeroByteArray(pbL);
|
||||
if(other.m_bProtected && (pbR != null)) MemUtil.ZeroByteArray(pbR);
|
||||
}
|
||||
|
||||
return bEq;
|
||||
}
|
||||
|
||||
private static byte[] GetRandom32()
|
||||
{
|
||||
// Do not use CryptoRandom here, as it uses ProtectedBinary;
|
||||
// we would have an infinite recursion when trying to
|
||||
// construct a ProtectedBinary object
|
||||
// return CryptoRandom.Instance.GetRandomBytes(32);
|
||||
|
||||
byte[] pbAll = new byte[32 + 16 + 8 + 4];
|
||||
int i = 0;
|
||||
|
||||
try
|
||||
{
|
||||
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
|
||||
byte[] pb = new byte[32];
|
||||
rng.GetBytes(pb);
|
||||
|
||||
Array.Copy(pb, 0, pbAll, i, 32);
|
||||
i += 32;
|
||||
|
||||
MemUtil.ZeroByteArray(pb);
|
||||
// In .NET 2.0, RNGCryptoServiceProvider does not
|
||||
// implement IDisposable; in later .NET versions it does
|
||||
MemUtil.DisposeIfPossible(rng);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try // In case RNGCryptoServiceProvider doesn't work properly
|
||||
{
|
||||
byte[] pb = Guid.NewGuid().ToByteArray();
|
||||
Array.Copy(pb, 0, pbAll, i, 16);
|
||||
i += 16;
|
||||
|
||||
MemUtil.Int64ToBytesEx(DateTime.UtcNow.ToBinary(), pbAll, i);
|
||||
i += 8;
|
||||
|
||||
MemUtil.Int32ToBytesEx(Environment.TickCount, pbAll, i);
|
||||
i += 4;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
Debug.Assert(i == pbAll.Length);
|
||||
|
||||
byte[] pbHash = CryptoUtil.HashSha256(pbAll);
|
||||
MemUtil.ZeroByteArray(pbAll);
|
||||
return pbHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
434
ModernKeePassLib/Security/ProtectedString.cs
Normal file
434
ModernKeePassLib/Security/ProtectedString.cs
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
// SecureString objects are limited to 65536 characters, don't use
|
||||
|
||||
namespace ModernKeePassLib.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// A string that is protected in process memory.
|
||||
/// <c>ProtectedString</c> objects are immutable and thread-safe.
|
||||
/// </summary>
|
||||
#if (DEBUG && !KeePassLibSD)
|
||||
[DebuggerDisplay("{ReadString()}")]
|
||||
#endif
|
||||
public sealed class ProtectedString
|
||||
{
|
||||
// Exactly one of the following will be non-null
|
||||
private ProtectedBinary m_pbUtf8 = null;
|
||||
private string m_strPlainText = null;
|
||||
|
||||
private bool m_bIsProtected;
|
||||
|
||||
private static readonly ProtectedString m_psEmpty = new ProtectedString();
|
||||
/// <summary>
|
||||
/// Get an empty <c>ProtectedString</c> object, without protection.
|
||||
/// </summary>
|
||||
public static ProtectedString Empty
|
||||
{
|
||||
get { return m_psEmpty; }
|
||||
}
|
||||
|
||||
private static readonly ProtectedString m_psEmptyEx = new ProtectedString(
|
||||
true, new byte[0]);
|
||||
/// <summary>
|
||||
/// Get an empty <c>ProtectedString</c> object, with protection turned on.
|
||||
/// </summary>
|
||||
public static ProtectedString EmptyEx
|
||||
{
|
||||
get { return m_psEmptyEx; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A flag specifying whether the <c>ProtectedString</c> object
|
||||
/// has turned on memory protection or not.
|
||||
/// </summary>
|
||||
public bool IsProtected
|
||||
{
|
||||
get { return m_bIsProtected; }
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
|
||||
if(p != null) return (p.Length == 0);
|
||||
|
||||
Debug.Assert(m_strPlainText != null);
|
||||
return (m_strPlainText.Length == 0);
|
||||
}
|
||||
}
|
||||
|
||||
private int m_nCachedLength = -1;
|
||||
/// <summary>
|
||||
/// Length of the protected string, in characters.
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_nCachedLength >= 0) return m_nCachedLength;
|
||||
|
||||
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
|
||||
if(p != null)
|
||||
{
|
||||
byte[] pbPlain = p.ReadData();
|
||||
try { m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); }
|
||||
finally { MemUtil.ZeroByteArray(pbPlain); }
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(m_strPlainText != null);
|
||||
m_nCachedLength = m_strPlainText.Length;
|
||||
}
|
||||
|
||||
return m_nCachedLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new protected string object. Protection is
|
||||
/// disabled.
|
||||
/// </summary>
|
||||
public ProtectedString()
|
||||
{
|
||||
Init(false, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new protected string. The string is initialized
|
||||
/// to the value supplied in the parameters.
|
||||
/// </summary>
|
||||
/// <param name="bEnableProtection">If this parameter is <c>true</c>,
|
||||
/// the string will be protected in memory (encrypted). If it
|
||||
/// is <c>false</c>, the string will be stored as plain-text.</param>
|
||||
/// <param name="strValue">The initial string value.</param>
|
||||
public ProtectedString(bool bEnableProtection, string strValue)
|
||||
{
|
||||
Init(bEnableProtection, strValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new protected string. The string is initialized
|
||||
/// to the value supplied in the parameters (UTF-8 encoded string).
|
||||
/// </summary>
|
||||
/// <param name="bEnableProtection">If this parameter is <c>true</c>,
|
||||
/// the string will be protected in memory (encrypted). If it
|
||||
/// is <c>false</c>, the string will be stored as plain-text.</param>
|
||||
/// <param name="vUtf8Value">The initial string value, encoded as
|
||||
/// UTF-8 byte array. This parameter won't be modified; the caller
|
||||
/// is responsible for clearing it.</param>
|
||||
public ProtectedString(bool bEnableProtection, byte[] vUtf8Value)
|
||||
{
|
||||
Init(bEnableProtection, vUtf8Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new protected string. The string is initialized
|
||||
/// to the value passed in the <c>XorredBuffer</c> object.
|
||||
/// </summary>
|
||||
/// <param name="bEnableProtection">Enable protection or not.</param>
|
||||
/// <param name="xb"><c>XorredBuffer</c> object containing the
|
||||
/// string in UTF-8 representation. The UTF-8 string must not
|
||||
/// be <c>null</c>-terminated.</param>
|
||||
public ProtectedString(bool bEnableProtection, XorredBuffer xb)
|
||||
{
|
||||
if(xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
|
||||
|
||||
byte[] pb = xb.ReadPlainText();
|
||||
try { Init(bEnableProtection, pb); }
|
||||
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
|
||||
}
|
||||
|
||||
private void Init(bool bEnableProtection, string str)
|
||||
{
|
||||
if(str == null) throw new ArgumentNullException("str");
|
||||
|
||||
m_bIsProtected = bEnableProtection;
|
||||
|
||||
// As the string already is in memory and immutable,
|
||||
// protection would be useless
|
||||
m_strPlainText = str;
|
||||
}
|
||||
|
||||
private void Init(bool bEnableProtection, byte[] pbUtf8)
|
||||
{
|
||||
if(pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
|
||||
|
||||
m_bIsProtected = bEnableProtection;
|
||||
|
||||
if(bEnableProtection)
|
||||
m_pbUtf8 = new ProtectedBinary(true, pbUtf8);
|
||||
else
|
||||
m_strPlainText = StrUtil.Utf8.GetString(pbUtf8, 0, pbUtf8.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the protected string to a standard string object.
|
||||
/// Be careful with this function, as the returned string object
|
||||
/// isn't protected anymore and stored in plain-text in the
|
||||
/// process memory.
|
||||
/// </summary>
|
||||
/// <returns>Plain-text string. Is never <c>null</c>.</returns>
|
||||
public string ReadString()
|
||||
{
|
||||
if(m_strPlainText != null) return m_strPlainText;
|
||||
|
||||
byte[] pb = ReadUtf8();
|
||||
string str = ((pb.Length == 0) ? string.Empty :
|
||||
StrUtil.Utf8.GetString(pb, 0, pb.Length));
|
||||
// No need to clear pb
|
||||
|
||||
// As the text is now visible in process memory anyway,
|
||||
// there's no need to protect it anymore (strings are
|
||||
// immutable and thus cannot be overwritten)
|
||||
m_strPlainText = str;
|
||||
m_pbUtf8 = null; // Thread-safe order
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read out the string and return it as a char array.
|
||||
/// The returned array is not protected and should be cleared by
|
||||
/// the caller.
|
||||
/// </summary>
|
||||
/// <returns>Plain-text char array.</returns>
|
||||
public char[] ReadChars()
|
||||
{
|
||||
if(m_strPlainText != null) return m_strPlainText.ToCharArray();
|
||||
|
||||
byte[] pb = ReadUtf8();
|
||||
char[] v;
|
||||
try { v = StrUtil.Utf8.GetChars(pb); }
|
||||
finally { MemUtil.ZeroByteArray(pb); }
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read out the string and return a byte array that contains the
|
||||
/// string encoded using UTF-8.
|
||||
/// The returned array is not protected and should be cleared by
|
||||
/// the caller.
|
||||
/// </summary>
|
||||
/// <returns>Plain-text UTF-8 byte array.</returns>
|
||||
public byte[] ReadUtf8()
|
||||
{
|
||||
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
|
||||
if(p != null) return p.ReadData();
|
||||
|
||||
return StrUtil.Utf8.GetBytes(m_strPlainText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string as an UTF-8 sequence xorred with bytes
|
||||
/// from a <c>CryptoRandomStream</c>.
|
||||
/// </summary>
|
||||
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
|
||||
{
|
||||
if(crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
|
||||
|
||||
byte[] pbData = ReadUtf8();
|
||||
int cb = pbData.Length;
|
||||
|
||||
byte[] pbPad = crsRandomSource.GetRandomBytes((uint)cb);
|
||||
Debug.Assert(pbPad.Length == cb);
|
||||
|
||||
for(int i = 0; i < cb; ++i)
|
||||
pbData[i] ^= pbPad[i];
|
||||
|
||||
MemUtil.ZeroByteArray(pbPad);
|
||||
return pbData;
|
||||
}
|
||||
|
||||
public ProtectedString WithProtection(bool bProtect)
|
||||
{
|
||||
if(bProtect == m_bIsProtected) return this;
|
||||
|
||||
byte[] pb = ReadUtf8();
|
||||
|
||||
// No need to clear pb; either the current or the new object is unprotected
|
||||
return new ProtectedString(bProtect, pb);
|
||||
}
|
||||
|
||||
public bool Equals(ProtectedString ps, bool bCheckProtEqual)
|
||||
{
|
||||
if(ps == null) throw new ArgumentNullException("ps");
|
||||
if(object.ReferenceEquals(this, ps)) return true; // Perf. opt.
|
||||
|
||||
bool bPA = m_bIsProtected, bPB = ps.m_bIsProtected;
|
||||
if(bCheckProtEqual && (bPA != bPB)) return false;
|
||||
if(!bPA && !bPB) return (ReadString() == ps.ReadString());
|
||||
|
||||
byte[] pbA = ReadUtf8(), pbB = null;
|
||||
bool bEq;
|
||||
try
|
||||
{
|
||||
pbB = ps.ReadUtf8();
|
||||
bEq = MemUtil.ArraysEqual(pbA, pbB);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(bPA) MemUtil.ZeroByteArray(pbA);
|
||||
if(bPB && (pbB != null)) MemUtil.ZeroByteArray(pbB);
|
||||
}
|
||||
|
||||
return bEq;
|
||||
}
|
||||
|
||||
public ProtectedString Insert(int iStart, string strInsert)
|
||||
{
|
||||
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
|
||||
if(strInsert == null) throw new ArgumentNullException("strInsert");
|
||||
if(strInsert.Length == 0) return this;
|
||||
|
||||
if(!m_bIsProtected)
|
||||
return new ProtectedString(false, ReadString().Insert(
|
||||
iStart, strInsert));
|
||||
|
||||
UTF8Encoding utf8 = StrUtil.Utf8;
|
||||
char[] v = ReadChars(), vNew = null;
|
||||
byte[] pbNew = null;
|
||||
ProtectedString ps;
|
||||
|
||||
try
|
||||
{
|
||||
if(iStart > v.Length)
|
||||
throw new ArgumentOutOfRangeException("iStart");
|
||||
|
||||
char[] vIns = strInsert.ToCharArray();
|
||||
|
||||
vNew = new char[v.Length + vIns.Length];
|
||||
Array.Copy(v, 0, vNew, 0, iStart);
|
||||
Array.Copy(vIns, 0, vNew, iStart, vIns.Length);
|
||||
Array.Copy(v, iStart, vNew, iStart + vIns.Length,
|
||||
v.Length - iStart);
|
||||
|
||||
pbNew = utf8.GetBytes(vNew);
|
||||
ps = new ProtectedString(true, pbNew);
|
||||
|
||||
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
|
||||
ReadString().Insert(iStart, strInsert));
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
|
||||
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
|
||||
}
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
public ProtectedString Remove(int iStart, int nCount)
|
||||
{
|
||||
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
|
||||
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
|
||||
if(nCount == 0) return this;
|
||||
|
||||
if(!m_bIsProtected)
|
||||
return new ProtectedString(false, ReadString().Remove(
|
||||
iStart, nCount));
|
||||
|
||||
UTF8Encoding utf8 = StrUtil.Utf8;
|
||||
char[] v = ReadChars(), vNew = null;
|
||||
byte[] pbNew = null;
|
||||
ProtectedString ps;
|
||||
|
||||
try
|
||||
{
|
||||
if((iStart + nCount) > v.Length)
|
||||
throw new ArgumentException("(iStart + nCount) > v.Length");
|
||||
|
||||
vNew = new char[v.Length - nCount];
|
||||
Array.Copy(v, 0, vNew, 0, iStart);
|
||||
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
|
||||
(iStart + nCount));
|
||||
|
||||
pbNew = utf8.GetBytes(vNew);
|
||||
ps = new ProtectedString(true, pbNew);
|
||||
|
||||
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
|
||||
ReadString().Remove(iStart, nCount));
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
|
||||
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
|
||||
}
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
public static ProtectedString operator +(ProtectedString a, ProtectedString b)
|
||||
{
|
||||
if(a == null) throw new ArgumentNullException("a");
|
||||
if(b == null) throw new ArgumentNullException("b");
|
||||
|
||||
if(b.IsEmpty) return a;
|
||||
if(a.IsEmpty) return b;
|
||||
if(!a.IsProtected && !b.IsProtected)
|
||||
return new ProtectedString(false, a.ReadString() + b.ReadString());
|
||||
|
||||
char[] vA = a.ReadChars(), vB = null, vNew = null;
|
||||
byte[] pbNew = null;
|
||||
ProtectedString ps;
|
||||
|
||||
try
|
||||
{
|
||||
vB = b.ReadChars();
|
||||
|
||||
vNew = new char[vA.Length + vB.Length];
|
||||
Array.Copy(vA, vNew, vA.Length);
|
||||
Array.Copy(vB, 0, vNew, vA.Length, vB.Length);
|
||||
|
||||
pbNew = StrUtil.Utf8.GetBytes(vNew);
|
||||
ps = new ProtectedString(true, pbNew);
|
||||
}
|
||||
finally
|
||||
{
|
||||
MemUtil.ZeroArray<char>(vA);
|
||||
if(vB != null) MemUtil.ZeroArray<char>(vB);
|
||||
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
|
||||
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
|
||||
}
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
public static ProtectedString operator +(ProtectedString a, string b)
|
||||
{
|
||||
ProtectedString psB = new ProtectedString(false, b);
|
||||
return (a + psB);
|
||||
}
|
||||
}
|
||||
}
|
||||
109
ModernKeePassLib/Security/XorredBuffer.cs
Normal file
109
ModernKeePassLib/Security/XorredBuffer.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// A <c>XorredBuffer</c> object stores data that is encrypted
|
||||
/// using a XOR pad.
|
||||
/// </summary>
|
||||
public sealed class XorredBuffer : IDisposable
|
||||
{
|
||||
private byte[] m_pbCT;
|
||||
private byte[] m_pbXorPad;
|
||||
|
||||
public uint Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_pbCT == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
|
||||
return (uint)m_pbCT.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new <c>XorredBuffer</c> object.
|
||||
/// The <paramref name="pbCT" /> byte array must have the same
|
||||
/// length as the <paramref name="pbXorPad" /> byte array.
|
||||
/// The <c>XorredBuffer</c> object takes ownership of the two byte
|
||||
/// arrays, i.e. the caller must not use them afterwards.
|
||||
/// </summary>
|
||||
/// <param name="pbCT">Data with XOR pad applied.</param>
|
||||
/// <param name="pbXorPad">XOR pad that can be used to decrypt the
|
||||
/// <paramref name="pbCT" /> byte array.</param>
|
||||
public XorredBuffer(byte[] pbCT, byte[] pbXorPad)
|
||||
{
|
||||
if(pbCT == null) { Debug.Assert(false); throw new ArgumentNullException("pbCT"); }
|
||||
if(pbXorPad == null) { Debug.Assert(false); throw new ArgumentNullException("pbXorPad"); }
|
||||
if(pbCT.Length != pbXorPad.Length)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ArgumentOutOfRangeException("pbXorPad");
|
||||
}
|
||||
|
||||
m_pbCT = pbCT;
|
||||
m_pbXorPad = pbXorPad;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
~XorredBuffer()
|
||||
{
|
||||
Debug.Assert((m_pbCT == null) && (m_pbXorPad == null));
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(m_pbCT == null) return;
|
||||
|
||||
MemUtil.ZeroByteArray(m_pbCT);
|
||||
m_pbCT = null;
|
||||
|
||||
MemUtil.ZeroByteArray(m_pbXorPad);
|
||||
m_pbXorPad = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a copy of the plain-text. The caller is responsible
|
||||
/// for clearing the byte array safely after using it.
|
||||
/// </summary>
|
||||
/// <returns>Plain-text byte array.</returns>
|
||||
public byte[] ReadPlainText()
|
||||
{
|
||||
byte[] pbCT = m_pbCT, pbX = m_pbXorPad;
|
||||
if((pbCT == null) || (pbX == null) || (pbCT.Length != pbX.Length))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
|
||||
byte[] pbPT = new byte[pbCT.Length];
|
||||
|
||||
for(int i = 0; i < pbPT.Length; ++i)
|
||||
pbPT[i] = (byte)(pbCT[i] ^ pbX[i]);
|
||||
|
||||
return pbPT;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
ModernKeePassLib/Serialization/BinaryReaderEx.cs
Normal file
92
ModernKeePassLib/Serialization/BinaryReaderEx.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.IO;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class BinaryReaderEx
|
||||
{
|
||||
private Stream m_s;
|
||||
// private Encoding m_enc; // See constructor
|
||||
|
||||
private string m_strReadExcp; // May be null
|
||||
public string ReadExceptionText
|
||||
{
|
||||
get { return m_strReadExcp; }
|
||||
set { m_strReadExcp = value; }
|
||||
}
|
||||
|
||||
private Stream m_sCopyTo = null;
|
||||
/// <summary>
|
||||
/// If this property is set to a non-null stream, all data that
|
||||
/// is read from the input stream is automatically written to
|
||||
/// the copy stream (before returning the read data).
|
||||
/// </summary>
|
||||
public Stream CopyDataTo
|
||||
{
|
||||
get { return m_sCopyTo; }
|
||||
set { m_sCopyTo = value; }
|
||||
}
|
||||
|
||||
public BinaryReaderEx(Stream input, Encoding encoding,
|
||||
string strReadExceptionText)
|
||||
{
|
||||
if(input == null) throw new ArgumentNullException("input");
|
||||
|
||||
m_s = input;
|
||||
// m_enc = encoding; // Not used yet
|
||||
m_strReadExcp = strReadExceptionText;
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int nCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] pb = MemUtil.Read(m_s, nCount);
|
||||
if((pb == null) || (pb.Length != nCount))
|
||||
{
|
||||
if(!string.IsNullOrEmpty(m_strReadExcp))
|
||||
throw new EndOfStreamException(m_strReadExcp);
|
||||
else throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
if(m_sCopyTo != null) m_sCopyTo.Write(pb, 0, pb.Length);
|
||||
return pb;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(m_strReadExcp))
|
||||
throw new IOException(m_strReadExcp);
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
byte[] pb = ReadBytes(1);
|
||||
return pb[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
274
ModernKeePassLib/Serialization/FileLock.cs
Normal file
274
ModernKeePassLib/Serialization/FileLock.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 System.Threading;
|
||||
#if ModernKeePassLib
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage.Streams;
|
||||
#endif
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class FileLockException : Exception
|
||||
{
|
||||
private readonly string m_strMsg;
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get { return m_strMsg; }
|
||||
}
|
||||
|
||||
public FileLockException(string strBaseFile, string strUser)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!string.IsNullOrEmpty(strBaseFile))
|
||||
{
|
||||
sb.Append(strBaseFile);
|
||||
sb.Append(MessageService.NewParagraph);
|
||||
}
|
||||
|
||||
sb.Append(KLRes.FileLockedWrite);
|
||||
sb.Append(MessageService.NewLine);
|
||||
|
||||
if(!string.IsNullOrEmpty(strUser)) sb.Append(strUser);
|
||||
else sb.Append("?");
|
||||
|
||||
sb.Append(MessageService.NewParagraph);
|
||||
sb.Append(KLRes.TryAgainSecs);
|
||||
|
||||
m_strMsg = sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FileLock : IDisposable
|
||||
{
|
||||
private const string LockFileExt = ".lock";
|
||||
private const string LockFileHeader = "KeePass Lock File";
|
||||
|
||||
private IOConnectionInfo m_iocLockFile;
|
||||
|
||||
private sealed class LockFileInfo
|
||||
{
|
||||
public readonly string ID;
|
||||
public readonly DateTime Time;
|
||||
public readonly string UserName;
|
||||
public readonly string Machine;
|
||||
public readonly string Domain;
|
||||
|
||||
private LockFileInfo(string strID, string strTime, string strUserName,
|
||||
string strMachine, string strDomain)
|
||||
{
|
||||
this.ID = (strID ?? string.Empty).Trim();
|
||||
|
||||
DateTime dt;
|
||||
if(TimeUtil.TryDeserializeUtc(strTime.Trim(), out dt))
|
||||
this.Time = dt;
|
||||
else
|
||||
{
|
||||
Debug.Assert(false);
|
||||
this.Time = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
this.UserName = (strUserName ?? string.Empty).Trim();
|
||||
this.Machine = (strMachine ?? string.Empty).Trim();
|
||||
this.Domain = (strDomain ?? string.Empty).Trim();
|
||||
|
||||
if(this.Domain.Equals(this.Machine, StrUtil.CaseIgnoreCmp))
|
||||
this.Domain = string.Empty;
|
||||
}
|
||||
|
||||
public string GetOwner()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append((this.UserName.Length > 0) ? this.UserName : "?");
|
||||
|
||||
bool bMachine = (this.Machine.Length > 0);
|
||||
bool bDomain = (this.Domain.Length > 0);
|
||||
if(bMachine || bDomain)
|
||||
{
|
||||
sb.Append(" (");
|
||||
sb.Append(this.Machine);
|
||||
if(bMachine && bDomain) sb.Append(" @ ");
|
||||
sb.Append(this.Domain);
|
||||
sb.Append(")");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static LockFileInfo Load(IOConnectionInfo iocLockFile)
|
||||
{
|
||||
Stream s = null;
|
||||
try
|
||||
{
|
||||
s = IOConnection.OpenRead(iocLockFile);
|
||||
if(s == null) return null;
|
||||
|
||||
string str = null;
|
||||
using(StreamReader sr = new StreamReader(s, StrUtil.Utf8))
|
||||
{
|
||||
str = sr.ReadToEnd();
|
||||
}
|
||||
if(str == null) { Debug.Assert(false); return null; }
|
||||
|
||||
str = StrUtil.NormalizeNewLines(str, false);
|
||||
string[] v = str.Split('\n');
|
||||
if((v == null) || (v.Length < 6)) { Debug.Assert(false); return null; }
|
||||
|
||||
if(!v[0].StartsWith(LockFileHeader)) { Debug.Assert(false); return null; }
|
||||
return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
|
||||
}
|
||||
catch(FileNotFoundException) { }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { if(s != null) s.Dispose(); }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Throws on error
|
||||
public static LockFileInfo Create(IOConnectionInfo iocLockFile)
|
||||
{
|
||||
LockFileInfo lfi;
|
||||
Stream s = null;
|
||||
try
|
||||
{
|
||||
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16);
|
||||
string strTime = TimeUtil.SerializeUtc(DateTime.UtcNow);
|
||||
|
||||
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
|
||||
#if KeePassUAP
|
||||
EnvironmentExt.UserName, EnvironmentExt.MachineName,
|
||||
EnvironmentExt.UserDomainName);
|
||||
#elif ModernKeePassLib|| KeePassLibSD
|
||||
string.Empty, string.Empty, string.Empty);
|
||||
#else
|
||||
Environment.UserName, Environment.MachineName,
|
||||
Environment.UserDomainName);
|
||||
#endif
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
#if !KeePassLibSD
|
||||
sb.AppendLine(LockFileHeader);
|
||||
sb.AppendLine(lfi.ID);
|
||||
sb.AppendLine(strTime);
|
||||
sb.AppendLine(lfi.UserName);
|
||||
sb.AppendLine(lfi.Machine);
|
||||
sb.AppendLine(lfi.Domain);
|
||||
#else
|
||||
sb.Append(LockFileHeader + MessageService.NewLine);
|
||||
sb.Append(lfi.ID + MessageService.NewLine);
|
||||
sb.Append(strTime + MessageService.NewLine);
|
||||
sb.Append(lfi.UserName + MessageService.NewLine);
|
||||
sb.Append(lfi.Machine + MessageService.NewLine);
|
||||
sb.Append(lfi.Domain + MessageService.NewLine);
|
||||
#endif
|
||||
|
||||
byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString());
|
||||
|
||||
s = IOConnection.OpenWrite(iocLockFile);
|
||||
if(s == null) throw new IOException(iocLockFile.GetDisplayName());
|
||||
s.Write(pbFile, 0, pbFile.Length);
|
||||
}
|
||||
finally { if(s != null) s.Dispose(); }
|
||||
|
||||
return lfi;
|
||||
}
|
||||
}
|
||||
|
||||
public FileLock(IOConnectionInfo iocBaseFile)
|
||||
{
|
||||
if(iocBaseFile == null) throw new ArgumentNullException("strBaseFile");
|
||||
|
||||
m_iocLockFile = iocBaseFile.CloneDeep();
|
||||
m_iocLockFile.Path += LockFileExt;
|
||||
|
||||
LockFileInfo lfiEx = LockFileInfo.Load(m_iocLockFile);
|
||||
if(lfiEx != null)
|
||||
{
|
||||
m_iocLockFile = null; // Otherwise Dispose deletes the existing one
|
||||
throw new FileLockException(iocBaseFile.GetDisplayName(),
|
||||
lfiEx.GetOwner());
|
||||
}
|
||||
|
||||
LockFileInfo.Create(m_iocLockFile);
|
||||
}
|
||||
|
||||
~FileLock()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool bDisposing)
|
||||
{
|
||||
if(m_iocLockFile == null) return;
|
||||
|
||||
bool bFileDeleted = false;
|
||||
for(int r = 0; r < 5; ++r)
|
||||
{
|
||||
// if(!OwnLockFile()) { bFileDeleted = true; break; }
|
||||
|
||||
try
|
||||
{
|
||||
IOConnection.DeleteFile(m_iocLockFile);
|
||||
bFileDeleted = true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
if(bFileDeleted) break;
|
||||
|
||||
#if ModernKeePassLib
|
||||
if(bDisposing)
|
||||
Task.Delay(50).Wait();
|
||||
#else
|
||||
if(bDisposing) Thread.Sleep(50);
|
||||
#endif
|
||||
}
|
||||
|
||||
// if(bDisposing && !bFileDeleted)
|
||||
// IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception
|
||||
|
||||
m_iocLockFile = null;
|
||||
}
|
||||
|
||||
// private bool OwnLockFile()
|
||||
// {
|
||||
// if(m_iocLockFile == null) { Debug.Assert(false); return false; }
|
||||
// if(m_strLockID == null) { Debug.Assert(false); return false; }
|
||||
// LockFileInfo lfi = LockFileInfo.Load(m_iocLockFile);
|
||||
// if(lfi == null) return false;
|
||||
// return m_strLockID.Equals(lfi.ID);
|
||||
// }
|
||||
}
|
||||
}
|
||||
474
ModernKeePassLib/Serialization/FileTransactionEx.cs
Normal file
474
ModernKeePassLib/Serialization/FileTransactionEx.cs
Normal file
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
using System.Security.AccessControl;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Resources;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class FileTransactionEx : IDisposable
|
||||
{
|
||||
private bool m_bTransacted;
|
||||
private IOConnectionInfo m_iocBase; // Null means disposed
|
||||
private IOConnectionInfo m_iocTemp;
|
||||
private IOConnectionInfo m_iocTxfMidFallback = null; // Null <=> TxF not used
|
||||
|
||||
private bool m_bMadeUnhidden = false;
|
||||
private List<IOConnectionInfo> m_lToDelete = new List<IOConnectionInfo>();
|
||||
|
||||
private const string StrTempSuffix = ".tmp";
|
||||
private static readonly string StrTxfTempPrefix = PwDefs.ShortProductName + "_TxF_";
|
||||
private const string StrTxfTempSuffix = ".tmp";
|
||||
|
||||
private static Dictionary<string, bool> g_dEnabled =
|
||||
new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer);
|
||||
|
||||
private static bool g_bExtraSafe = false;
|
||||
internal static bool ExtraSafe
|
||||
{
|
||||
get { return g_bExtraSafe; }
|
||||
set { g_bExtraSafe = value; }
|
||||
}
|
||||
|
||||
public FileTransactionEx(IOConnectionInfo iocBaseFile) :
|
||||
this(iocBaseFile, true)
|
||||
{
|
||||
}
|
||||
|
||||
public FileTransactionEx(IOConnectionInfo iocBaseFile, bool bTransacted)
|
||||
{
|
||||
if(iocBaseFile == null) throw new ArgumentNullException("iocBaseFile");
|
||||
|
||||
m_bTransacted = bTransacted;
|
||||
|
||||
m_iocBase = iocBaseFile.CloneDeep();
|
||||
if(m_iocBase.IsLocalFile())
|
||||
m_iocBase.Path = UrlUtil.GetShortestAbsolutePath(m_iocBase.Path);
|
||||
|
||||
string strPath = m_iocBase.Path;
|
||||
|
||||
#if !ModernKeePassLib
|
||||
if(m_iocBase.IsLocalFile())
|
||||
{
|
||||
try
|
||||
{
|
||||
if(File.Exists(strPath))
|
||||
{
|
||||
// Symbolic links are realized via reparse points;
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365503.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365680.aspx
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365006.aspx
|
||||
// Performing a file transaction on a symbolic link
|
||||
// would delete/replace the symbolic link instead of
|
||||
// writing to its target
|
||||
FileAttributes fa = File.GetAttributes(strPath);
|
||||
if((long)(fa & FileAttributes.ReparsePoint) != 0)
|
||||
m_bTransacted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the base and the temporary file are in different
|
||||
// folders and the base file doesn't exist (i.e. we can't
|
||||
// backup the ACL), a transaction would cause the new file
|
||||
// to have the default ACL of the temporary folder instead
|
||||
// of the one of the base folder; therefore, we don't use
|
||||
// a transaction when the base file doesn't exist (this
|
||||
// also results in other applications monitoring the folder
|
||||
// to see one file creation only)
|
||||
m_bTransacted = false;
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !ModernKeePassLib
|
||||
// Prevent transactions for FTP URLs under .NET 4.0 in order to
|
||||
// avoid/workaround .NET bug 621450:
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only
|
||||
if(strPath.StartsWith("ftp:", StrUtil.CaseIgnoreCmp) &&
|
||||
(Environment.Version.Major >= 4) && !NativeLib.IsUnix())
|
||||
m_bTransacted = false;
|
||||
#endif
|
||||
|
||||
foreach(KeyValuePair<string, bool> kvp in g_dEnabled)
|
||||
{
|
||||
if(strPath.StartsWith(kvp.Key, StrUtil.CaseIgnoreCmp))
|
||||
{
|
||||
m_bTransacted = kvp.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_bTransacted)
|
||||
{
|
||||
m_iocTemp = m_iocBase.CloneDeep();
|
||||
m_iocTemp.Path += StrTempSuffix;
|
||||
|
||||
TxfPrepare(); // Adjusts m_iocTemp
|
||||
}
|
||||
else m_iocTemp = m_iocBase;
|
||||
}
|
||||
|
||||
~FileTransactionEx()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool bDisposing)
|
||||
{
|
||||
m_iocBase = null;
|
||||
if(!bDisposing) return;
|
||||
|
||||
try
|
||||
{
|
||||
foreach(IOConnectionInfo ioc in m_lToDelete)
|
||||
{
|
||||
if(IOConnection.FileExists(ioc, false))
|
||||
IOConnection.DeleteFile(ioc);
|
||||
}
|
||||
|
||||
m_lToDelete.Clear();
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
public Stream OpenWrite()
|
||||
{
|
||||
if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
|
||||
|
||||
if(!m_bTransacted) m_bMadeUnhidden |= UrlUtil.UnhideFile(m_iocTemp.Path);
|
||||
|
||||
return IOConnection.OpenWrite(m_iocTemp);
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
|
||||
|
||||
if(!m_bTransacted)
|
||||
{
|
||||
if(m_bMadeUnhidden) UrlUtil.HideFile(m_iocTemp.Path, true);
|
||||
}
|
||||
else CommitWriteTransaction();
|
||||
|
||||
m_iocBase = null; // Dispose
|
||||
}
|
||||
|
||||
private void CommitWriteTransaction()
|
||||
{
|
||||
if(g_bExtraSafe)
|
||||
{
|
||||
if(!IOConnection.FileExists(m_iocTemp))
|
||||
throw new FileNotFoundException(m_iocTemp.Path +
|
||||
MessageService.NewLine + KLRes.FileSaveFailed);
|
||||
}
|
||||
|
||||
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
|
||||
|
||||
#if !ModernKeePassLib
|
||||
// 'All' includes 'Audit' (SACL), which requires SeSecurityPrivilege,
|
||||
// which we usually don't have and therefore get an exception;
|
||||
// trying to set 'Owner' or 'Group' can result in an
|
||||
// UnauthorizedAccessException; thus we restore 'Access' (DACL) only
|
||||
const AccessControlSections acs = AccessControlSections.Access;
|
||||
|
||||
#endif
|
||||
bool bEfsEncrypted = false;
|
||||
byte[] pbSec = null;
|
||||
DateTime? otCreation = null;
|
||||
|
||||
bool bBaseExists = IOConnection.FileExists(m_iocBase);
|
||||
if(bBaseExists && m_iocBase.IsLocalFile())
|
||||
{
|
||||
// FileAttributes faBase = FileAttributes.Normal;
|
||||
try
|
||||
{
|
||||
#if !ModernKeePassLib
|
||||
FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
|
||||
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
|
||||
try { if(bEfsEncrypted) File.Decrypt(m_iocBase.Path); } // For TxF
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
#if ModernKeePassLib
|
||||
otCreation = m_iocBase.StorageFile.DateCreated.UtcDateTime;
|
||||
#else
|
||||
otCreation = File.GetCreationTimeUtc(m_iocBase.Path);
|
||||
#endif
|
||||
#if !ModernKeePassLib
|
||||
// May throw with Mono
|
||||
FileSecurity sec = File.GetAccessControl(m_iocBase.Path, acs);
|
||||
if(sec != null) pbSec = sec.GetSecurityDescriptorBinaryForm();
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
|
||||
|
||||
// if((long)(faBase & FileAttributes.ReadOnly) != 0)
|
||||
// throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if(!TxfMove())
|
||||
{
|
||||
if(bBaseExists) IOConnection.DeleteFile(m_iocBase);
|
||||
IOConnection.RenameFile(m_iocTemp, m_iocBase);
|
||||
}
|
||||
else { Debug.Assert(pbSec != null); } // TxF success => NTFS => has ACL
|
||||
|
||||
try
|
||||
{
|
||||
// If File.GetCreationTimeUtc fails, it may return a
|
||||
// date with year 1601, and Unix times start in 1970,
|
||||
// so testing for 1971 should ensure validity;
|
||||
// https://msdn.microsoft.com/en-us/library/system.io.file.getcreationtimeutc.aspx
|
||||
#if !ModernKeePassLib
|
||||
if(otCreation.HasValue && (otCreation.Value.Year >= 1971))
|
||||
File.SetCreationTimeUtc(m_iocBase.Path, otCreation.Value);
|
||||
#endif
|
||||
|
||||
#if !ModernKeePassLib
|
||||
if(bEfsEncrypted)
|
||||
{
|
||||
try { File.Encrypt(m_iocBase.Path); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
// File.SetAccessControl(m_iocBase.Path, secPrev);
|
||||
// Directly calling File.SetAccessControl with the previous
|
||||
// FileSecurity object does not work; the binary form
|
||||
// indirection is required;
|
||||
// https://sourceforge.net/p/keepass/bugs/1738/
|
||||
// https://msdn.microsoft.com/en-us/library/system.io.file.setaccesscontrol.aspx
|
||||
if((pbSec != null) && (pbSec.Length != 0))
|
||||
{
|
||||
FileSecurity sec = new FileSecurity();
|
||||
sec.SetSecurityDescriptorBinaryForm(pbSec, acs);
|
||||
|
||||
File.SetAccessControl(m_iocBase.Path, sec);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true);
|
||||
}
|
||||
|
||||
// For plugins
|
||||
public static void Configure(string strPrefix, bool? obTransacted)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strPrefix)) { Debug.Assert(false); return; }
|
||||
|
||||
if(obTransacted.HasValue)
|
||||
g_dEnabled[strPrefix] = obTransacted.Value;
|
||||
else g_dEnabled.Remove(strPrefix);
|
||||
}
|
||||
|
||||
private static bool TxfIsSupported(char chDriveLetter)
|
||||
{
|
||||
if(chDriveLetter == '\0') return false;
|
||||
|
||||
#if ModernKeePassLib
|
||||
return true;
|
||||
#else
|
||||
try
|
||||
{
|
||||
string strRoot = (new string(chDriveLetter, 1)) + ":\\";
|
||||
|
||||
const int cch = NativeMethods.MAX_PATH + 1;
|
||||
StringBuilder sbName = new StringBuilder(cch + 1);
|
||||
uint uSerial = 0, cchMaxComp = 0, uFlags = 0;
|
||||
StringBuilder sbFileSystem = new StringBuilder(cch + 1);
|
||||
|
||||
if(!NativeMethods.GetVolumeInformation(strRoot, sbName, (uint)cch,
|
||||
ref uSerial, ref cchMaxComp, ref uFlags, sbFileSystem, (uint)cch))
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((uFlags & NativeMethods.FILE_SUPPORTS_TRANSACTIONS) != 0);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void TxfPrepare()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(NativeLib.IsUnix()) return;
|
||||
if(!m_iocBase.IsLocalFile()) return;
|
||||
|
||||
string strID = StrUtil.AlphaNumericOnly(Convert.ToBase64String(
|
||||
CryptoRandom.Instance.GetRandomBytes(16)));
|
||||
string strTempDir = UrlUtil.GetTempPath();
|
||||
// See also ClearOld method
|
||||
string strTemp = UrlUtil.EnsureTerminatingSeparator(strTempDir,
|
||||
false) + StrTxfTempPrefix + strID + StrTxfTempSuffix;
|
||||
|
||||
char chB = UrlUtil.GetDriveLetter(m_iocBase.Path);
|
||||
char chT = UrlUtil.GetDriveLetter(strTemp);
|
||||
if(!TxfIsSupported(chB)) return;
|
||||
if((chT != chB) && !TxfIsSupported(chT)) return;
|
||||
|
||||
m_iocTxfMidFallback = m_iocTemp;
|
||||
#if ModernKeePassLib
|
||||
var tempFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync(m_iocTemp.Path).GetAwaiter()
|
||||
.GetResult();
|
||||
m_iocTemp = IOConnectionInfo.FromFile(tempFile);
|
||||
#else
|
||||
m_iocTemp = IOConnectionInfo.FromPath(strTemp);
|
||||
#endif
|
||||
|
||||
m_lToDelete.Add(m_iocTemp);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); m_iocTxfMidFallback = null; }
|
||||
}
|
||||
|
||||
private bool TxfMove()
|
||||
{
|
||||
if(m_iocTxfMidFallback == null) return false;
|
||||
|
||||
if(TxfMoveWithTx()) return true;
|
||||
|
||||
// Move the temporary file onto the base file's drive first,
|
||||
// such that it cannot happen that both the base file and
|
||||
// the temporary file are deleted/corrupted
|
||||
#if ModernKeePassLib
|
||||
m_iocTemp.StorageFile = ApplicationData.Current.TemporaryFolder.CreateFileAsync(m_iocTemp.Path).GetAwaiter()
|
||||
.GetResult();
|
||||
#else
|
||||
const uint f = (NativeMethods.MOVEFILE_COPY_ALLOWED |
|
||||
NativeMethods.MOVEFILE_REPLACE_EXISTING);
|
||||
bool b = NativeMethods.MoveFileEx(m_iocTemp.Path, m_iocTxfMidFallback.Path, f);
|
||||
if(b) b = NativeMethods.MoveFileEx(m_iocTxfMidFallback.Path, m_iocBase.Path, f);
|
||||
if(!b) throw new Win32Exception();
|
||||
|
||||
Debug.Assert(!File.Exists(m_iocTemp.Path));
|
||||
Debug.Assert(!File.Exists(m_iocTxfMidFallback.Path));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TxfMoveWithTx()
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
return true;
|
||||
#else
|
||||
IntPtr hTx = new IntPtr((int)NativeMethods.INVALID_HANDLE_VALUE);
|
||||
Debug.Assert(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE);
|
||||
try
|
||||
{
|
||||
string strTx = PwDefs.ShortProductName + " TxF - " +
|
||||
StrUtil.AlphaNumericOnly(Convert.ToBase64String(
|
||||
CryptoRandom.Instance.GetRandomBytes(16)));
|
||||
const int mchTx = NativeMethods.MAX_TRANSACTION_DESCRIPTION_LENGTH;
|
||||
if(strTx.Length >= mchTx) strTx = strTx.Substring(0, mchTx - 1);
|
||||
|
||||
hTx = NativeMethods.CreateTransaction(IntPtr.Zero,
|
||||
IntPtr.Zero, 0, 0, 0, 0, strTx);
|
||||
if(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!NativeMethods.MoveFileTransacted(m_iocTemp.Path, m_iocBase.Path,
|
||||
IntPtr.Zero, IntPtr.Zero, (NativeMethods.MOVEFILE_COPY_ALLOWED |
|
||||
NativeMethods.MOVEFILE_REPLACE_EXISTING), hTx))
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!NativeMethods.CommitTransaction(hTx))
|
||||
{
|
||||
Debug.Assert(false, (new Win32Exception()).Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Assert(!File.Exists(m_iocTemp.Path));
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally
|
||||
{
|
||||
if(hTx.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE)
|
||||
{
|
||||
try { if(!NativeMethods.CloseHandle(hTx)) { Debug.Assert(false); } }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ClearOld()
|
||||
{
|
||||
try
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
ApplicationData.Current.TemporaryFolder.GetFileAsync(UrlUtil.GetTempPath()).GetAwaiter()
|
||||
.GetResult().DeleteAsync().GetAwaiter().GetResult();
|
||||
#else
|
||||
// See also TxfPrepare method
|
||||
DirectoryInfo di = new DirectoryInfo(UrlUtil.GetTempPath());
|
||||
List<FileInfo> l = UrlUtil.GetFileInfos(di, StrTxfTempPrefix +
|
||||
"*" + StrTxfTempSuffix, SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach(FileInfo fi in l)
|
||||
{
|
||||
if(fi == null) { Debug.Assert(false); continue; }
|
||||
if(!fi.Name.StartsWith(StrTxfTempPrefix, StrUtil.CaseIgnoreCmp) ||
|
||||
!fi.Name.EndsWith(StrTxfTempSuffix, StrUtil.CaseIgnoreCmp))
|
||||
continue;
|
||||
|
||||
if((DateTime.UtcNow - fi.LastWriteTimeUtc).TotalDays > 1.0)
|
||||
fi.Delete();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
312
ModernKeePassLib/Serialization/HashedBlockStream.cs
Normal file
312
ModernKeePassLib/Serialization/HashedBlockStream.cs
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
#if KeePassLibSD
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class HashedBlockStream : Stream
|
||||
{
|
||||
private const int NbDefaultBufferSize = 1024 * 1024; // 1 MB
|
||||
|
||||
private Stream m_sBaseStream;
|
||||
private bool m_bWriting;
|
||||
private bool m_bVerify;
|
||||
private bool m_bEos = false;
|
||||
|
||||
private BinaryReader m_brInput;
|
||||
private BinaryWriter m_bwOutput;
|
||||
|
||||
private byte[] m_pbBuffer;
|
||||
private int m_nBufferPos = 0;
|
||||
|
||||
private uint 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 HashedBlockStream(Stream sBaseStream, bool bWriting)
|
||||
{
|
||||
Initialize(sBaseStream, bWriting, 0, true);
|
||||
}
|
||||
|
||||
public HashedBlockStream(Stream sBaseStream, bool bWriting, int nBufferSize)
|
||||
{
|
||||
Initialize(sBaseStream, bWriting, nBufferSize, true);
|
||||
}
|
||||
|
||||
public HashedBlockStream(Stream sBaseStream, bool bWriting, int nBufferSize,
|
||||
bool bVerify)
|
||||
{
|
||||
Initialize(sBaseStream, bWriting, nBufferSize, bVerify);
|
||||
}
|
||||
|
||||
private void Initialize(Stream sBaseStream, bool bWriting, int nBufferSize,
|
||||
bool bVerify)
|
||||
{
|
||||
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
|
||||
if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize");
|
||||
|
||||
if(nBufferSize == 0) nBufferSize = NbDefaultBufferSize;
|
||||
|
||||
m_sBaseStream = sBaseStream;
|
||||
m_bWriting = bWriting;
|
||||
m_bVerify = bVerify;
|
||||
|
||||
UTF8Encoding utf8 = StrUtil.Utf8;
|
||||
if(!m_bWriting) // Reading mode
|
||||
{
|
||||
if(!m_sBaseStream.CanRead)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
m_brInput = new BinaryReader(sBaseStream, utf8);
|
||||
|
||||
m_pbBuffer = MemUtil.EmptyByteArray;
|
||||
}
|
||||
else // Writing mode
|
||||
{
|
||||
if(!m_sBaseStream.CanWrite)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
m_bwOutput = new BinaryWriter(sBaseStream, utf8);
|
||||
|
||||
m_pbBuffer = new byte[nBufferSize];
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing && (m_sBaseStream != null))
|
||||
{
|
||||
if(!m_bWriting) // Reading mode
|
||||
{
|
||||
m_brInput.Dispose();
|
||||
m_brInput = null;
|
||||
}
|
||||
else // Writing mode
|
||||
{
|
||||
if(m_nBufferPos == 0) // No data left in buffer
|
||||
WriteHashedBlock(); // Write terminating block
|
||||
else
|
||||
{
|
||||
WriteHashedBlock(); // Write remaining buffered data
|
||||
WriteHashedBlock(); // Write terminating block
|
||||
}
|
||||
|
||||
Flush();
|
||||
m_bwOutput.Dispose();
|
||||
m_bwOutput = null;
|
||||
}
|
||||
|
||||
m_sBaseStream.Dispose();
|
||||
m_sBaseStream = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if(m_bWriting) m_bwOutput.Flush();
|
||||
}
|
||||
|
||||
public override long Seek(long lOffset, SeekOrigin soOrigin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long lValue)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] pbBuffer, int nOffset, int nCount)
|
||||
{
|
||||
if(m_bWriting) throw new InvalidOperationException();
|
||||
|
||||
int nRemaining = nCount;
|
||||
while(nRemaining > 0)
|
||||
{
|
||||
if(m_nBufferPos == m_pbBuffer.Length)
|
||||
{
|
||||
if(ReadHashedBlock() == false)
|
||||
return (nCount - nRemaining); // Bytes actually read
|
||||
}
|
||||
|
||||
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nRemaining);
|
||||
|
||||
Array.Copy(m_pbBuffer, m_nBufferPos, pbBuffer, nOffset, nCopy);
|
||||
|
||||
nOffset += nCopy;
|
||||
m_nBufferPos += nCopy;
|
||||
|
||||
nRemaining -= nCopy;
|
||||
}
|
||||
|
||||
return nCount;
|
||||
}
|
||||
|
||||
private bool ReadHashedBlock()
|
||||
{
|
||||
if(m_bEos) return false; // End of stream reached already
|
||||
|
||||
m_nBufferPos = 0;
|
||||
|
||||
if(m_brInput.ReadUInt32() != m_uBlockIndex)
|
||||
throw new InvalidDataException();
|
||||
++m_uBlockIndex;
|
||||
|
||||
byte[] pbStoredHash = m_brInput.ReadBytes(32);
|
||||
if((pbStoredHash == null) || (pbStoredHash.Length != 32))
|
||||
throw new InvalidDataException();
|
||||
|
||||
int nBufferSize = 0;
|
||||
try { nBufferSize = m_brInput.ReadInt32(); }
|
||||
catch(NullReferenceException) // Mono bug workaround (LaunchPad 783268)
|
||||
{
|
||||
if(!NativeLib.IsUnix()) throw;
|
||||
}
|
||||
|
||||
if(nBufferSize < 0)
|
||||
throw new InvalidDataException();
|
||||
|
||||
if(nBufferSize == 0)
|
||||
{
|
||||
for(int iHash = 0; iHash < 32; ++iHash)
|
||||
{
|
||||
if(pbStoredHash[iHash] != 0)
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
m_bEos = true;
|
||||
m_pbBuffer = MemUtil.EmptyByteArray;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pbBuffer = m_brInput.ReadBytes(nBufferSize);
|
||||
if((m_pbBuffer == null) || ((m_pbBuffer.Length != nBufferSize) && m_bVerify))
|
||||
throw new InvalidDataException();
|
||||
|
||||
if(m_bVerify)
|
||||
{
|
||||
byte[] pbComputedHash = CryptoUtil.HashSha256(m_pbBuffer);
|
||||
if((pbComputedHash == null) || (pbComputedHash.Length != 32))
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if(!MemUtil.ArraysEqual(pbStoredHash, pbComputedHash))
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Write(byte[] pbBuffer, int nOffset, int nCount)
|
||||
{
|
||||
if(!m_bWriting) throw new InvalidOperationException();
|
||||
|
||||
while(nCount > 0)
|
||||
{
|
||||
if(m_nBufferPos == m_pbBuffer.Length)
|
||||
WriteHashedBlock();
|
||||
|
||||
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nCount);
|
||||
|
||||
Array.Copy(pbBuffer, nOffset, m_pbBuffer, m_nBufferPos, nCopy);
|
||||
|
||||
nOffset += nCopy;
|
||||
m_nBufferPos += nCopy;
|
||||
|
||||
nCount -= nCopy;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteHashedBlock()
|
||||
{
|
||||
m_bwOutput.Write(m_uBlockIndex);
|
||||
++m_uBlockIndex;
|
||||
|
||||
if(m_nBufferPos > 0)
|
||||
{
|
||||
byte[] pbHash = CryptoUtil.HashSha256(m_pbBuffer, 0, m_nBufferPos);
|
||||
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bwOutput.Write((ulong)0); // Zero hash
|
||||
m_bwOutput.Write((ulong)0);
|
||||
m_bwOutput.Write((ulong)0);
|
||||
m_bwOutput.Write((ulong)0);
|
||||
}
|
||||
|
||||
m_bwOutput.Write(m_nBufferPos);
|
||||
|
||||
if(m_nBufferPos > 0)
|
||||
m_bwOutput.Write(m_pbBuffer, 0, m_nBufferPos);
|
||||
|
||||
m_nBufferPos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
324
ModernKeePassLib/Serialization/HmacBlockStream.cs
Normal file
324
ModernKeePassLib/Serialization/HmacBlockStream.cs
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.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];
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing && (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.Dispose();
|
||||
m_sBase = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
Debug.Assert(m_sBase != null); // Object should not be disposed
|
||||
if(m_bWriting && (m_sBase != null)) m_sBase.Flush();
|
||||
}
|
||||
|
||||
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(KLRes.FileCorrupted + " " +
|
||||
KLRes.FileIncomplete);
|
||||
|
||||
// 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(KLRes.FileCorrupted + " " +
|
||||
KLRes.FileIncomplete);
|
||||
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(KLRes.FileCorrupted + " " +
|
||||
KLRes.FileIncompleteExpc);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
926
ModernKeePassLib/Serialization/IOConnection.cs
Normal file
926
ModernKeePassLib/Serialization/IOConnection.cs
Normal file
@@ -0,0 +1,926 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassUAP)
|
||||
using System.Net.Cache;
|
||||
using System.Net.Security;
|
||||
#endif
|
||||
|
||||
#if !ModernKeePassLib && !KeePassUAP
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#endif
|
||||
|
||||
#if ModernKeePassLib
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
#endif
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
internal sealed class IOWebClient : WebClient
|
||||
{
|
||||
private IOConnectionInfo m_ioc;
|
||||
|
||||
public IOWebClient(IOConnectionInfo ioc) : base()
|
||||
{
|
||||
m_ioc = ioc;
|
||||
}
|
||||
|
||||
protected override WebRequest GetWebRequest(Uri address)
|
||||
{
|
||||
WebRequest request = base.GetWebRequest(address);
|
||||
IOConnection.ConfigureWebRequest(request, m_ioc);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !ModernKeePassLib
|
||||
internal abstract class WrapperStream : Stream
|
||||
{
|
||||
private readonly Stream m_s;
|
||||
protected Stream BaseStream
|
||||
{
|
||||
get { return m_s; }
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return m_s.CanRead; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return m_s.CanSeek; }
|
||||
}
|
||||
|
||||
public override bool CanTimeout
|
||||
{
|
||||
get { return m_s.CanTimeout; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return m_s.CanWrite; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { return m_s.Length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return m_s.Position; }
|
||||
set { m_s.Position = value; }
|
||||
}
|
||||
|
||||
public override int ReadTimeout
|
||||
{
|
||||
get { return m_s.ReadTimeout; }
|
||||
set { m_s.ReadTimeout = value; }
|
||||
}
|
||||
|
||||
public override int WriteTimeout
|
||||
{
|
||||
get { return m_s.WriteTimeout; }
|
||||
set { m_s.WriteTimeout = value; }
|
||||
}
|
||||
|
||||
public WrapperStream(Stream sBase) : base()
|
||||
{
|
||||
if(sBase == null) throw new ArgumentNullException("sBase");
|
||||
|
||||
m_s = sBase;
|
||||
}
|
||||
|
||||
#if !KeePassUAP
|
||||
public override IAsyncResult BeginRead(byte[] buffer, int offset,
|
||||
int count, AsyncCallback callback, object state)
|
||||
{
|
||||
return m_s.BeginRead(buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWrite(byte[] buffer, int offset,
|
||||
int count, AsyncCallback callback, object state)
|
||||
{
|
||||
return BeginWrite(buffer, offset, count, callback, state);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if(disposing) m_s.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#if !KeePassUAP
|
||||
public override int EndRead(IAsyncResult asyncResult)
|
||||
{
|
||||
return m_s.EndRead(asyncResult);
|
||||
}
|
||||
|
||||
public override void EndWrite(IAsyncResult asyncResult)
|
||||
{
|
||||
m_s.EndWrite(asyncResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
m_s.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return m_s.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
return m_s.ReadByte();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return m_s.Seek(offset, origin);
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
m_s.SetLength(value);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
m_s.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
m_s.WriteByte(value);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class IocStream : WrapperStream
|
||||
{
|
||||
private readonly bool m_bWrite; // Initially opened for writing
|
||||
private bool m_bDisposed = false;
|
||||
|
||||
public IocStream(Stream sBase) : base(sBase)
|
||||
{
|
||||
m_bWrite = sBase.CanWrite;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if(disposing && MonoWorkarounds.IsRequired(10163) && m_bWrite &&
|
||||
!m_bDisposed)
|
||||
{
|
||||
try
|
||||
{
|
||||
Stream s = this.BaseStream;
|
||||
Type t = s.GetType();
|
||||
if(t.Name == "WebConnectionStream")
|
||||
{
|
||||
PropertyInfo pi = t.GetProperty("Request",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if(pi != null)
|
||||
{
|
||||
WebRequest wr = (pi.GetValue(s, null) as WebRequest);
|
||||
if(wr != null)
|
||||
IOConnection.DisposeResponse(wr.GetResponse(), false);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
m_bDisposed = true;
|
||||
}
|
||||
|
||||
public static Stream WrapIfRequired(Stream s)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); return null; }
|
||||
|
||||
if(MonoWorkarounds.IsRequired(10163) && s.CanWrite)
|
||||
return new IocStream(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static class IOConnection
|
||||
{
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
private static ProxyServerType m_pstProxyType = ProxyServerType.System;
|
||||
private static string m_strProxyAddr = string.Empty;
|
||||
private static string m_strProxyPort = string.Empty;
|
||||
private static ProxyAuthType m_patProxyAuthType = ProxyAuthType.Auto;
|
||||
private static string m_strProxyUserName = string.Empty;
|
||||
private static string m_strProxyPassword = string.Empty;
|
||||
|
||||
#if !KeePassUAP
|
||||
private static bool? m_obDefaultExpect100Continue = null;
|
||||
|
||||
private static bool m_bSslCertsAcceptInvalid = false;
|
||||
internal static bool SslCertsAcceptInvalid
|
||||
{
|
||||
// get { return m_bSslCertsAcceptInvalid; }
|
||||
set { m_bSslCertsAcceptInvalid = value; }
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Web request methods
|
||||
public static readonly string WrmDeleteFile = "DELETEFILE";
|
||||
public static readonly string WrmMoveFile = "MOVEFILE";
|
||||
|
||||
// Web request headers
|
||||
public static readonly string WrhMoveFileTo = "MoveFileTo";
|
||||
|
||||
public static event EventHandler<IOAccessEventArgs> IOAccessPre;
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
// Allow self-signed certificates, expired certificates, etc.
|
||||
private static bool AcceptCertificate(object sender,
|
||||
X509Certificate certificate, X509Chain chain,
|
||||
SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void SetProxy(ProxyServerType pst, string strAddr,
|
||||
string strPort, ProxyAuthType pat, string strUserName,
|
||||
string strPassword)
|
||||
{
|
||||
m_pstProxyType = pst;
|
||||
m_strProxyAddr = (strAddr ?? string.Empty);
|
||||
m_strProxyPort = (strPort ?? string.Empty);
|
||||
m_patProxyAuthType = pat;
|
||||
m_strProxyUserName = (strUserName ?? string.Empty);
|
||||
m_strProxyPassword = (strPassword ?? string.Empty);
|
||||
}
|
||||
|
||||
internal static void ConfigureWebRequest(WebRequest request,
|
||||
IOConnectionInfo ioc)
|
||||
{
|
||||
if(request == null) { Debug.Assert(false); return; } // No throw
|
||||
|
||||
IocProperties p = ((ioc != null) ? ioc.Properties : null);
|
||||
if(p == null) { Debug.Assert(false); p = new IocProperties(); }
|
||||
|
||||
IHasIocProperties ihpReq = (request as IHasIocProperties);
|
||||
if(ihpReq != null)
|
||||
{
|
||||
IocProperties pEx = ihpReq.IOConnectionProperties;
|
||||
if(pEx != null) p.CopyTo(pEx);
|
||||
else ihpReq.IOConnectionProperties = p.CloneDeep();
|
||||
}
|
||||
|
||||
if(IsHttpWebRequest(request))
|
||||
{
|
||||
// WebDAV support
|
||||
#if !KeePassUAP
|
||||
request.PreAuthenticate = true; // Also auth GET
|
||||
#endif
|
||||
if(string.Equals(request.Method, WebRequestMethods.Http.Post,
|
||||
StrUtil.CaseIgnoreCmp))
|
||||
request.Method = WebRequestMethods.Http.Put;
|
||||
|
||||
#if !KeePassUAP
|
||||
HttpWebRequest hwr = (request as HttpWebRequest);
|
||||
if(hwr != null)
|
||||
{
|
||||
string strUA = p.Get(IocKnownProperties.UserAgent);
|
||||
if(!string.IsNullOrEmpty(strUA)) hwr.UserAgent = strUA;
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
#endif
|
||||
}
|
||||
#if !KeePassUAP
|
||||
else if(IsFtpWebRequest(request))
|
||||
{
|
||||
FtpWebRequest fwr = (request as FtpWebRequest);
|
||||
if(fwr != null)
|
||||
{
|
||||
bool? obPassive = p.GetBool(IocKnownProperties.Passive);
|
||||
if(obPassive.HasValue) fwr.UsePassive = obPassive.Value;
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !KeePassUAP
|
||||
// Not implemented and ignored in Mono < 2.10
|
||||
try
|
||||
{
|
||||
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
|
||||
}
|
||||
catch(NotImplementedException) { }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
IWebProxy prx;
|
||||
if(GetWebProxy(out prx)) request.Proxy = prx;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
#if !KeePassUAP
|
||||
long? olTimeout = p.GetLong(IocKnownProperties.Timeout);
|
||||
if(olTimeout.HasValue && (olTimeout.Value >= 0))
|
||||
request.Timeout = (int)Math.Min(olTimeout.Value, (long)int.MaxValue);
|
||||
|
||||
bool? ob = p.GetBool(IocKnownProperties.PreAuth);
|
||||
if(ob.HasValue) request.PreAuthenticate = ob.Value;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void ConfigureWebClient(WebClient wc)
|
||||
{
|
||||
#if !KeePassUAP
|
||||
// Not implemented and ignored in Mono < 2.10
|
||||
try
|
||||
{
|
||||
wc.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
|
||||
}
|
||||
catch(NotImplementedException) { }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
IWebProxy prx;
|
||||
if(GetWebProxy(out prx)) wc.Proxy = prx;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
private static bool GetWebProxy(out IWebProxy prx)
|
||||
{
|
||||
bool b = GetWebProxyServer(out prx);
|
||||
if(b) AssignCredentials(prx);
|
||||
return b;
|
||||
}
|
||||
|
||||
private static bool GetWebProxyServer(out IWebProxy prx)
|
||||
{
|
||||
prx = null;
|
||||
|
||||
if(m_pstProxyType == ProxyServerType.None)
|
||||
return true; // Use null proxy
|
||||
|
||||
if(m_pstProxyType == ProxyServerType.Manual)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_strProxyAddr.Length == 0)
|
||||
{
|
||||
// First try default (from config), then system
|
||||
prx = WebRequest.DefaultWebProxy;
|
||||
#if !KeePassUAP
|
||||
if(prx == null) prx = WebRequest.GetSystemWebProxy();
|
||||
#endif
|
||||
}
|
||||
else if(m_strProxyPort.Length > 0)
|
||||
prx = new WebProxy(m_strProxyAddr, int.Parse(m_strProxyPort));
|
||||
else prx = new WebProxy(m_strProxyAddr);
|
||||
|
||||
return (prx != null);
|
||||
}
|
||||
#if KeePassUAP
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#else
|
||||
catch(Exception ex)
|
||||
{
|
||||
string strInfo = m_strProxyAddr;
|
||||
if(m_strProxyPort.Length > 0)
|
||||
strInfo += ":" + m_strProxyPort;
|
||||
MessageService.ShowWarning(strInfo, ex);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false; // Use default
|
||||
}
|
||||
|
||||
Debug.Assert(m_pstProxyType == ProxyServerType.System);
|
||||
try
|
||||
{
|
||||
// First try system, then default (from config)
|
||||
#if !KeePassUAP
|
||||
prx = WebRequest.GetSystemWebProxy();
|
||||
#endif
|
||||
if(prx == null) prx = WebRequest.DefaultWebProxy;
|
||||
|
||||
return (prx != null);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void AssignCredentials(IWebProxy prx)
|
||||
{
|
||||
if(prx == null) return; // No assert
|
||||
|
||||
string strUserName = m_strProxyUserName;
|
||||
string strPassword = m_strProxyPassword;
|
||||
|
||||
ProxyAuthType pat = m_patProxyAuthType;
|
||||
if(pat == ProxyAuthType.Auto)
|
||||
{
|
||||
if((strUserName.Length > 0) || (strPassword.Length > 0))
|
||||
pat = ProxyAuthType.Manual;
|
||||
else pat = ProxyAuthType.Default;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(pat == ProxyAuthType.None)
|
||||
prx.Credentials = null;
|
||||
else if(pat == ProxyAuthType.Default)
|
||||
prx.Credentials = CredentialCache.DefaultCredentials;
|
||||
else if(pat == ProxyAuthType.Manual)
|
||||
{
|
||||
if((strUserName.Length > 0) || (strPassword.Length > 0))
|
||||
prx.Credentials = new NetworkCredential(
|
||||
strUserName, strPassword);
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
private static void PrepareWebAccess(IOConnectionInfo ioc)
|
||||
{
|
||||
#if !KeePassUAP
|
||||
IocProperties p = ((ioc != null) ? ioc.Properties : null);
|
||||
if(p == null) { Debug.Assert(false); p = new IocProperties(); }
|
||||
|
||||
try
|
||||
{
|
||||
if(m_bSslCertsAcceptInvalid)
|
||||
ServicePointManager.ServerCertificateValidationCallback =
|
||||
IOConnection.AcceptCertificate;
|
||||
else
|
||||
ServicePointManager.ServerCertificateValidationCallback = null;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try
|
||||
{
|
||||
SecurityProtocolType spt = (SecurityProtocolType.Ssl3 |
|
||||
SecurityProtocolType.Tls);
|
||||
|
||||
// The flags Tls11 and Tls12 in SecurityProtocolType have been
|
||||
// introduced in .NET 4.5 and must not be set when running under
|
||||
// older .NET versions (otherwise an exception is thrown)
|
||||
Type tSpt = typeof(SecurityProtocolType);
|
||||
string[] vSpt = Enum.GetNames(tSpt);
|
||||
foreach(string strSpt in vSpt)
|
||||
{
|
||||
if(strSpt.Equals("Tls11", StrUtil.CaseIgnoreCmp))
|
||||
spt |= (SecurityProtocolType)Enum.Parse(tSpt, "Tls11", true);
|
||||
else if(strSpt.Equals("Tls12", StrUtil.CaseIgnoreCmp))
|
||||
spt |= (SecurityProtocolType)Enum.Parse(tSpt, "Tls12", true);
|
||||
}
|
||||
|
||||
ServicePointManager.SecurityProtocol = spt;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try
|
||||
{
|
||||
bool bCurCont = ServicePointManager.Expect100Continue;
|
||||
if(!m_obDefaultExpect100Continue.HasValue)
|
||||
{
|
||||
Debug.Assert(bCurCont); // Default should be true
|
||||
m_obDefaultExpect100Continue = bCurCont;
|
||||
}
|
||||
|
||||
bool bNewCont = m_obDefaultExpect100Continue.Value;
|
||||
bool? ob = p.GetBool(IocKnownProperties.Expect100Continue);
|
||||
if(ob.HasValue) bNewCont = ob.Value;
|
||||
|
||||
if(bNewCont != bCurCont)
|
||||
ServicePointManager.Expect100Continue = bNewCont;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
#endif
|
||||
}
|
||||
|
||||
private static IOWebClient CreateWebClient(IOConnectionInfo ioc)
|
||||
{
|
||||
PrepareWebAccess(ioc);
|
||||
|
||||
IOWebClient wc = new IOWebClient(ioc);
|
||||
ConfigureWebClient(wc);
|
||||
|
||||
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
|
||||
wc.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
|
||||
else if(NativeLib.IsUnix()) // Mono requires credentials
|
||||
wc.Credentials = new NetworkCredential("anonymous", string.Empty);
|
||||
|
||||
return wc;
|
||||
}
|
||||
|
||||
private static WebRequest CreateWebRequest(IOConnectionInfo ioc)
|
||||
{
|
||||
PrepareWebAccess(ioc);
|
||||
|
||||
WebRequest req = WebRequest.Create(ioc.Path);
|
||||
ConfigureWebRequest(req, ioc);
|
||||
|
||||
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
|
||||
req.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
|
||||
else if(NativeLib.IsUnix()) // Mono requires credentials
|
||||
req.Credentials = new NetworkCredential("anonymous", string.Empty);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
public static Stream OpenRead(IOConnectionInfo ioc)
|
||||
{
|
||||
RaiseIOAccessPreEvent(ioc, IOAccessType.Read);
|
||||
|
||||
if(StrUtil.IsDataUri(ioc.Path))
|
||||
{
|
||||
byte[] pbData = StrUtil.DataUriToData(ioc.Path);
|
||||
if(pbData != null) return new MemoryStream(pbData, false);
|
||||
}
|
||||
|
||||
if(ioc.IsLocalFile()) return OpenReadLocal(ioc);
|
||||
|
||||
return IocStream.WrapIfRequired(CreateWebClient(ioc).OpenRead(
|
||||
new Uri(ioc.Path)));
|
||||
}
|
||||
#else
|
||||
public static Stream OpenRead(IOConnectionInfo ioc)
|
||||
{
|
||||
RaiseIOAccessPreEvent(ioc, IOAccessType.Read);
|
||||
|
||||
return OpenReadLocal(ioc);
|
||||
}
|
||||
#endif
|
||||
|
||||
private static Stream OpenReadLocal(IOConnectionInfo ioc)
|
||||
{
|
||||
return ioc.StorageFile.OpenAsync(FileAccessMode.Read).GetAwaiter().GetResult().AsStream();
|
||||
}
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
public static Stream OpenWrite(IOConnectionInfo ioc)
|
||||
{
|
||||
if(ioc == null) { Debug.Assert(false); return null; }
|
||||
|
||||
RaiseIOAccessPreEvent(ioc, IOAccessType.Write);
|
||||
|
||||
if(ioc.IsLocalFile()) return OpenWriteLocal(ioc);
|
||||
|
||||
Uri uri = new Uri(ioc.Path);
|
||||
Stream s;
|
||||
|
||||
// Mono does not set HttpWebRequest.Method to POST for writes,
|
||||
// so one needs to set the method to PUT explicitly
|
||||
if(NativeLib.IsUnix() && IsHttpWebRequest(uri))
|
||||
s = CreateWebClient(ioc).OpenWrite(uri, WebRequestMethods.Http.Put);
|
||||
else s = CreateWebClient(ioc).OpenWrite(uri);
|
||||
|
||||
return IocStream.WrapIfRequired(s);
|
||||
}
|
||||
#else
|
||||
public static Stream OpenWrite(IOConnectionInfo ioc)
|
||||
{
|
||||
RaiseIOAccessPreEvent(ioc, IOAccessType.Write);
|
||||
|
||||
return OpenWriteLocal(ioc);
|
||||
}
|
||||
#endif
|
||||
|
||||
private static Stream OpenWriteLocal(IOConnectionInfo ioc)
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
return ioc.StorageFile.OpenAsync(FileAccessMode.ReadWrite).GetAwaiter().GetResult().AsStream();
|
||||
#else
|
||||
return new FileStream(ioc.Path, FileMode.Create, FileAccess.Write,
|
||||
FileShare.None);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool FileExists(IOConnectionInfo ioc)
|
||||
{
|
||||
return FileExists(ioc, false);
|
||||
}
|
||||
|
||||
public static bool FileExists(IOConnectionInfo ioc, bool bThrowErrors)
|
||||
{
|
||||
if(ioc == null) { Debug.Assert(false); return false; }
|
||||
|
||||
RaiseIOAccessPreEvent(ioc, IOAccessType.Exists);
|
||||
|
||||
#if ModernKeePassLib
|
||||
return ioc.StorageFile.IsAvailable;
|
||||
#else
|
||||
if(ioc.IsLocalFile()) return File.Exists(ioc.Path);
|
||||
|
||||
#if !KeePassLibSD
|
||||
if(ioc.Path.StartsWith("ftp://", StrUtil.CaseIgnoreCmp))
|
||||
{
|
||||
bool b = SendCommand(ioc, WebRequestMethods.Ftp.GetDateTimestamp);
|
||||
if(!b && bThrowErrors) throw new InvalidOperationException();
|
||||
return b;
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
Stream s = OpenRead(ioc);
|
||||
if(s == null) throw new FileNotFoundException();
|
||||
|
||||
try { s.ReadByte(); }
|
||||
catch(Exception) { }
|
||||
|
||||
// We didn't download the file completely; close may throw
|
||||
// an exception -- that's okay
|
||||
try { s.Close(); }
|
||||
catch(Exception) { }
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
if(bThrowErrors) throw;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void DeleteFile(IOConnectionInfo ioc)
|
||||
{
|
||||
RaiseIOAccessPreEvent(ioc, IOAccessType.Delete);
|
||||
|
||||
#if ModernKeePassLib
|
||||
if (!ioc.IsLocalFile()) return;
|
||||
ioc.StorageFile?.DeleteAsync().GetAwaiter().GetResult();
|
||||
#else
|
||||
if(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; }
|
||||
|
||||
#if !KeePassLibSD
|
||||
WebRequest req = CreateWebRequest(ioc);
|
||||
if(req != null)
|
||||
{
|
||||
if(IsHttpWebRequest(req)) req.Method = "DELETE";
|
||||
else if(IsFtpWebRequest(req))
|
||||
req.Method = WebRequestMethods.Ftp.DeleteFile;
|
||||
else if(IsFileWebRequest(req))
|
||||
{
|
||||
File.Delete(UrlUtil.FileUrlToPath(ioc.Path));
|
||||
return;
|
||||
}
|
||||
else req.Method = WrmDeleteFile;
|
||||
|
||||
DisposeResponse(req.GetResponse(), true);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rename/move a file. For local file system and WebDAV, the
|
||||
/// specified file is moved, i.e. the file destination can be
|
||||
/// in a different directory/path. In contrast, for FTP the
|
||||
/// file is renamed, i.e. its destination must be in the same
|
||||
/// directory/path.
|
||||
/// </summary>
|
||||
/// <param name="iocFrom">Source file path.</param>
|
||||
/// <param name="iocTo">Target file path.</param>
|
||||
public static void RenameFile(IOConnectionInfo iocFrom, IOConnectionInfo iocTo)
|
||||
{
|
||||
RaiseIOAccessPreEvent(iocFrom, iocTo, IOAccessType.Move);
|
||||
|
||||
#if ModernKeePassLib
|
||||
if (!iocFrom.IsLocalFile()) return;
|
||||
iocFrom.StorageFile?.RenameAsync(iocTo.Path).GetAwaiter().GetResult();
|
||||
#else
|
||||
if(iocFrom.IsLocalFile()) { File.Move(iocFrom.Path, iocTo.Path); return; }
|
||||
|
||||
#if !KeePassLibSD
|
||||
WebRequest req = CreateWebRequest(iocFrom);
|
||||
if(req != null)
|
||||
{
|
||||
if(IsHttpWebRequest(req))
|
||||
{
|
||||
#if KeePassUAP
|
||||
throw new NotSupportedException();
|
||||
#else
|
||||
req.Method = "MOVE";
|
||||
req.Headers.Set("Destination", iocTo.Path); // Full URL supported
|
||||
#endif
|
||||
}
|
||||
else if(IsFtpWebRequest(req))
|
||||
{
|
||||
#if KeePassUAP
|
||||
throw new NotSupportedException();
|
||||
#else
|
||||
req.Method = WebRequestMethods.Ftp.Rename;
|
||||
string strTo = UrlUtil.GetFileName(iocTo.Path);
|
||||
|
||||
// We're affected by .NET bug 621450:
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only
|
||||
// Prepending "./", "%2E/" or "Dummy/../" doesn't work.
|
||||
|
||||
((FtpWebRequest)req).RenameTo = strTo;
|
||||
#endif
|
||||
}
|
||||
else if(IsFileWebRequest(req))
|
||||
{
|
||||
File.Move(UrlUtil.FileUrlToPath(iocFrom.Path),
|
||||
UrlUtil.FileUrlToPath(iocTo.Path));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if KeePassUAP
|
||||
throw new NotSupportedException();
|
||||
#else
|
||||
req.Method = WrmMoveFile;
|
||||
req.Headers.Set(WrhMoveFileTo, iocTo.Path);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !KeePassUAP // Unreachable code
|
||||
DisposeResponse(req.GetResponse(), true);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// using(Stream sIn = IOConnection.OpenRead(iocFrom))
|
||||
// {
|
||||
// using(Stream sOut = IOConnection.OpenWrite(iocTo))
|
||||
// {
|
||||
// MemUtil.CopyStream(sIn, sOut);
|
||||
// sOut.Close();
|
||||
// }
|
||||
//
|
||||
// sIn.Close();
|
||||
// }
|
||||
// DeleteFile(iocFrom);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
|
||||
private static bool SendCommand(IOConnectionInfo ioc, string strMethod)
|
||||
{
|
||||
try
|
||||
{
|
||||
WebRequest req = CreateWebRequest(ioc);
|
||||
req.Method = strMethod;
|
||||
DisposeResponse(req.GetResponse(), true);
|
||||
}
|
||||
catch(Exception) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if !ModernKeePassLib
|
||||
internal static void DisposeResponse(WebResponse wr, bool bGetStream)
|
||||
{
|
||||
if(wr == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
if(bGetStream)
|
||||
{
|
||||
Stream s = wr.GetResponseStream();
|
||||
if(s != null) s.Close();
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try { wr.Close(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
public static byte[] ReadFile(IOConnectionInfo ioc)
|
||||
{
|
||||
Stream sIn = null;
|
||||
MemoryStream ms = null;
|
||||
try
|
||||
{
|
||||
sIn = IOConnection.OpenRead(ioc);
|
||||
if(sIn == null) return null;
|
||||
|
||||
ms = new MemoryStream();
|
||||
MemUtil.CopyStream(sIn, ms);
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
catch(Exception) { }
|
||||
finally
|
||||
{
|
||||
if(sIn != null) sIn.Dispose();
|
||||
if(ms != null) ms.Dispose();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void RaiseIOAccessPreEvent(IOConnectionInfo ioc, IOAccessType t)
|
||||
{
|
||||
RaiseIOAccessPreEvent(ioc, null, t);
|
||||
}
|
||||
|
||||
private static void RaiseIOAccessPreEvent(IOConnectionInfo ioc,
|
||||
IOConnectionInfo ioc2, IOAccessType t)
|
||||
{
|
||||
if(ioc == null) { Debug.Assert(false); return; }
|
||||
// ioc2 may be null
|
||||
|
||||
if(IOConnection.IOAccessPre != null)
|
||||
{
|
||||
IOConnectionInfo ioc2Lcl = ((ioc2 != null) ? ioc2.CloneDeep() : null);
|
||||
IOAccessEventArgs e = new IOAccessEventArgs(ioc.CloneDeep(), ioc2Lcl, t);
|
||||
IOConnection.IOAccessPre(null, e);
|
||||
}
|
||||
}
|
||||
#if !ModernKeePassLib
|
||||
private static bool IsHttpWebRequest(Uri uri)
|
||||
{
|
||||
if(uri == null) { Debug.Assert(false); return false; }
|
||||
|
||||
string sch = uri.Scheme;
|
||||
if(sch == null) { Debug.Assert(false); return false; }
|
||||
return (sch.Equals("http", StrUtil.CaseIgnoreCmp) || // Uri.UriSchemeHttp
|
||||
sch.Equals("https", StrUtil.CaseIgnoreCmp)); // Uri.UriSchemeHttps
|
||||
}
|
||||
|
||||
internal static bool IsHttpWebRequest(WebRequest wr)
|
||||
{
|
||||
if(wr == null) { Debug.Assert(false); return false; }
|
||||
|
||||
#if KeePassUAP
|
||||
return IsHttpWebRequest(wr.RequestUri);
|
||||
#else
|
||||
return (wr is HttpWebRequest);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static bool IsFtpWebRequest(WebRequest wr)
|
||||
{
|
||||
if(wr == null) { Debug.Assert(false); return false; }
|
||||
|
||||
#if KeePassUAP
|
||||
return string.Equals(wr.RequestUri.Scheme, "ftp", StrUtil.CaseIgnoreCmp);
|
||||
#else
|
||||
return (wr is FtpWebRequest);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static bool IsFileWebRequest(WebRequest wr)
|
||||
{
|
||||
if(wr == null) { Debug.Assert(false); return false; }
|
||||
|
||||
#if KeePassUAP
|
||||
return string.Equals(wr.RequestUri.Scheme, "file", StrUtil.CaseIgnoreCmp);
|
||||
#else
|
||||
return (wr is FileWebRequest);
|
||||
#endif
|
||||
}
|
||||
#endif // ModernKeePass
|
||||
}
|
||||
}
|
||||
407
ModernKeePassLib/Serialization/IOConnectionInfo.cs
Normal file
407
ModernKeePassLib/Serialization/IOConnectionInfo.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
#if ModernKeePassLib
|
||||
using Windows.Storage;
|
||||
//using PCLStorage;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Utility;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage.AccessCache;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public enum IOCredSaveMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not remember user name or password.
|
||||
/// </summary>
|
||||
NoSave = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Remember the user name only, not the password.
|
||||
/// </summary>
|
||||
UserNameOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Save both user name and password.
|
||||
/// </summary>
|
||||
SaveCred
|
||||
}
|
||||
|
||||
public enum IOCredProtMode
|
||||
{
|
||||
None = 0,
|
||||
Obf
|
||||
}
|
||||
|
||||
/* public enum IOFileFormatHint
|
||||
{
|
||||
None = 0,
|
||||
Deprecated
|
||||
} */
|
||||
|
||||
public sealed class IOConnectionInfo : IDeepCloneable<IOConnectionInfo>
|
||||
{
|
||||
// private IOFileFormatHint m_ioHint = IOFileFormatHint.None;
|
||||
|
||||
private string m_strUrl = string.Empty;
|
||||
public string Path
|
||||
{
|
||||
get { return m_strUrl; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_strUrl = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strUser = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string UserName
|
||||
{
|
||||
get { return m_strUser; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_strUser = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strPassword = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string Password
|
||||
{
|
||||
get { return m_strPassword; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_strPassword = value;
|
||||
}
|
||||
}
|
||||
|
||||
private IOCredProtMode m_ioCredProtMode = IOCredProtMode.None;
|
||||
public IOCredProtMode CredProtMode
|
||||
{
|
||||
get { return m_ioCredProtMode; }
|
||||
set { m_ioCredProtMode = value; }
|
||||
}
|
||||
|
||||
private IOCredSaveMode m_ioCredSaveMode = IOCredSaveMode.NoSave;
|
||||
public IOCredSaveMode CredSaveMode
|
||||
{
|
||||
get { return m_ioCredSaveMode; }
|
||||
set { m_ioCredSaveMode = value; }
|
||||
}
|
||||
|
||||
private bool m_bComplete = false;
|
||||
[XmlIgnore]
|
||||
public bool IsComplete // Credentials etc. fully specified
|
||||
{
|
||||
get { return m_bComplete; }
|
||||
set { m_bComplete = value; }
|
||||
}
|
||||
|
||||
/* public IOFileFormatHint FileFormatHint
|
||||
{
|
||||
get { return m_ioHint; }
|
||||
set { m_ioHint = value; }
|
||||
} */
|
||||
|
||||
private IocProperties m_props = new IocProperties();
|
||||
[XmlIgnore]
|
||||
public IocProperties Properties
|
||||
{
|
||||
get { return m_props; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_props = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For serialization only; use <c>Properties</c> in code.
|
||||
/// </summary>
|
||||
[DefaultValue("")]
|
||||
public string PropertiesEx
|
||||
{
|
||||
get { return m_props.Serialize(); }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
IocProperties p = IocProperties.Deserialize(value);
|
||||
Debug.Assert(p != null);
|
||||
m_props = (p ?? new IocProperties());
|
||||
}
|
||||
}
|
||||
|
||||
public IOConnectionInfo CloneDeep()
|
||||
{
|
||||
IOConnectionInfo ioc = (IOConnectionInfo)this.MemberwiseClone();
|
||||
ioc.m_props = m_props.CloneDeep();
|
||||
return ioc;
|
||||
}
|
||||
|
||||
#if DEBUG // For debugger display only
|
||||
public override string ToString()
|
||||
{
|
||||
return GetDisplayName();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Serialize the current connection info to a string. Credentials
|
||||
/// are serialized based on the <c>CredSaveMode</c> property.
|
||||
/// </summary>
|
||||
/// <param name="iocToCompile">Input object to be serialized.</param>
|
||||
/// <returns>Serialized object as string.</returns>
|
||||
public static string SerializeToString(IOConnectionInfo iocToCompile)
|
||||
{
|
||||
Debug.Assert(iocToCompile != null);
|
||||
if(iocToCompile == null) throw new ArgumentNullException("iocToCompile");
|
||||
|
||||
string strUrl = iocToCompile.Path;
|
||||
string strUser = TransformUnreadable(iocToCompile.UserName, true);
|
||||
string strPassword = TransformUnreadable(iocToCompile.Password, true);
|
||||
|
||||
string strAll = strUrl + strUser + strPassword + "CUN";
|
||||
char chSep = StrUtil.GetUnusedChar(strAll);
|
||||
if(chSep == char.MinValue) throw new FormatException();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(chSep);
|
||||
sb.Append(strUrl);
|
||||
sb.Append(chSep);
|
||||
|
||||
if(iocToCompile.CredSaveMode == IOCredSaveMode.SaveCred)
|
||||
{
|
||||
sb.Append('C');
|
||||
sb.Append(chSep);
|
||||
sb.Append(strUser);
|
||||
sb.Append(chSep);
|
||||
sb.Append(strPassword);
|
||||
}
|
||||
else if(iocToCompile.CredSaveMode == IOCredSaveMode.UserNameOnly)
|
||||
{
|
||||
sb.Append('U');
|
||||
sb.Append(chSep);
|
||||
sb.Append(strUser);
|
||||
sb.Append(chSep);
|
||||
}
|
||||
else // Don't remember credentials
|
||||
{
|
||||
sb.Append('N');
|
||||
sb.Append(chSep);
|
||||
sb.Append(chSep);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static IOConnectionInfo UnserializeFromString(string strToDecompile)
|
||||
{
|
||||
Debug.Assert(strToDecompile != null);
|
||||
if(strToDecompile == null) throw new ArgumentNullException("strToDecompile");
|
||||
if(strToDecompile.Length <= 1) throw new ArgumentException();
|
||||
|
||||
char chSep = strToDecompile[0];
|
||||
string[] vParts = strToDecompile.Substring(1, strToDecompile.Length -
|
||||
1).Split(new char[]{ chSep });
|
||||
if(vParts.Length < 4) throw new ArgumentException();
|
||||
|
||||
IOConnectionInfo s = new IOConnectionInfo();
|
||||
s.Path = vParts[0];
|
||||
|
||||
if(vParts[1] == "C")
|
||||
s.CredSaveMode = IOCredSaveMode.SaveCred;
|
||||
else if(vParts[1] == "U")
|
||||
s.CredSaveMode = IOCredSaveMode.UserNameOnly;
|
||||
else
|
||||
s.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
s.UserName = TransformUnreadable(vParts[2], false);
|
||||
s.Password = TransformUnreadable(vParts[3], false);
|
||||
return s;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Very simple string protection. Doesn't really encrypt the input
|
||||
/// string, only encodes it that it's not readable on the first glance.
|
||||
/// </summary>
|
||||
/// <param name="strToEncode">The string to encode/decode.</param>
|
||||
/// <param name="bEncode">If <c>true</c>, the string will be encoded,
|
||||
/// otherwise it'll be decoded.</param>
|
||||
/// <returns>Encoded/decoded string.</returns>
|
||||
private static string TransformUnreadable(string strToEncode, bool bEncode)
|
||||
{
|
||||
Debug.Assert(strToEncode != null);
|
||||
if(strToEncode == null) throw new ArgumentNullException("strToEncode");
|
||||
|
||||
if(bEncode)
|
||||
{
|
||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(strToEncode);
|
||||
|
||||
unchecked
|
||||
{
|
||||
for(int iPos = 0; iPos < pbUtf8.Length; ++iPos)
|
||||
pbUtf8[iPos] += (byte)(iPos * 11);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(pbUtf8);
|
||||
}
|
||||
else // Decode
|
||||
{
|
||||
byte[] pbBase = Convert.FromBase64String(strToEncode);
|
||||
|
||||
unchecked
|
||||
{
|
||||
for(int iPos = 0; iPos < pbBase.Length; ++iPos)
|
||||
pbBase[iPos] -= (byte)(iPos * 11);
|
||||
}
|
||||
|
||||
return StrUtil.Utf8.GetString(pbBase, 0, pbBase.Length);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public string GetDisplayName()
|
||||
{
|
||||
string str = m_strUrl;
|
||||
|
||||
if(m_strUser.Length > 0)
|
||||
str += (" (" + m_strUser + ")");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return (m_strUrl.Length == 0);
|
||||
}
|
||||
|
||||
#if ModernKeePassLib
|
||||
public static IOConnectionInfo FromFile(StorageFile file)
|
||||
{
|
||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||
|
||||
ioc.StorageFile = file;
|
||||
ioc.Path = file.Path;
|
||||
ioc.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
return ioc;
|
||||
}
|
||||
|
||||
public static IOConnectionInfo FromPath(string strPath)
|
||||
{
|
||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||
|
||||
ioc.Path = strPath;
|
||||
ioc.StorageFile = StorageApplicationPermissions.FutureAccessList.GetFileAsync(strPath).GetAwaiter().GetResult();
|
||||
ioc.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
return ioc;
|
||||
}
|
||||
#else
|
||||
public static IOConnectionInfo FromPath(string strPath)
|
||||
{
|
||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||
|
||||
ioc.Path = strPath;
|
||||
ioc.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
return ioc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public StorageFile StorageFile { get; set; }
|
||||
|
||||
public bool CanProbablyAccess()
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
if (IsLocalFile())
|
||||
{
|
||||
//return (FileSystem.Current.GetFileFromPathAsync(m_strUrl).Result != null);
|
||||
var file = StorageFile.GetFileFromPathAsync(m_strUrl).GetAwaiter().GetResult();
|
||||
return file != null;
|
||||
}
|
||||
#else
|
||||
if(IsLocalFile()) return File.Exists(m_strUrl);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsLocalFile()
|
||||
{
|
||||
// Not just ":/", see e.g. AppConfigEx.ChangePathRelAbs
|
||||
return (m_strUrl.IndexOf("://") < 0);
|
||||
}
|
||||
|
||||
public void ClearCredentials(bool bDependingOnRememberMode)
|
||||
{
|
||||
if((bDependingOnRememberMode == false) ||
|
||||
(m_ioCredSaveMode == IOCredSaveMode.NoSave))
|
||||
{
|
||||
m_strUser = string.Empty;
|
||||
}
|
||||
|
||||
if((bDependingOnRememberMode == false) ||
|
||||
(m_ioCredSaveMode == IOCredSaveMode.NoSave) ||
|
||||
(m_ioCredSaveMode == IOCredSaveMode.UserNameOnly))
|
||||
{
|
||||
m_strPassword = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void Obfuscate(bool bObf)
|
||||
{
|
||||
if(bObf && (m_ioCredProtMode == IOCredProtMode.None))
|
||||
{
|
||||
m_strPassword = StrUtil.Obfuscate(m_strPassword);
|
||||
m_ioCredProtMode = IOCredProtMode.Obf;
|
||||
}
|
||||
else if(!bObf && (m_ioCredProtMode == IOCredProtMode.Obf))
|
||||
{
|
||||
m_strPassword = StrUtil.Deobfuscate(m_strPassword);
|
||||
m_ioCredProtMode = IOCredProtMode.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
192
ModernKeePassLib/Serialization/IocProperties.cs
Normal file
192
ModernKeePassLib/Serialization/IocProperties.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
using StrDict = System.Collections.Generic.Dictionary<string, string>;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public interface IHasIocProperties
|
||||
{
|
||||
IocProperties IOConnectionProperties { get; set; }
|
||||
}
|
||||
|
||||
public sealed class IocProperties : IDeepCloneable<IocProperties>
|
||||
{
|
||||
private StrDict m_dict = new StrDict();
|
||||
|
||||
public IocProperties()
|
||||
{
|
||||
}
|
||||
|
||||
public IocProperties CloneDeep()
|
||||
{
|
||||
IocProperties p = new IocProperties();
|
||||
p.m_dict = new StrDict(m_dict);
|
||||
return p;
|
||||
}
|
||||
|
||||
public string Get(string strKey)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strKey)) return null;
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in m_dict)
|
||||
{
|
||||
if(kvp.Key.Equals(strKey, StrUtil.CaseIgnoreCmp))
|
||||
return kvp.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Set(string strKey, string strValue)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strKey)) { Debug.Assert(false); return; }
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in m_dict)
|
||||
{
|
||||
if(kvp.Key.Equals(strKey, StrUtil.CaseIgnoreCmp))
|
||||
{
|
||||
if(string.IsNullOrEmpty(strValue)) m_dict.Remove(kvp.Key);
|
||||
else m_dict[kvp.Key] = strValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(strValue)) m_dict[strKey] = strValue;
|
||||
}
|
||||
|
||||
public bool? GetBool(string strKey)
|
||||
{
|
||||
string str = Get(strKey);
|
||||
if(string.IsNullOrEmpty(str)) return null;
|
||||
|
||||
return StrUtil.StringToBool(str);
|
||||
}
|
||||
|
||||
public void SetBool(string strKey, bool? ob)
|
||||
{
|
||||
if(ob.HasValue) Set(strKey, (ob.Value ? "1" : "0"));
|
||||
else Set(strKey, null);
|
||||
}
|
||||
|
||||
public long? GetLong(string strKey)
|
||||
{
|
||||
string str = Get(strKey);
|
||||
if(string.IsNullOrEmpty(str)) return null;
|
||||
|
||||
long l;
|
||||
if(StrUtil.TryParseLongInvariant(str, out l)) return l;
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetLong(string strKey, long? ol)
|
||||
{
|
||||
if(ol.HasValue)
|
||||
Set(strKey, ol.Value.ToString(NumberFormatInfo.InvariantInfo));
|
||||
else Set(strKey, null);
|
||||
}
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
if(m_dict.Count == 0) return string.Empty;
|
||||
|
||||
StringBuilder sbAll = new StringBuilder();
|
||||
foreach(KeyValuePair<string, string> kvp in m_dict)
|
||||
{
|
||||
sbAll.Append(kvp.Key);
|
||||
sbAll.Append(kvp.Value);
|
||||
}
|
||||
|
||||
string strAll = sbAll.ToString();
|
||||
char chSepOuter = ';';
|
||||
if(strAll.IndexOf(chSepOuter) >= 0)
|
||||
chSepOuter = StrUtil.GetUnusedChar(strAll);
|
||||
|
||||
strAll += chSepOuter;
|
||||
char chSepInner = '=';
|
||||
if(strAll.IndexOf(chSepInner) >= 0)
|
||||
chSepInner = StrUtil.GetUnusedChar(strAll);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(chSepOuter);
|
||||
sb.Append(chSepInner);
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in m_dict)
|
||||
{
|
||||
sb.Append(chSepOuter);
|
||||
sb.Append(kvp.Key);
|
||||
sb.Append(chSepInner);
|
||||
sb.Append(kvp.Value);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static IocProperties Deserialize(string strSerialized)
|
||||
{
|
||||
IocProperties p = new IocProperties();
|
||||
if(string.IsNullOrEmpty(strSerialized)) return p; // No assert
|
||||
|
||||
char chSepOuter = strSerialized[0];
|
||||
string[] v = strSerialized.Substring(1).Split(new char[] { chSepOuter });
|
||||
if((v == null) || (v.Length < 2)) { Debug.Assert(false); return p; }
|
||||
|
||||
string strMeta = v[0];
|
||||
if(string.IsNullOrEmpty(strMeta)) { Debug.Assert(false); return p; }
|
||||
|
||||
char chSepInner = strMeta[0];
|
||||
char[] vSepInner = new char[] { chSepInner };
|
||||
|
||||
for(int i = 1; i < v.Length; ++i)
|
||||
{
|
||||
string strProp = v[i];
|
||||
if(string.IsNullOrEmpty(strProp)) { Debug.Assert(false); continue; }
|
||||
|
||||
string[] vProp = strProp.Split(vSepInner);
|
||||
if((vProp == null) || (vProp.Length < 2)) { Debug.Assert(false); continue; }
|
||||
Debug.Assert(vProp.Length == 2);
|
||||
|
||||
p.Set(vProp[0], vProp[1]);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
public void CopyTo(IocProperties p)
|
||||
{
|
||||
if(p == null) { Debug.Assert(false); return; }
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in m_dict)
|
||||
{
|
||||
p.m_dict[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
ModernKeePassLib/Serialization/IocPropertyInfo.cs
Normal file
99
ModernKeePassLib/Serialization/IocPropertyInfo.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class IocPropertyInfo
|
||||
{
|
||||
private readonly string m_strName;
|
||||
public string Name
|
||||
{
|
||||
get { return m_strName; }
|
||||
}
|
||||
|
||||
private readonly Type m_t;
|
||||
public Type Type
|
||||
{
|
||||
get { return m_t; }
|
||||
}
|
||||
|
||||
private string m_strDisplayName;
|
||||
public string DisplayName
|
||||
{
|
||||
get { return m_strDisplayName; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strDisplayName = value;
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> m_lProtocols = new List<string>();
|
||||
public IEnumerable<string> Protocols
|
||||
{
|
||||
get { return m_lProtocols; }
|
||||
}
|
||||
|
||||
public IocPropertyInfo(string strName, Type t, string strDisplayName,
|
||||
string[] vProtocols)
|
||||
{
|
||||
if(strName == null) throw new ArgumentNullException("strName");
|
||||
if(t == null) throw new ArgumentNullException("t");
|
||||
if(strDisplayName == null) throw new ArgumentNullException("strDisplayName");
|
||||
|
||||
m_strName = strName;
|
||||
m_t = t;
|
||||
m_strDisplayName = strDisplayName;
|
||||
|
||||
AddProtocols(vProtocols);
|
||||
}
|
||||
|
||||
public void AddProtocols(string[] v)
|
||||
{
|
||||
if(v == null) { Debug.Assert(false); return; }
|
||||
|
||||
foreach(string strProtocol in v)
|
||||
{
|
||||
if(strProtocol == null) continue;
|
||||
|
||||
string str = strProtocol.Trim();
|
||||
if(str.Length == 0) continue;
|
||||
|
||||
bool bFound = false;
|
||||
foreach(string strEx in m_lProtocols)
|
||||
{
|
||||
if(strEx.Equals(str, StrUtil.CaseIgnoreCmp))
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bFound) m_lProtocols.Add(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
ModernKeePassLib/Serialization/IocPropertyInfoPool.cs
Normal file
123
ModernKeePassLib/Serialization/IocPropertyInfoPool.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public static class IocKnownProtocols
|
||||
{
|
||||
public static readonly string Http = "HTTP";
|
||||
public static readonly string Https = "HTTPS";
|
||||
public static readonly string WebDav = "WebDAV";
|
||||
public static readonly string Ftp = "FTP";
|
||||
}
|
||||
|
||||
public static class IocKnownProperties
|
||||
{
|
||||
public static readonly string Timeout = "Timeout";
|
||||
public static readonly string PreAuth = "PreAuth";
|
||||
|
||||
public static readonly string UserAgent = "UserAgent";
|
||||
public static readonly string Expect100Continue = "Expect100Continue";
|
||||
|
||||
public static readonly string Passive = "Passive";
|
||||
}
|
||||
|
||||
public static class IocPropertyInfoPool
|
||||
{
|
||||
private static List<IocPropertyInfo> m_l = null;
|
||||
public static IEnumerable<IocPropertyInfo> PropertyInfos
|
||||
{
|
||||
get { EnsureInitialized(); return m_l; }
|
||||
}
|
||||
|
||||
private static void EnsureInitialized()
|
||||
{
|
||||
if(m_l != null) return;
|
||||
|
||||
string strGen = KLRes.General;
|
||||
string strHttp = IocKnownProtocols.Http;
|
||||
string strHttps = IocKnownProtocols.Https;
|
||||
string strWebDav = IocKnownProtocols.WebDav;
|
||||
string strFtp = IocKnownProtocols.Ftp;
|
||||
|
||||
string[] vGen = new string[] { strGen };
|
||||
string[] vHttp = new string[] { strHttp, strHttps, strWebDav };
|
||||
string[] vFtp = new string[] { strFtp };
|
||||
|
||||
List<IocPropertyInfo> l = new List<IocPropertyInfo>();
|
||||
|
||||
l.Add(new IocPropertyInfo(IocKnownProperties.Timeout,
|
||||
typeof(long), KLRes.Timeout + " [ms]", vGen));
|
||||
l.Add(new IocPropertyInfo(IocKnownProperties.PreAuth,
|
||||
typeof(bool), KLRes.PreAuth, vGen));
|
||||
|
||||
l.Add(new IocPropertyInfo(IocKnownProperties.UserAgent,
|
||||
typeof(string), KLRes.UserAgent, vHttp));
|
||||
l.Add(new IocPropertyInfo(IocKnownProperties.Expect100Continue,
|
||||
typeof(bool), KLRes.Expect100Continue, vHttp));
|
||||
|
||||
l.Add(new IocPropertyInfo(IocKnownProperties.Passive,
|
||||
typeof(bool), KLRes.Passive, vFtp));
|
||||
|
||||
// l.Add(new IocPropertyInfo("Test", typeof(bool),
|
||||
// "Long long long long long long long long long long long long long long long long long long long long",
|
||||
// new string[] { "Proto 1/9", "Proto 2/9", "Proto 3/9", "Proto 4/9", "Proto 5/9",
|
||||
// "Proto 6/9", "Proto 7/9", "Proto 8/9", "Proto 9/9" }));
|
||||
|
||||
m_l = l;
|
||||
}
|
||||
|
||||
public static IocPropertyInfo Get(string strName)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
|
||||
|
||||
EnsureInitialized();
|
||||
foreach(IocPropertyInfo pi in m_l)
|
||||
{
|
||||
if(pi.Name.Equals(strName, StrUtil.CaseIgnoreCmp))
|
||||
return pi;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool Add(IocPropertyInfo pi)
|
||||
{
|
||||
if(pi == null) { Debug.Assert(false); return false; }
|
||||
|
||||
// Name must be non-empty
|
||||
string strName = pi.Name;
|
||||
if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return false; }
|
||||
|
||||
IocPropertyInfo piEx = Get(strName); // Ensures initialized
|
||||
if(piEx != null) { Debug.Assert(false); return false; } // Exists already
|
||||
|
||||
m_l.Add(pi);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
1085
ModernKeePassLib/Serialization/KdbxFile.Read.Streamed.cs
Normal file
1085
ModernKeePassLib/Serialization/KdbxFile.Read.Streamed.cs
Normal file
File diff suppressed because it is too large
Load Diff
602
ModernKeePassLib/Serialization/KdbxFile.Read.cs
Normal file
602
ModernKeePassLib/Serialization/KdbxFile.Read.cs
Normal file
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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
|
||||
*/
|
||||
|
||||
// #define KDBX_BENCHMARK
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
#if !ModernKeePassLib && !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#else
|
||||
using Windows.Storage;
|
||||
#endif
|
||||
|
||||
#if !KeePassLibSD
|
||||
using System.IO.Compression;
|
||||
#else
|
||||
using KeePassLibSD;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Cryptography.KeyDerivation;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Keys;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialization to KeePass KDBX files.
|
||||
/// </summary>
|
||||
public sealed partial class KdbxFile
|
||||
{
|
||||
#if ModernKeePassLib
|
||||
public void Load(StorageFile file, KdbxFormat fmt, IStatusLogger slLogger)
|
||||
{
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromFile(file);
|
||||
Load(IOConnection.OpenRead(ioc), fmt, slLogger);
|
||||
}
|
||||
|
||||
#else
|
||||
/// <summary>
|
||||
/// Load a KDBX file.
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">File to load.</param>
|
||||
/// <param name="fmt">Format.</param>
|
||||
/// <param name="slLogger">Status logger (optional).</param>
|
||||
public void Load(string strFilePath, KdbxFormat fmt, IStatusLogger slLogger)
|
||||
{
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
|
||||
Load(IOConnection.OpenRead(ioc), fmt, slLogger);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 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="fmt">Format.</param>
|
||||
/// <param name="slLogger">Status logger (optional).</param>
|
||||
public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
|
||||
{
|
||||
Debug.Assert(sSource != null);
|
||||
if(sSource == null) throw new ArgumentNullException("sSource");
|
||||
|
||||
if(m_bUsedOnce)
|
||||
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
|
||||
m_bUsedOnce = true;
|
||||
|
||||
#if KDBX_BENCHMARK
|
||||
Stopwatch swTime = Stopwatch.StartNew();
|
||||
#endif
|
||||
|
||||
m_format = fmt;
|
||||
m_slLogger = slLogger;
|
||||
|
||||
m_pbsBinaries.Clear();
|
||||
|
||||
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
|
||||
{
|
||||
Stream sXml;
|
||||
if(fmt == KdbxFormat.Default)
|
||||
{
|
||||
BinaryReaderEx br = new BinaryReaderEx(sHashing,
|
||||
encNoBom, KLRes.FileCorrupted);
|
||||
byte[] pbHeader = LoadHeader(br);
|
||||
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
|
||||
|
||||
int cbEncKey, cbEncIV;
|
||||
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
|
||||
|
||||
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
|
||||
|
||||
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
|
||||
KLRes.FileIncomplete;
|
||||
|
||||
Stream sPlain;
|
||||
if(m_uFileVersion < FileVersion32_4)
|
||||
{
|
||||
Stream sDecrypted = EncryptStream(sHashing, iCipher,
|
||||
pbCipherKey, cbEncIV, false);
|
||||
if((sDecrypted == null) || (sDecrypted == sHashing))
|
||||
throw new SecurityException(KLRes.CryptoStreamFailed);
|
||||
lStreams.Add(sDecrypted);
|
||||
|
||||
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
|
||||
encNoBom, strIncomplete);
|
||||
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
|
||||
|
||||
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
|
||||
throw new EndOfStreamException(strIncomplete);
|
||||
if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
|
||||
throw new InvalidCompositeKeyException();
|
||||
|
||||
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
|
||||
}
|
||||
else // KDBX >= 4
|
||||
{
|
||||
byte[] pbStoredHash = MemUtil.Read(sHashing, 32);
|
||||
if((pbStoredHash == null) || (pbStoredHash.Length != 32))
|
||||
throw new EndOfStreamException(strIncomplete);
|
||||
if(!MemUtil.ArraysEqual(m_pbHashOfHeader, pbStoredHash))
|
||||
throw new InvalidDataException(KLRes.FileHeaderCorrupted);
|
||||
|
||||
byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
|
||||
byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
|
||||
if((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
|
||||
throw new EndOfStreamException(strIncomplete);
|
||||
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)
|
||||
{
|
||||
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
|
||||
lStreams.Add(sXml);
|
||||
}
|
||||
else sXml = sPlain;
|
||||
|
||||
if(m_uFileVersion >= FileVersion32_4)
|
||||
LoadInnerHeader(sXml); // Binary header before XML
|
||||
}
|
||||
else if(fmt == KdbxFormat.PlainXml)
|
||||
sXml = sHashing;
|
||||
else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
|
||||
|
||||
if(fmt == KdbxFormat.Default)
|
||||
{
|
||||
if(m_pbInnerRandomStreamKey == null)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new SecurityException("Invalid inner random stream key!");
|
||||
}
|
||||
|
||||
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
|
||||
m_pbInnerRandomStreamKey);
|
||||
}
|
||||
|
||||
#if KeePassDebug_WriteXml
|
||||
// FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
|
||||
// FileAccess.Write, FileShare.None);
|
||||
// try
|
||||
// {
|
||||
// while(true)
|
||||
// {
|
||||
// int b = sXml.ReadByte();
|
||||
// if(b == -1) break;
|
||||
// fsOut.WriteByte((byte)b);
|
||||
// }
|
||||
// }
|
||||
// catch(Exception) { }
|
||||
// fsOut.Close();
|
||||
#endif
|
||||
|
||||
ReadXmlStreamed(sXml, sHashing);
|
||||
// ReadXmlDom(sXml);
|
||||
}
|
||||
#if !ModernKeePassLib
|
||||
catch(CryptographicException) // Thrown on invalid padding
|
||||
{
|
||||
throw new CryptographicException(KLRes.FileCorrupted);
|
||||
}
|
||||
#endif
|
||||
finally
|
||||
{
|
||||
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
|
||||
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
|
||||
|
||||
CommonCleanUpRead(lStreams, sHashing);
|
||||
}
|
||||
|
||||
#if KDBX_BENCHMARK
|
||||
swTime.Stop();
|
||||
MessageService.ShowInfo("Loading KDBX took " +
|
||||
swTime.ElapsedMilliseconds.ToString() + " ms.");
|
||||
#endif
|
||||
}
|
||||
|
||||
private void CommonCleanUpRead(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);
|
||||
|
||||
CleanUpInnerRandomStream();
|
||||
|
||||
// Reset memory protection settings (to always use reasonable
|
||||
// defaults)
|
||||
m_pwDatabase.MemoryProtection = new MemoryProtectionConfig();
|
||||
|
||||
// Remove old backups (this call is required here in order to apply
|
||||
// the default history maintenance settings for people upgrading from
|
||||
// KeePass <= 2.14 to >= 2.15; also it ensures history integrity in
|
||||
// case a different application has created the KDBX file and ignored
|
||||
// the history maintenance settings)
|
||||
m_pwDatabase.MaintainBackups(); // Don't mark database as modified
|
||||
|
||||
// Expand the root group, such that in case the user accidently
|
||||
// collapses the root group he can simply reopen the database
|
||||
PwGroup pgRoot = m_pwDatabase.RootGroup;
|
||||
if(pgRoot != null) pgRoot.IsExpanded = true;
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
m_pbHashOfHeader = null;
|
||||
}
|
||||
|
||||
private byte[] LoadHeader(BinaryReaderEx br)
|
||||
{
|
||||
string strPrevExcpText = br.ReadExceptionText;
|
||||
br.ReadExceptionText = KLRes.FileHeaderCorrupted + " " +
|
||||
KLRes.FileIncompleteExpc;
|
||||
|
||||
MemoryStream msHeader = new MemoryStream();
|
||||
Debug.Assert(br.CopyDataTo == null);
|
||||
br.CopyDataTo = msHeader;
|
||||
|
||||
byte[] pbSig1 = br.ReadBytes(4);
|
||||
uint uSig1 = MemUtil.BytesToUInt32(pbSig1);
|
||||
byte[] pbSig2 = br.ReadBytes(4);
|
||||
uint uSig2 = MemUtil.BytesToUInt32(pbSig2);
|
||||
|
||||
if((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2))
|
||||
throw new OldFormatException(PwDefs.ShortProductName + @" 1.x",
|
||||
OldFormatException.OldFormatType.KeePass1x);
|
||||
|
||||
if((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) { }
|
||||
else if((uSig1 == FileSignaturePreRelease1) && (uSig2 ==
|
||||
FileSignaturePreRelease2)) { }
|
||||
else throw new FormatException(KLRes.FileSigInvalid);
|
||||
|
||||
byte[] pb = br.ReadBytes(4);
|
||||
uint uVersion = MemUtil.BytesToUInt32(pb);
|
||||
if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
|
||||
throw new FormatException(KLRes.FileVersionUnsupported +
|
||||
MessageService.NewParagraph + KLRes.FileNewVerReq);
|
||||
m_uFileVersion = uVersion;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(!ReadHeaderField(br)) break;
|
||||
}
|
||||
|
||||
br.CopyDataTo = null;
|
||||
byte[] pbHeader = msHeader.ToArray();
|
||||
msHeader.Dispose();
|
||||
|
||||
br.ReadExceptionText = strPrevExcpText;
|
||||
return pbHeader;
|
||||
}
|
||||
|
||||
private bool ReadHeaderField(BinaryReaderEx brSource)
|
||||
{
|
||||
Debug.Assert(brSource != null);
|
||||
if(brSource == null) throw new ArgumentNullException("brSource");
|
||||
|
||||
byte btFieldID = brSource.ReadByte();
|
||||
|
||||
int cbSize;
|
||||
Debug.Assert(m_uFileVersion > 0);
|
||||
if(m_uFileVersion < FileVersion32_4)
|
||||
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) pbData = brSource.ReadBytes(cbSize);
|
||||
|
||||
bool bResult = true;
|
||||
KdbxHeaderFieldID kdbID = (KdbxHeaderFieldID)btFieldID;
|
||||
switch(kdbID)
|
||||
{
|
||||
case KdbxHeaderFieldID.EndOfHeader:
|
||||
bResult = false; // Returning false indicates end of header
|
||||
break;
|
||||
|
||||
case KdbxHeaderFieldID.CipherID:
|
||||
SetCipher(pbData);
|
||||
break;
|
||||
|
||||
case KdbxHeaderFieldID.CompressionFlags:
|
||||
SetCompressionFlags(pbData);
|
||||
break;
|
||||
|
||||
case KdbxHeaderFieldID.MasterSeed:
|
||||
m_pbMasterSeed = pbData;
|
||||
CryptoRandom.Instance.AddEntropy(pbData);
|
||||
break;
|
||||
|
||||
// Obsolete; for backward compatibility only
|
||||
case KdbxHeaderFieldID.TransformSeed:
|
||||
Debug.Assert(m_uFileVersion < FileVersion32_4);
|
||||
|
||||
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:
|
||||
Debug.Assert(m_uFileVersion < FileVersion32_4);
|
||||
|
||||
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:
|
||||
m_pbEncryptionIV = pbData;
|
||||
break;
|
||||
|
||||
case KdbxHeaderFieldID.InnerRandomStreamKey:
|
||||
Debug.Assert(m_uFileVersion < FileVersion32_4);
|
||||
Debug.Assert(m_pbInnerRandomStreamKey == null);
|
||||
m_pbInnerRandomStreamKey = pbData;
|
||||
CryptoRandom.Instance.AddEntropy(pbData);
|
||||
break;
|
||||
|
||||
case KdbxHeaderFieldID.StreamStartBytes:
|
||||
Debug.Assert(m_uFileVersion < FileVersion32_4);
|
||||
m_pbStreamStartBytes = pbData;
|
||||
break;
|
||||
|
||||
case KdbxHeaderFieldID.InnerRandomStreamID:
|
||||
Debug.Assert(m_uFileVersion < FileVersion32_4);
|
||||
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)
|
||||
m_slLogger.SetText(KLRes.UnknownHeaderId + @": " +
|
||||
kdbID.ToString() + "!", LogStatusType.Warning);
|
||||
break;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
private void LoadInnerHeader(Stream s)
|
||||
{
|
||||
BinaryReaderEx br = new BinaryReaderEx(s, StrUtil.Utf8,
|
||||
KLRes.FileCorrupted + " " + KLRes.FileIncompleteExpc);
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(!ReadInnerHeaderField(br)) break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReadInnerHeaderField(BinaryReaderEx br)
|
||||
{
|
||||
Debug.Assert(br != null);
|
||||
if(br == null) throw new ArgumentNullException("br");
|
||||
|
||||
byte btFieldID = br.ReadByte();
|
||||
|
||||
int cbSize = MemUtil.BytesToInt32(br.ReadBytes(4));
|
||||
if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
|
||||
|
||||
byte[] pbData = MemUtil.EmptyByteArray;
|
||||
if(cbSize > 0) pbData = br.ReadBytes(cbSize);
|
||||
|
||||
bool bResult = true;
|
||||
KdbxInnerHeaderFieldID kdbID = (KdbxInnerHeaderFieldID)btFieldID;
|
||||
switch(kdbID)
|
||||
{
|
||||
case KdbxInnerHeaderFieldID.EndOfHeader:
|
||||
bResult = false; // Returning false indicates end of header
|
||||
break;
|
||||
|
||||
case KdbxInnerHeaderFieldID.InnerRandomStreamID:
|
||||
SetInnerRandomStreamID(pbData);
|
||||
break;
|
||||
|
||||
case KdbxInnerHeaderFieldID.InnerRandomStreamKey:
|
||||
Debug.Assert(m_pbInnerRandomStreamKey == null);
|
||||
m_pbInnerRandomStreamKey = pbData;
|
||||
CryptoRandom.Instance.AddEntropy(pbData);
|
||||
break;
|
||||
|
||||
case KdbxInnerHeaderFieldID.Binary:
|
||||
if(pbData.Length < 1) throw new FormatException();
|
||||
KdbxBinaryFlags f = (KdbxBinaryFlags)pbData[0];
|
||||
bool bProt = ((f & KdbxBinaryFlags.Protected) != KdbxBinaryFlags.None);
|
||||
|
||||
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
|
||||
1, pbData.Length - 1);
|
||||
m_pbsBinaries.Add(pb);
|
||||
|
||||
if(bProt) MemUtil.ZeroByteArray(pbData);
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
private void SetCipher(byte[] pbID)
|
||||
{
|
||||
if((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
|
||||
throw new FormatException(KLRes.FileUnknownCipher);
|
||||
|
||||
m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
|
||||
}
|
||||
|
||||
private void SetCompressionFlags(byte[] pbFlags)
|
||||
{
|
||||
int nID = (int)MemUtil.BytesToUInt32(pbFlags);
|
||||
if((nID < 0) || (nID >= (int)PwCompressionAlgorithm.Count))
|
||||
throw new FormatException(KLRes.FileUnknownCompression);
|
||||
|
||||
m_pwDatabase.Compression = (PwCompressionAlgorithm)nID;
|
||||
}
|
||||
|
||||
private void SetInnerRandomStreamID(byte[] pbID)
|
||||
{
|
||||
uint uID = MemUtil.BytesToUInt32(pbID);
|
||||
if(uID >= (uint)CrsAlgorithm.Count)
|
||||
throw new FormatException(KLRes.FileUnknownCipher);
|
||||
|
||||
m_craInnerRandomStream = (CrsAlgorithm)uID;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static List<PwEntry> ReadEntries(Stream msData)
|
||||
{
|
||||
return ReadEntries(msData, null, false);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static List<PwEntry> ReadEntries(PwDatabase pdContext, Stream msData)
|
||||
{
|
||||
return ReadEntries(msData, pdContext, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read entries from a stream.
|
||||
/// </summary>
|
||||
/// <param name="msData">Input stream to read the entries from.</param>
|
||||
/// <param name="pdContext">Context database (e.g. for storing icons).</param>
|
||||
/// <param name="bCopyIcons">If <c>true</c>, custom icons required by
|
||||
/// the loaded entries are copied to the context database.</param>
|
||||
/// <returns>Loaded entries.</returns>
|
||||
public static List<PwEntry> ReadEntries(Stream msData, PwDatabase pdContext,
|
||||
bool bCopyIcons)
|
||||
{
|
||||
List<PwEntry> lEntries = new List<PwEntry>();
|
||||
|
||||
if(msData == null) { Debug.Assert(false); return lEntries; }
|
||||
// pdContext may be null
|
||||
|
||||
/* KdbxFile f = new KdbxFile(pwDatabase);
|
||||
f.m_format = KdbxFormat.PlainXml;
|
||||
|
||||
XmlDocument doc = XmlUtilEx.CreateXmlDocument();
|
||||
doc.Load(msData);
|
||||
|
||||
XmlElement el = doc.DocumentElement;
|
||||
if(el.Name != ElemRoot) throw new FormatException();
|
||||
|
||||
List<PwEntry> vEntries = new List<PwEntry>();
|
||||
|
||||
foreach(XmlNode xmlChild in el.ChildNodes)
|
||||
{
|
||||
if(xmlChild.Name == ElemEntry)
|
||||
{
|
||||
PwEntry pe = f.ReadEntry(xmlChild);
|
||||
pe.Uuid = new PwUuid(true);
|
||||
|
||||
foreach(PwEntry peHistory in pe.History)
|
||||
peHistory.Uuid = pe.Uuid;
|
||||
|
||||
vEntries.Add(pe);
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
return vEntries; */
|
||||
|
||||
PwDatabase pd = new PwDatabase();
|
||||
pd.New(new IOConnectionInfo(), new CompositeKey());
|
||||
|
||||
KdbxFile f = new KdbxFile(pd);
|
||||
f.Load(msData, KdbxFormat.PlainXml, null);
|
||||
|
||||
foreach(PwEntry pe in pd.RootGroup.Entries)
|
||||
{
|
||||
pe.SetUuid(new PwUuid(true), true);
|
||||
lEntries.Add(pe);
|
||||
|
||||
if(bCopyIcons && (pdContext != null))
|
||||
{
|
||||
PwUuid pu = pe.CustomIconUuid;
|
||||
if(!pu.Equals(PwUuid.Zero))
|
||||
{
|
||||
int iSrc = pd.GetCustomIconIndex(pu);
|
||||
int iDst = pdContext.GetCustomIconIndex(pu);
|
||||
|
||||
if(iSrc < 0) { Debug.Assert(false); }
|
||||
else if(iDst < 0)
|
||||
{
|
||||
pdContext.CustomIcons.Add(pd.CustomIcons[iSrc]);
|
||||
|
||||
pdContext.Modified = true;
|
||||
pdContext.UINeedsIconUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lEntries;
|
||||
}
|
||||
}
|
||||
}
|
||||
1053
ModernKeePassLib/Serialization/KdbxFile.Write.cs
Normal file
1053
ModernKeePassLib/Serialization/KdbxFile.Write.cs
Normal file
File diff suppressed because it is too large
Load Diff
544
ModernKeePassLib/Serialization/KdbxFile.cs
Normal file
544
ModernKeePassLib/Serialization/KdbxFile.cs
Normal file
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
#if ModernKeePassLib
|
||||
using Windows.Storage;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Cryptography.KeyDerivation;
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// The <c>KdbxFile</c> class supports saving the data to various
|
||||
/// formats.
|
||||
/// </summary>
|
||||
public enum KdbxFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// The default, encrypted file format.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Use this flag when exporting data to a plain-text XML file.
|
||||
/// </summary>
|
||||
PlainXml
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialization to KeePass KDBX files.
|
||||
/// </summary>
|
||||
public sealed partial class KdbxFile
|
||||
{
|
||||
/// <summary>
|
||||
/// File identifier, first 32-bit value.
|
||||
/// </summary>
|
||||
internal const uint FileSignature1 = 0x9AA2D903;
|
||||
|
||||
/// <summary>
|
||||
/// File identifier, second 32-bit value.
|
||||
/// </summary>
|
||||
internal const uint FileSignature2 = 0xB54BFB67;
|
||||
|
||||
/// <summary>
|
||||
/// File version of files saved by the current <c>KdbxFile</c> class.
|
||||
/// KeePass 2.07 has version 1.01, 2.08 has 1.02, 2.09 has 2.00,
|
||||
/// 2.10 has 2.02, 2.11 has 2.04, 2.15 has 3.00, 2.20 has 3.01.
|
||||
/// 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 = 0x00040000;
|
||||
|
||||
internal const uint FileVersion32_4 = 0x00040000; // First of 4.x series
|
||||
internal const uint FileVersion32_3 = 0x00030001; // Old format 3.1
|
||||
|
||||
private const uint FileVersionCriticalMask = 0xFFFF0000;
|
||||
|
||||
// KeePass 1.x signature
|
||||
internal const uint FileSignatureOld1 = 0x9AA2D903;
|
||||
internal const uint FileSignatureOld2 = 0xB54BFB65;
|
||||
// KeePass 2.x pre-release (alpha and beta) signature
|
||||
internal const uint FileSignaturePreRelease1 = 0x9AA2D903;
|
||||
internal const uint FileSignaturePreRelease2 = 0xB54BFB66;
|
||||
|
||||
private const string ElemDocNode = "KeePassFile";
|
||||
private const string ElemMeta = "Meta";
|
||||
private const string ElemRoot = "Root";
|
||||
private const string ElemGroup = "Group";
|
||||
private const string ElemEntry = "Entry";
|
||||
|
||||
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";
|
||||
private const string ElemDbDescChanged = "DatabaseDescriptionChanged";
|
||||
private const string ElemDbDefaultUser = "DefaultUserName";
|
||||
private const string ElemDbDefaultUserChanged = "DefaultUserNameChanged";
|
||||
private const string ElemDbMntncHistoryDays = "MaintenanceHistoryDays";
|
||||
private const string ElemDbColor = "Color";
|
||||
private const string ElemDbKeyChanged = "MasterKeyChanged";
|
||||
private const string ElemDbKeyChangeRec = "MasterKeyChangeRec";
|
||||
private const string ElemDbKeyChangeForce = "MasterKeyChangeForce";
|
||||
private const string ElemDbKeyChangeForceOnce = "MasterKeyChangeForceOnce";
|
||||
private const string ElemRecycleBinEnabled = "RecycleBinEnabled";
|
||||
private const string ElemRecycleBinUuid = "RecycleBinUUID";
|
||||
private const string ElemRecycleBinChanged = "RecycleBinChanged";
|
||||
private const string ElemEntryTemplatesGroup = "EntryTemplatesGroup";
|
||||
private const string ElemEntryTemplatesGroupChanged = "EntryTemplatesGroupChanged";
|
||||
private const string ElemHistoryMaxItems = "HistoryMaxItems";
|
||||
private const string ElemHistoryMaxSize = "HistoryMaxSize";
|
||||
private const string ElemLastSelectedGroup = "LastSelectedGroup";
|
||||
private const string ElemLastTopVisibleGroup = "LastTopVisibleGroup";
|
||||
|
||||
private const string ElemMemoryProt = "MemoryProtection";
|
||||
private const string ElemProtTitle = "ProtectTitle";
|
||||
private const string ElemProtUserName = "ProtectUserName";
|
||||
private const string ElemProtPassword = "ProtectPassword";
|
||||
private const string ElemProtUrl = "ProtectURL";
|
||||
private const string ElemProtNotes = "ProtectNotes";
|
||||
// private const string ElemProtAutoHide = "AutoEnableVisualHiding";
|
||||
|
||||
private const string ElemCustomIcons = "CustomIcons";
|
||||
private const string ElemCustomIconItem = "Icon";
|
||||
private const string ElemCustomIconItemID = "UUID";
|
||||
private const string ElemCustomIconItemData = "Data";
|
||||
|
||||
private const string ElemAutoType = "AutoType";
|
||||
private const string ElemHistory = "History";
|
||||
|
||||
private const string ElemName = "Name";
|
||||
private const string ElemNotes = "Notes";
|
||||
private const string ElemUuid = "UUID";
|
||||
private const string ElemIcon = "IconID";
|
||||
private const string ElemCustomIconID = "CustomIconUUID";
|
||||
private const string ElemFgColor = "ForegroundColor";
|
||||
private const string ElemBgColor = "BackgroundColor";
|
||||
private const string ElemOverrideUrl = "OverrideURL";
|
||||
private const string ElemTimes = "Times";
|
||||
private const string ElemTags = "Tags";
|
||||
|
||||
private const string ElemCreationTime = "CreationTime";
|
||||
private const string ElemLastModTime = "LastModificationTime";
|
||||
private const string ElemLastAccessTime = "LastAccessTime";
|
||||
private const string ElemExpiryTime = "ExpiryTime";
|
||||
private const string ElemExpires = "Expires";
|
||||
private const string ElemUsageCount = "UsageCount";
|
||||
private const string ElemLocationChanged = "LocationChanged";
|
||||
|
||||
private const string ElemGroupDefaultAutoTypeSeq = "DefaultAutoTypeSequence";
|
||||
private const string ElemEnableAutoType = "EnableAutoType";
|
||||
private const string ElemEnableSearching = "EnableSearching";
|
||||
|
||||
private const string ElemString = "String";
|
||||
private const string ElemBinary = "Binary";
|
||||
private const string ElemKey = "Key";
|
||||
private const string ElemValue = "Value";
|
||||
|
||||
private const string ElemAutoTypeEnabled = "Enabled";
|
||||
private const string ElemAutoTypeObfuscation = "DataTransferObfuscation";
|
||||
private const string ElemAutoTypeDefaultSeq = "DefaultSequence";
|
||||
private const string ElemAutoTypeItem = "Association";
|
||||
private const string ElemWindow = "Window";
|
||||
private const string ElemKeystrokeSequence = "KeystrokeSequence";
|
||||
|
||||
private const string ElemBinaries = "Binaries";
|
||||
|
||||
private const string AttrId = "ID";
|
||||
private const string AttrRef = "Ref";
|
||||
private const string AttrProtected = "Protected";
|
||||
private const string AttrProtectedInMemPlainXml = "ProtectInMemory";
|
||||
private const string AttrCompressed = "Compressed";
|
||||
|
||||
private const string ElemIsExpanded = "IsExpanded";
|
||||
private const string ElemLastTopVisibleEntry = "LastTopVisibleEntry";
|
||||
|
||||
private const string ElemDeletedObjects = "DeletedObjects";
|
||||
private const string ElemDeletedObject = "DeletedObject";
|
||||
private const string ElemDeletionTime = "DeletionTime";
|
||||
|
||||
private const string ValFalse = "False";
|
||||
private const string ValTrue = "True";
|
||||
|
||||
private const string ElemCustomData = "CustomData";
|
||||
private const string ElemStringDictExItem = "Item";
|
||||
|
||||
private PwDatabase m_pwDatabase; // Not null, see constructor
|
||||
private bool m_bUsedOnce = false;
|
||||
|
||||
private XmlWriter m_xmlWriter = null;
|
||||
private CryptoRandomStream m_randomStream = null;
|
||||
private KdbxFormat m_format = KdbxFormat.Default;
|
||||
private IStatusLogger m_slLogger = null;
|
||||
|
||||
private uint m_uFileVersion = 0;
|
||||
private byte[] m_pbMasterSeed = null;
|
||||
// private byte[] m_pbTransformSeed = null;
|
||||
private byte[] m_pbEncryptionIV = null;
|
||||
private byte[] m_pbStreamStartBytes = null;
|
||||
|
||||
// ArcFourVariant only for backward compatibility; KeePass defaults
|
||||
// to a more secure algorithm when *writing* databases
|
||||
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
|
||||
private byte[] m_pbInnerRandomStreamKey = null;
|
||||
|
||||
private ProtectedBinarySet m_pbsBinaries = new ProtectedBinarySet();
|
||||
|
||||
private byte[] m_pbHashOfHeader = null;
|
||||
private byte[] m_pbHashOfFileOnDisk = null;
|
||||
|
||||
private readonly DateTime m_dtNow = DateTime.UtcNow; // Cache current time
|
||||
|
||||
private const uint NeutralLanguageOffset = 0x100000; // 2^20, see 32-bit Unicode specs
|
||||
private const uint NeutralLanguageIDSec = 0x7DC5C; // See 32-bit Unicode specs
|
||||
private const uint NeutralLanguageID = NeutralLanguageOffset + NeutralLanguageIDSec;
|
||||
private static bool m_bLocalizedNames = false;
|
||||
|
||||
private enum KdbxHeaderFieldID : byte
|
||||
{
|
||||
EndOfHeader = 0,
|
||||
Comment = 1,
|
||||
CipherID = 2,
|
||||
CompressionFlags = 3,
|
||||
MasterSeed = 4,
|
||||
TransformSeed = 5, // KDBX 3.1, for backward compatibility only
|
||||
TransformRounds = 6, // KDBX 3.1, for backward compatibility only
|
||||
EncryptionIV = 7,
|
||||
InnerRandomStreamKey = 8, // KDBX 3.1, for backward compatibility only
|
||||
StreamStartBytes = 9, // KDBX 3.1, for backward compatibility only
|
||||
InnerRandomStreamID = 10, // KDBX 3.1, for backward compatibility only
|
||||
KdfParameters = 11, // KDBX 4, superseding Transform*
|
||||
PublicCustomData = 12 // KDBX 4
|
||||
}
|
||||
|
||||
// Inner header in KDBX >= 4 files
|
||||
private enum KdbxInnerHeaderFieldID : byte
|
||||
{
|
||||
EndOfHeader = 0,
|
||||
InnerRandomStreamID = 1, // Supersedes KdbxHeaderFieldID.InnerRandomStreamID
|
||||
InnerRandomStreamKey = 2, // Supersedes KdbxHeaderFieldID.InnerRandomStreamKey
|
||||
Binary = 3
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum KdbxBinaryFlags : byte
|
||||
{
|
||||
None = 0,
|
||||
Protected = 1
|
||||
}
|
||||
|
||||
public byte[] HashOfFileOnDisk
|
||||
{
|
||||
get { return m_pbHashOfFileOnDisk; }
|
||||
}
|
||||
|
||||
private bool m_bRepairMode = false;
|
||||
public bool RepairMode
|
||||
{
|
||||
get { return m_bRepairMode; }
|
||||
set { m_bRepairMode = value; }
|
||||
}
|
||||
|
||||
private uint m_uForceVersion = 0;
|
||||
internal uint ForceVersion
|
||||
{
|
||||
get { return m_uForceVersion; }
|
||||
set { m_uForceVersion = value; }
|
||||
}
|
||||
|
||||
private string m_strDetachBins = null;
|
||||
/// <summary>
|
||||
/// Detach binaries when opening a file. If this isn't <c>null</c>,
|
||||
/// all binaries are saved to the specified path and are removed
|
||||
/// from the database.
|
||||
/// </summary>
|
||||
public string DetachBinaries
|
||||
{
|
||||
get { return m_strDetachBins; }
|
||||
set { m_strDetachBins = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
/// <param name="pwDataStore">The <c>PwDatabase</c> instance that the
|
||||
/// class will load file data into or use to create a KDBX file.</param>
|
||||
public KdbxFile(PwDatabase pwDataStore)
|
||||
{
|
||||
Debug.Assert(pwDataStore != null);
|
||||
if(pwDataStore == null) throw new ArgumentNullException("pwDataStore");
|
||||
|
||||
m_pwDatabase = pwDataStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this once to determine the current localization settings.
|
||||
/// </summary>
|
||||
public static void DetermineLanguageId()
|
||||
{
|
||||
// Test if localized names should be used. If localized names are used,
|
||||
// the m_bLocalizedNames value must be set to true. By default, localized
|
||||
// names should be used! (Otherwise characters could be corrupted
|
||||
// because of different code pages).
|
||||
unchecked
|
||||
{
|
||||
uint uTest = 0;
|
||||
foreach(char ch in PwDatabase.LocalizedAppName)
|
||||
uTest = uTest * 5 + ch;
|
||||
|
||||
m_bLocalizedNames = (uTest != NeutralLanguageID);
|
||||
}
|
||||
}
|
||||
|
||||
private uint GetMinKdbxVersion()
|
||||
{
|
||||
if(m_uForceVersion != 0) return m_uForceVersion;
|
||||
|
||||
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
|
||||
|
||||
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].Dispose(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
// Do not clear the list
|
||||
}
|
||||
|
||||
private void CleanUpInnerRandomStream()
|
||||
{
|
||||
if(m_randomStream != null) m_randomStream.Dispose();
|
||||
|
||||
if(m_pbInnerRandomStreamKey != null)
|
||||
MemUtil.ZeroByteArray(m_pbInnerRandomStreamKey);
|
||||
}
|
||||
|
||||
private static void SaveBinary(string strName, ProtectedBinary pb,
|
||||
string strSaveDir)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(string.IsNullOrEmpty(strName)) strName = "File.bin";
|
||||
|
||||
string strPath;
|
||||
int iTry = 1;
|
||||
do
|
||||
{
|
||||
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
|
||||
|
||||
string strExt = UrlUtil.GetExtension(strName);
|
||||
string strDesc = UrlUtil.StripExtension(strName);
|
||||
|
||||
strPath += strDesc;
|
||||
if(iTry > 1)
|
||||
strPath += " (" + iTry.ToString(NumberFormatInfo.InvariantInfo) +
|
||||
")";
|
||||
|
||||
if(!string.IsNullOrEmpty(strExt)) strPath += "." + strExt;
|
||||
|
||||
++iTry;
|
||||
}
|
||||
#if ModernKeePassLib
|
||||
while (StorageFile.GetFileFromPathAsync(strPath).GetResults() != null);
|
||||
#else
|
||||
while(File.Exists(strPath));
|
||||
#endif
|
||||
|
||||
#if ModernKeePassLib
|
||||
byte[] pbData = pb.ReadData();
|
||||
/*var file = FileSystem.Current.GetFileFromPathAsync(strPath).Result;
|
||||
using (var stream = file.OpenAsync(FileAccess.ReadAndWrite).Result) {*/
|
||||
var file = StorageFile.GetFileFromPathAsync(strPath).GetAwaiter().GetResult();
|
||||
using (var stream = file.OpenAsync(FileAccessMode.ReadWrite).GetAwaiter().GetResult().AsStream())
|
||||
{
|
||||
stream.Write (pbData, 0, pbData.Length);
|
||||
}
|
||||
MemUtil.ZeroByteArray(pbData);
|
||||
#elif !KeePassLibSD
|
||||
byte[] pbData = pb.ReadData();
|
||||
File.WriteAllBytes(strPath, pbData);
|
||||
MemUtil.ZeroByteArray(pbData);
|
||||
#else
|
||||
FileStream fs = new FileStream(strPath, FileMode.Create,
|
||||
FileAccess.Write, FileShare.None);
|
||||
byte[] pbData = pb.ReadData();
|
||||
try { File.WriteAllBytes(strPath, pbData); }
|
||||
finally { if(pb.IsProtected) MemUtil.ZeroByteArray(pbData); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
66
ModernKeePassLib/Serialization/OldFormatException.cs
Normal file
66
ModernKeePassLib/Serialization/OldFormatException.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class OldFormatException : Exception
|
||||
{
|
||||
private string m_strFormat = string.Empty;
|
||||
private OldFormatType m_type = OldFormatType.Unknown;
|
||||
|
||||
public enum OldFormatType
|
||||
{
|
||||
Unknown = 0,
|
||||
KeePass1x = 1
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
string str = KLRes.OldFormat + ((m_strFormat.Length > 0) ?
|
||||
(@" (" + m_strFormat + @")") : string.Empty) + ".";
|
||||
|
||||
if(m_type == OldFormatType.KeePass1x)
|
||||
str += MessageService.NewParagraph + KLRes.KeePass1xHint;
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public OldFormatException(string strFormatName)
|
||||
{
|
||||
if(strFormatName != null) m_strFormat = strFormatName;
|
||||
}
|
||||
|
||||
public OldFormatException(string strFormatName, OldFormatType t)
|
||||
{
|
||||
if(strFormatName != null) m_strFormat = strFormatName;
|
||||
|
||||
m_type = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
400
ModernKeePassLib/Translation/KPControlCustomization.cs
Normal file
400
ModernKeePassLib/Translation/KPControlCustomization.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Translation
|
||||
{
|
||||
public sealed class KpccLayout
|
||||
{
|
||||
public enum LayoutParameterEx
|
||||
{
|
||||
X, Y, Width, Height
|
||||
}
|
||||
|
||||
private const string m_strControlRelative = @"%c";
|
||||
|
||||
internal const NumberStyles m_nsParser = (NumberStyles.AllowLeadingSign |
|
||||
NumberStyles.AllowDecimalPoint);
|
||||
internal static readonly CultureInfo m_lclInv = CultureInfo.InvariantCulture;
|
||||
|
||||
private string m_strPosX = string.Empty;
|
||||
[XmlAttribute]
|
||||
[DefaultValue("")]
|
||||
public string X
|
||||
{
|
||||
get { return m_strPosX; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strPosX = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strPosY = string.Empty;
|
||||
[XmlAttribute]
|
||||
[DefaultValue("")]
|
||||
public string Y
|
||||
{
|
||||
get { return m_strPosY; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strPosY = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strSizeW = string.Empty;
|
||||
[XmlAttribute]
|
||||
[DefaultValue("")]
|
||||
public string Width
|
||||
{
|
||||
get { return m_strSizeW; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strSizeW = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strSizeH = string.Empty;
|
||||
[XmlAttribute]
|
||||
[DefaultValue("")]
|
||||
public string Height
|
||||
{
|
||||
get { return m_strSizeH; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strSizeH = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetControlRelativeValue(LayoutParameterEx lp, string strValue)
|
||||
{
|
||||
Debug.Assert(strValue != null);
|
||||
if(strValue == null) throw new ArgumentNullException("strValue");
|
||||
|
||||
if(strValue.Length > 0) strValue += m_strControlRelative;
|
||||
|
||||
if(lp == LayoutParameterEx.X) m_strPosX = strValue;
|
||||
else if(lp == LayoutParameterEx.Y) m_strPosY = strValue;
|
||||
else if(lp == LayoutParameterEx.Width) m_strSizeW = strValue;
|
||||
else if(lp == LayoutParameterEx.Height) m_strSizeH = strValue;
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
internal void ApplyTo(Control c)
|
||||
{
|
||||
Debug.Assert(c != null); if(c == null) return;
|
||||
|
||||
int? v;
|
||||
v = GetModControlParameter(c, LayoutParameterEx.X, m_strPosX);
|
||||
if(v.HasValue) c.Left = v.Value;
|
||||
v = GetModControlParameter(c, LayoutParameterEx.Y, m_strPosY);
|
||||
if(v.HasValue) c.Top = v.Value;
|
||||
v = GetModControlParameter(c, LayoutParameterEx.Width, m_strSizeW);
|
||||
if(v.HasValue) c.Width = v.Value;
|
||||
v = GetModControlParameter(c, LayoutParameterEx.Height, m_strSizeH);
|
||||
if(v.HasValue) c.Height = v.Value;
|
||||
}
|
||||
|
||||
private static int? GetModControlParameter(Control c, LayoutParameterEx p,
|
||||
string strModParam)
|
||||
{
|
||||
if(strModParam.Length == 0) return null;
|
||||
|
||||
Debug.Assert(c.Left == c.Location.X);
|
||||
Debug.Assert(c.Top == c.Location.Y);
|
||||
Debug.Assert(c.Width == c.Size.Width);
|
||||
Debug.Assert(c.Height == c.Size.Height);
|
||||
|
||||
int iPrev;
|
||||
if(p == LayoutParameterEx.X) iPrev = c.Left;
|
||||
else if(p == LayoutParameterEx.Y) iPrev = c.Top;
|
||||
else if(p == LayoutParameterEx.Width) iPrev = c.Width;
|
||||
else if(p == LayoutParameterEx.Height) iPrev = c.Height;
|
||||
else { Debug.Assert(false); return null; }
|
||||
|
||||
double? dRel = ToControlRelativePercent(strModParam);
|
||||
if(dRel.HasValue)
|
||||
return (iPrev + (int)((dRel.Value * (double)iPrev) / 100.0));
|
||||
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static double? ToControlRelativePercent(string strEncoded)
|
||||
{
|
||||
Debug.Assert(strEncoded != null);
|
||||
if(strEncoded == null) throw new ArgumentNullException("strEncoded");
|
||||
|
||||
if(strEncoded.Length == 0) return null;
|
||||
|
||||
if(strEncoded.EndsWith(m_strControlRelative))
|
||||
{
|
||||
string strValue = strEncoded.Substring(0, strEncoded.Length -
|
||||
m_strControlRelative.Length);
|
||||
if((strValue.Length == 1) && (strValue == "-"))
|
||||
strValue = "0";
|
||||
|
||||
double dRel;
|
||||
if(double.TryParse(strValue, m_nsParser, m_lclInv, out dRel))
|
||||
{
|
||||
return dRel;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static string ToControlRelativeString(string strEncoded)
|
||||
{
|
||||
Debug.Assert(strEncoded != null);
|
||||
if(strEncoded == null) throw new ArgumentNullException("strEncoded");
|
||||
|
||||
if(strEncoded.Length == 0) return string.Empty;
|
||||
|
||||
if(strEncoded.EndsWith(m_strControlRelative))
|
||||
return strEncoded.Substring(0, strEncoded.Length -
|
||||
m_strControlRelative.Length);
|
||||
|
||||
Debug.Assert(false);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class KPControlCustomization : IComparable<KPControlCustomization>
|
||||
{
|
||||
private string m_strMemberName = string.Empty;
|
||||
/// <summary>
|
||||
/// Member variable name of the control to be translated.
|
||||
/// </summary>
|
||||
[XmlAttribute]
|
||||
public string Name
|
||||
{
|
||||
get { return m_strMemberName; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strMemberName = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strHash = string.Empty;
|
||||
[XmlAttribute]
|
||||
public string BaseHash
|
||||
{
|
||||
get { return m_strHash; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strHash = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strText = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string Text
|
||||
{
|
||||
get { return m_strText; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strText = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strEngText = string.Empty;
|
||||
[XmlIgnore]
|
||||
public string TextEnglish
|
||||
{
|
||||
get { return m_strEngText; }
|
||||
set { m_strEngText = value; }
|
||||
}
|
||||
|
||||
private KpccLayout m_layout = new KpccLayout();
|
||||
public KpccLayout Layout
|
||||
{
|
||||
get { return m_layout; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_layout = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(KPControlCustomization kpOther)
|
||||
{
|
||||
if(kpOther == null) { Debug.Assert(false); return 1; }
|
||||
|
||||
return m_strMemberName.CompareTo(kpOther.Name);
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
private static readonly Type[] m_vTextControls = new Type[] {
|
||||
typeof(MenuStrip), typeof(PictureBox), typeof(ListView),
|
||||
typeof(TreeView), typeof(ToolStrip), typeof(WebBrowser),
|
||||
typeof(Panel), typeof(StatusStrip), typeof(ProgressBar),
|
||||
typeof(NumericUpDown), typeof(TabControl)
|
||||
};
|
||||
|
||||
public static bool ControlSupportsText(object oControl)
|
||||
{
|
||||
if(oControl == null) return false;
|
||||
|
||||
Type t = oControl.GetType();
|
||||
for(int i = 0; i < m_vTextControls.Length; ++i)
|
||||
{
|
||||
if(t == m_vTextControls[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Name-unchecked (!) property application method
|
||||
internal void ApplyTo(Control c)
|
||||
{
|
||||
if((m_strText.Length > 0) && ControlSupportsText(c) &&
|
||||
(c.Text.Length > 0))
|
||||
{
|
||||
c.Text = m_strText;
|
||||
}
|
||||
|
||||
m_layout.ApplyTo(c);
|
||||
}
|
||||
|
||||
public static string HashControl(Control c)
|
||||
{
|
||||
if(c == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
WriteCpiParam(sb, c.Text);
|
||||
|
||||
if(c is Form)
|
||||
{
|
||||
WriteCpiParam(sb, c.ClientSize.Width.ToString(KpccLayout.m_lclInv));
|
||||
WriteCpiParam(sb, c.ClientSize.Height.ToString(KpccLayout.m_lclInv));
|
||||
}
|
||||
else // Normal control
|
||||
{
|
||||
WriteCpiParam(sb, c.Left.ToString(KpccLayout.m_lclInv));
|
||||
WriteCpiParam(sb, c.Top.ToString(KpccLayout.m_lclInv));
|
||||
WriteCpiParam(sb, c.Width.ToString(KpccLayout.m_lclInv));
|
||||
WriteCpiParam(sb, c.Height.ToString(KpccLayout.m_lclInv));
|
||||
WriteCpiParam(sb, c.Dock.ToString());
|
||||
}
|
||||
|
||||
WriteCpiParam(sb, c.Font.Name);
|
||||
WriteCpiParam(sb, c.Font.SizeInPoints.ToString(KpccLayout.m_lclInv));
|
||||
WriteCpiParam(sb, c.Font.Bold ? "B" : "N");
|
||||
WriteCpiParam(sb, c.Font.Italic ? "I" : "N");
|
||||
WriteCpiParam(sb, c.Font.Underline ? "U" : "N");
|
||||
WriteCpiParam(sb, c.Font.Strikeout ? "S" : "N");
|
||||
|
||||
WriteControlDependentParams(sb, c);
|
||||
|
||||
byte[] pb = StrUtil.Utf8.GetBytes(sb.ToString());
|
||||
byte[] pbSha = CryptoUtil.HashSha256(pb);
|
||||
|
||||
// See also MatchHash
|
||||
return "v1:" + Convert.ToBase64String(pbSha, 0, 3,
|
||||
Base64FormattingOptions.None);
|
||||
}
|
||||
|
||||
private static void WriteControlDependentParams(StringBuilder sb, Control c)
|
||||
{
|
||||
CheckBox cb = (c as CheckBox);
|
||||
RadioButton rb = (c as RadioButton);
|
||||
Button btn = (c as Button);
|
||||
Label l = (c as Label);
|
||||
LinkLabel ll = (c as LinkLabel);
|
||||
|
||||
if(cb != null)
|
||||
{
|
||||
WriteCpiParam(sb, cb.AutoSize ? "A" : "F");
|
||||
WriteCpiParam(sb, cb.TextAlign.ToString());
|
||||
WriteCpiParam(sb, cb.TextImageRelation.ToString());
|
||||
WriteCpiParam(sb, cb.Appearance.ToString());
|
||||
WriteCpiParam(sb, cb.CheckAlign.ToString());
|
||||
}
|
||||
else if(rb != null)
|
||||
{
|
||||
WriteCpiParam(sb, rb.AutoSize ? "A" : "F");
|
||||
WriteCpiParam(sb, rb.TextAlign.ToString());
|
||||
WriteCpiParam(sb, rb.TextImageRelation.ToString());
|
||||
WriteCpiParam(sb, rb.Appearance.ToString());
|
||||
WriteCpiParam(sb, rb.CheckAlign.ToString());
|
||||
}
|
||||
else if(btn != null)
|
||||
{
|
||||
WriteCpiParam(sb, btn.AutoSize ? "A" : "F");
|
||||
WriteCpiParam(sb, btn.TextAlign.ToString());
|
||||
WriteCpiParam(sb, btn.TextImageRelation.ToString());
|
||||
}
|
||||
else if(l != null)
|
||||
{
|
||||
WriteCpiParam(sb, l.AutoSize ? "A" : "F");
|
||||
WriteCpiParam(sb, l.TextAlign.ToString());
|
||||
}
|
||||
else if(ll != null)
|
||||
{
|
||||
WriteCpiParam(sb, ll.AutoSize ? "A" : "F");
|
||||
WriteCpiParam(sb, ll.TextAlign.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteCpiParam(StringBuilder sb, string strProp)
|
||||
{
|
||||
sb.Append('/');
|
||||
sb.Append(strProp);
|
||||
}
|
||||
|
||||
public bool MatchHash(string strHash)
|
||||
{
|
||||
if(strHash == null) throw new ArgumentNullException("strHash");
|
||||
|
||||
// Currently only v1: is supported, see HashControl
|
||||
return (m_strHash == strHash);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
108
ModernKeePassLib/Translation/KPFormCustomization.cs
Normal file
108
ModernKeePassLib/Translation/KPFormCustomization.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 System.Xml.Serialization;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Translation
|
||||
{
|
||||
public sealed class KPFormCustomization
|
||||
{
|
||||
private string m_strFQName = string.Empty;
|
||||
/// <summary>
|
||||
/// The fully qualified name of the form.
|
||||
/// </summary>
|
||||
[XmlAttribute]
|
||||
public string FullName
|
||||
{
|
||||
get { return m_strFQName; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strFQName = value;
|
||||
}
|
||||
}
|
||||
|
||||
private KPControlCustomization m_ccWindow = new KPControlCustomization();
|
||||
public KPControlCustomization Window
|
||||
{
|
||||
get { return m_ccWindow; }
|
||||
set { m_ccWindow = value; }
|
||||
}
|
||||
|
||||
private List<KPControlCustomization> m_vControls =
|
||||
new List<KPControlCustomization>();
|
||||
[XmlArray("ChildControls")]
|
||||
[XmlArrayItem("Control")]
|
||||
public List<KPControlCustomization> Controls
|
||||
{
|
||||
get { return m_vControls; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_vControls = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
private Form m_formEnglish = null;
|
||||
[XmlIgnore]
|
||||
public Form FormEnglish
|
||||
{
|
||||
get { return m_formEnglish; }
|
||||
set { m_formEnglish = value; }
|
||||
}
|
||||
|
||||
public void ApplyTo(Form form)
|
||||
{
|
||||
Debug.Assert(form != null); if(form == null) throw new ArgumentNullException("form");
|
||||
|
||||
// Not supported by TrlUtil (preview form):
|
||||
// Debug.Assert(form.GetType().FullName == m_strFQName);
|
||||
|
||||
m_ccWindow.ApplyTo(form);
|
||||
|
||||
if(m_vControls.Count == 0) return;
|
||||
foreach(Control c in form.Controls) ApplyToControl(c);
|
||||
}
|
||||
|
||||
private void ApplyToControl(Control c)
|
||||
{
|
||||
foreach(KPControlCustomization cc in m_vControls)
|
||||
{
|
||||
if(c.Name == cc.Name)
|
||||
{
|
||||
cc.ApplyTo(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Control cSub in c.Controls) ApplyToControl(cSub);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
102
ModernKeePassLib/Translation/KPStringTable.cs
Normal file
102
ModernKeePassLib/Translation/KPStringTable.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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 System.Xml.Serialization;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Translation
|
||||
{
|
||||
public sealed class KPStringTable
|
||||
{
|
||||
private string m_strName = string.Empty;
|
||||
[XmlAttribute]
|
||||
public string Name
|
||||
{
|
||||
get { return m_strName; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strName = value;
|
||||
}
|
||||
}
|
||||
|
||||
private List<KPStringTableItem> m_vItems = new List<KPStringTableItem>();
|
||||
|
||||
[XmlArrayItem("Data")]
|
||||
public List<KPStringTableItem> Strings
|
||||
{
|
||||
get { return m_vItems; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_vItems = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> ToDictionary()
|
||||
{
|
||||
Dictionary<string, string> dict = new Dictionary<string, string>();
|
||||
|
||||
foreach(KPStringTableItem kpstItem in m_vItems)
|
||||
{
|
||||
if(kpstItem.Value.Length > 0)
|
||||
dict[kpstItem.Name] = kpstItem.Value;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
public void ApplyTo(ToolStripItemCollection tsic)
|
||||
{
|
||||
if(tsic == null) throw new ArgumentNullException("tsic");
|
||||
|
||||
Dictionary<string, string> dict = this.ToDictionary();
|
||||
if(dict.Count == 0) return;
|
||||
|
||||
this.ApplyTo(tsic, dict);
|
||||
}
|
||||
|
||||
private void ApplyTo(ToolStripItemCollection tsic, Dictionary<string, string> dict)
|
||||
{
|
||||
if(tsic == null) return;
|
||||
|
||||
foreach(ToolStripItem tsi in tsic)
|
||||
{
|
||||
if(tsi.Text.Length == 0) continue;
|
||||
|
||||
string strTrl;
|
||||
if(dict.TryGetValue(tsi.Name, out strTrl))
|
||||
tsi.Text = strTrl;
|
||||
|
||||
ToolStripMenuItem tsmi = tsi as ToolStripMenuItem;
|
||||
if((tsmi != null) && (tsmi.DropDownItems != null))
|
||||
this.ApplyTo(tsmi.DropDownItems);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
51
ModernKeePassLib/Translation/KPStringTableItem.cs
Normal file
51
ModernKeePassLib/Translation/KPStringTableItem.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace ModernKeePassLib.Translation
|
||||
{
|
||||
public sealed class KPStringTableItem
|
||||
{
|
||||
private string m_strName = string.Empty;
|
||||
public string Name
|
||||
{
|
||||
get { return m_strName; }
|
||||
set { m_strName = value; }
|
||||
}
|
||||
|
||||
private string m_strValue = string.Empty;
|
||||
public string Value
|
||||
{
|
||||
get { return m_strValue; }
|
||||
set { m_strValue = value; }
|
||||
}
|
||||
|
||||
private string m_strEnglish = string.Empty;
|
||||
[XmlIgnore]
|
||||
public string ValueEnglish
|
||||
{
|
||||
get { return m_strEnglish; }
|
||||
set { m_strEnglish = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
325
ModernKeePassLib/Translation/KPTranslation.cs
Normal file
325
ModernKeePassLib/Translation/KPTranslation.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
#endif
|
||||
|
||||
#if KeePassLibSD
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
#else
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Translation
|
||||
{
|
||||
[XmlRoot("Translation")]
|
||||
public sealed class KPTranslation
|
||||
{
|
||||
public static readonly string FileExtension = "lngx";
|
||||
|
||||
private KPTranslationProperties m_props = new KPTranslationProperties();
|
||||
public KPTranslationProperties Properties
|
||||
{
|
||||
get { return m_props; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_props = value;
|
||||
}
|
||||
}
|
||||
|
||||
private List<KPStringTable> m_vStringTables = new List<KPStringTable>();
|
||||
|
||||
[XmlArrayItem("StringTable")]
|
||||
public List<KPStringTable> StringTables
|
||||
{
|
||||
get { return m_vStringTables; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_vStringTables = value;
|
||||
}
|
||||
}
|
||||
|
||||
private List<KPFormCustomization> m_vForms = new List<KPFormCustomization>();
|
||||
|
||||
[XmlArrayItem("Form")]
|
||||
public List<KPFormCustomization> Forms
|
||||
{
|
||||
get { return m_vForms; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_vForms = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strUnusedText = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string UnusedText
|
||||
{
|
||||
get { return m_strUnusedText; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_strUnusedText = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save(KPTranslation kpTrl, string strFileName,
|
||||
IXmlSerializerEx xs)
|
||||
{
|
||||
using(FileStream fs = new FileStream(strFileName, FileMode.Create,
|
||||
FileAccess.Write, FileShare.None))
|
||||
{
|
||||
Save(kpTrl, fs, xs);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save(KPTranslation kpTrl, Stream sOut,
|
||||
IXmlSerializerEx xs)
|
||||
{
|
||||
if(xs == null) throw new ArgumentNullException("xs");
|
||||
|
||||
#if !KeePassLibSD
|
||||
using(GZipStream gz = new GZipStream(sOut, CompressionMode.Compress))
|
||||
#else
|
||||
using(GZipOutputStream gz = new GZipOutputStream(sOut))
|
||||
#endif
|
||||
{
|
||||
using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(gz))
|
||||
{
|
||||
xs.Serialize(xw, kpTrl);
|
||||
}
|
||||
}
|
||||
|
||||
sOut.Close();
|
||||
}
|
||||
|
||||
public static KPTranslation Load(string strFile, IXmlSerializerEx xs)
|
||||
{
|
||||
KPTranslation kpTrl = null;
|
||||
|
||||
using(FileStream fs = new FileStream(strFile, FileMode.Open,
|
||||
FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
kpTrl = Load(fs, xs);
|
||||
}
|
||||
|
||||
return kpTrl;
|
||||
}
|
||||
|
||||
public static KPTranslation Load(Stream s, IXmlSerializerEx xs)
|
||||
{
|
||||
if(xs == null) throw new ArgumentNullException("xs");
|
||||
|
||||
KPTranslation kpTrl = null;
|
||||
|
||||
#if !KeePassLibSD
|
||||
using(GZipStream gz = new GZipStream(s, CompressionMode.Decompress))
|
||||
#else
|
||||
using(GZipInputStream gz = new GZipInputStream(s))
|
||||
#endif
|
||||
{
|
||||
kpTrl = (xs.Deserialize(gz) as KPTranslation);
|
||||
}
|
||||
|
||||
s.Close();
|
||||
return kpTrl;
|
||||
}
|
||||
|
||||
public Dictionary<string, string> SafeGetStringTableDictionary(
|
||||
string strTableName)
|
||||
{
|
||||
foreach(KPStringTable kpst in m_vStringTables)
|
||||
{
|
||||
if(kpst.Name == strTableName) return kpst.ToDictionary();
|
||||
}
|
||||
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
public void ApplyTo(Form form)
|
||||
{
|
||||
if(form == null) throw new ArgumentNullException("form");
|
||||
|
||||
if(m_props.RightToLeft)
|
||||
{
|
||||
try
|
||||
{
|
||||
form.RightToLeft = RightToLeft.Yes;
|
||||
form.RightToLeftLayout = true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
string strTypeName = form.GetType().FullName;
|
||||
foreach(KPFormCustomization kpfc in m_vForms)
|
||||
{
|
||||
if(kpfc.FullName == strTypeName)
|
||||
{
|
||||
kpfc.ApplyTo(form);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_props.RightToLeft)
|
||||
{
|
||||
try { RtlApplyToControls(form.Controls); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
}
|
||||
|
||||
private static void RtlApplyToControls(Control.ControlCollection cc)
|
||||
{
|
||||
foreach(Control c in cc)
|
||||
{
|
||||
if(c.Controls.Count > 0) RtlApplyToControls(c.Controls);
|
||||
|
||||
if(c is DateTimePicker)
|
||||
((DateTimePicker)c).RightToLeftLayout = true;
|
||||
else if(c is ListView)
|
||||
((ListView)c).RightToLeftLayout = true;
|
||||
else if(c is MonthCalendar)
|
||||
((MonthCalendar)c).RightToLeftLayout = true;
|
||||
else if(c is ProgressBar)
|
||||
((ProgressBar)c).RightToLeftLayout = true;
|
||||
else if(c is TabControl)
|
||||
((TabControl)c).RightToLeftLayout = true;
|
||||
else if(c is TrackBar)
|
||||
((TrackBar)c).RightToLeftLayout = true;
|
||||
else if(c is TreeView)
|
||||
((TreeView)c).RightToLeftLayout = true;
|
||||
// else if(c is ToolStrip)
|
||||
// RtlApplyToToolStripItems(((ToolStrip)c).Items);
|
||||
/* else if(c is Button) // Also see Label
|
||||
{
|
||||
Button btn = (c as Button);
|
||||
Image img = btn.Image;
|
||||
if(img != null)
|
||||
{
|
||||
Image imgNew = (Image)img.Clone();
|
||||
imgNew.RotateFlip(RotateFlipType.RotateNoneFlipX);
|
||||
btn.Image = imgNew;
|
||||
}
|
||||
}
|
||||
else if(c is Label) // Also see Button
|
||||
{
|
||||
Label lbl = (c as Label);
|
||||
Image img = lbl.Image;
|
||||
if(img != null)
|
||||
{
|
||||
Image imgNew = (Image)img.Clone();
|
||||
imgNew.RotateFlip(RotateFlipType.RotateNoneFlipX);
|
||||
lbl.Image = imgNew;
|
||||
}
|
||||
} */
|
||||
|
||||
if(IsRtlMoveChildsRequired(c)) RtlMoveChildControls(c);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsRtlMoveChildsRequired(Control c)
|
||||
{
|
||||
if(c == null) { Debug.Assert(false); return false; }
|
||||
|
||||
return ((c is GroupBox) || (c is Panel));
|
||||
}
|
||||
|
||||
private static void RtlMoveChildControls(Control cParent)
|
||||
{
|
||||
int nParentWidth = cParent.Size.Width;
|
||||
|
||||
foreach(Control c in cParent.Controls)
|
||||
{
|
||||
DockStyle ds = c.Dock;
|
||||
if(ds == DockStyle.Left)
|
||||
c.Dock = DockStyle.Right;
|
||||
else if(ds == DockStyle.Right)
|
||||
c.Dock = DockStyle.Left;
|
||||
else
|
||||
{
|
||||
Point ptCur = c.Location;
|
||||
c.Location = new Point(nParentWidth - c.Size.Width - ptCur.X, ptCur.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* private static readonly string[] g_vRtlMirrorItemNames = new string[] { };
|
||||
private static void RtlApplyToToolStripItems(ToolStripItemCollection tsic)
|
||||
{
|
||||
foreach(ToolStripItem tsi in tsic)
|
||||
{
|
||||
if(tsi == null) { Debug.Assert(false); continue; }
|
||||
|
||||
if(Array.IndexOf<string>(g_vRtlMirrorItemNames, tsi.Name) >= 0)
|
||||
tsi.RightToLeftAutoMirrorImage = true;
|
||||
|
||||
ToolStripDropDownItem tsdd = (tsi as ToolStripDropDownItem);
|
||||
if(tsdd != null)
|
||||
RtlApplyToToolStripItems(tsdd.DropDownItems);
|
||||
}
|
||||
} */
|
||||
|
||||
public void ApplyTo(string strTableName, ToolStripItemCollection tsic)
|
||||
{
|
||||
if(tsic == null) throw new ArgumentNullException("tsic");
|
||||
|
||||
KPStringTable kpst = null;
|
||||
foreach(KPStringTable kpstEnum in m_vStringTables)
|
||||
{
|
||||
if(kpstEnum.Name == strTableName)
|
||||
{
|
||||
kpst = kpstEnum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(kpst != null) kpst.ApplyTo(tsic);
|
||||
}
|
||||
#endif
|
||||
|
||||
internal bool IsFor(string strIso6391Code)
|
||||
{
|
||||
if(strIso6391Code == null) { Debug.Assert(false); return false; }
|
||||
|
||||
return string.Equals(strIso6391Code, m_props.Iso6391Code,
|
||||
StrUtil.CaseIgnoreCmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
105
ModernKeePassLib/Translation/KPTranslationProperties.cs
Normal file
105
ModernKeePassLib/Translation/KPTranslationProperties.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2019 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.Text;
|
||||
|
||||
namespace ModernKeePassLib.Translation
|
||||
{
|
||||
public sealed class KPTranslationProperties
|
||||
{
|
||||
private string m_strApp = string.Empty;
|
||||
public string Application
|
||||
{
|
||||
get { return m_strApp; }
|
||||
set { m_strApp = value; }
|
||||
}
|
||||
|
||||
private string m_strForVersion = string.Empty;
|
||||
public string ApplicationVersion
|
||||
{
|
||||
get { return m_strForVersion; }
|
||||
set { m_strForVersion = value; }
|
||||
}
|
||||
|
||||
private string m_strNameEnglish = string.Empty;
|
||||
public string NameEnglish
|
||||
{
|
||||
get { return m_strNameEnglish; }
|
||||
set { m_strNameEnglish = value; }
|
||||
}
|
||||
|
||||
private string m_strNameNative = string.Empty;
|
||||
public string NameNative
|
||||
{
|
||||
get { return m_strNameNative; }
|
||||
set { m_strNameNative = value; }
|
||||
}
|
||||
|
||||
private string m_strIso6391Code = string.Empty;
|
||||
public string Iso6391Code
|
||||
{
|
||||
get { return m_strIso6391Code; }
|
||||
set { m_strIso6391Code = value; }
|
||||
}
|
||||
|
||||
private bool m_bRtl = false;
|
||||
public bool RightToLeft
|
||||
{
|
||||
get { return m_bRtl; }
|
||||
set { m_bRtl = value; }
|
||||
}
|
||||
|
||||
private string m_strAuthorName = string.Empty;
|
||||
public string AuthorName
|
||||
{
|
||||
get { return m_strAuthorName; }
|
||||
set { m_strAuthorName = value; }
|
||||
}
|
||||
|
||||
private string m_strAuthorContact = string.Empty;
|
||||
public string AuthorContact
|
||||
{
|
||||
get { return m_strAuthorContact; }
|
||||
set { m_strAuthorContact = value; }
|
||||
}
|
||||
|
||||
private string m_strGen = string.Empty;
|
||||
public string Generator
|
||||
{
|
||||
get { return m_strGen; }
|
||||
set { m_strGen = value; }
|
||||
}
|
||||
|
||||
private string m_strUuid = string.Empty;
|
||||
public string FileUuid
|
||||
{
|
||||
get { return m_strUuid; }
|
||||
set { m_strUuid = value; }
|
||||
}
|
||||
|
||||
private string m_strLastModified = string.Empty;
|
||||
public string LastModified
|
||||
{
|
||||
get { return m_strLastModified; }
|
||||
set { m_strLastModified = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user