Compare commits

...

4 Commits

19 changed files with 12752 additions and 2375 deletions

View File

@@ -11,10 +11,10 @@ Regular stable releases of Keepass2Android are available on [Google Play](https:
Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet). Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet).
# How can I contribute? # How can I contribute?
* Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](http://crowdin.net/project/keepass2android) * Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](https://crowdin.net/project/keepass2android)
* Add features by [creating a plugin](How-to-create-a-plug-in_.md) or creating a pull request. You might want to contact me before you start working so I can coordinate efforts. * Add features by [creating a plugin](How-to-create-a-plug-in_.md) or creating a pull request. You might want to contact me before you start working so I can coordinate efforts.
* [Become a GitHub sponsor to boost 🚀 development](https://github.com/sponsors/PhilippC) * [Become a GitHub sponsor to boost 🚀 development](https://github.com/sponsors/PhilippC)
* [Make a donation](http://philipp.crocoll.net/donate.php) * [Make a donation](https://philipp.crocoll.net/donate.php)
# How do I learn more? # How do I learn more?
Please see the [wiki](https://github.com/PhilippC/keepass2android/wiki/Documentation) for further information. Please see the [wiki](https://github.com/PhilippC/keepass2android/wiki/Documentation) for further information.

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -40,8 +40,8 @@ namespace KeePassLib.Cryptography
Null = 0, Null = 0,
/// <summary> /// <summary>
/// A variant of the ARCFour algorithm (RC4 incompatible). /// A variant of the ArcFour algorithm (RC4 incompatible).
/// </summary> /// Insecure; for backward compatibility only.
/// </summary> /// </summary>
ArcFourVariant = 1, ArcFourVariant = 1,
@@ -66,56 +66,60 @@ namespace KeePassLib.Cryptography
/// </summary> /// </summary>
public sealed class CryptoRandomStream : IDisposable public sealed class CryptoRandomStream : IDisposable
{ {
private readonly CrsAlgorithm m_crsAlgorithm; private readonly CrsAlgorithm m_alg;
private bool m_bDisposed = false;
private byte[] m_pbState = null; private readonly byte[] m_pbKey = null;
private readonly byte[] m_pbIV = null;
private readonly ChaCha20Cipher m_chacha20 = null;
private readonly Salsa20Cipher m_salsa20 = null;
private readonly byte[] m_pbState = null;
private byte m_i = 0; private byte m_i = 0;
private byte m_j = 0; private byte m_j = 0;
private Salsa20Cipher m_salsa20 = null;
private ChaCha20Cipher m_chacha20 = null;
/// <summary> /// <summary>
/// Construct a new cryptographically secure random stream object. /// Construct a new cryptographically secure random stream object.
/// </summary> /// </summary>
/// <param name="genAlgorithm">Algorithm to use.</param> /// <param name="a">Algorithm to use.</param>
/// <param name="pbKey">Initialization key. Must not be <c>null</c> and /// <param name="pbKey">Initialization key. Must not be <c>null</c>
/// must contain at least 1 byte.</param> /// and must contain at least 1 byte.</param>
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey) public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
{ {
if (pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); } if (pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
/// <exception cref="System.ArgumentNullException">Thrown if the
int cbKey = pbKey.Length; int cbKey = pbKey.Length;
if (cbKey <= 0) if (cbKey <= 0)
{ {
Debug.Assert(false); // Need at least one byte Debug.Assert(false); // Need at least one byte
throw new ArgumentOutOfRangeException("pbKey"); throw new ArgumentOutOfRangeException("pbKey");
} }
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
m_crsAlgorithm = a; m_alg = a;
/// <exception cref="System.ArgumentException">Thrown if the
if (a == CrsAlgorithm.ChaCha20) if (a == CrsAlgorithm.ChaCha20)
{ {
byte[] pbKey32 = new byte[32]; m_pbKey = new byte[32];
byte[] pbIV12 = new byte[12]; m_pbIV = new byte[12];
/// <paramref name="pbKey" /> parameter contains no bytes or the
using (SHA512Managed h = new SHA512Managed()) using (SHA512Managed h = new SHA512Managed())
{ {
byte[] pbHash = h.ComputeHash(pbKey); byte[] pbHash = h.ComputeHash(pbKey);
Array.Copy(pbHash, pbKey32, 32); Array.Copy(pbHash, m_pbKey, 32);
Array.Copy(pbHash, 32, pbIV12, 0, 12); Array.Copy(pbHash, 32, m_pbIV, 0, 12);
MemUtil.ZeroByteArray(pbHash); MemUtil.ZeroByteArray(pbHash);
} }
/// algorithm is unknown.</exception>
m_chacha20 = new ChaCha20Cipher(pbKey32, pbIV12, true); m_chacha20 = new ChaCha20Cipher(m_pbKey, m_pbIV, true);
} }
else if (a == CrsAlgorithm.Salsa20) else if (a == CrsAlgorithm.Salsa20)
{ {
byte[] pbKey32 = CryptoUtil.HashSha256(pbKey); m_pbKey = CryptoUtil.HashSha256(pbKey);
byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B, m_pbIV = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
0x97, 0x20, 0x5D, 0x2A }; // Unique constant 0x97, 0x20, 0x5D, 0x2A }; // Unique constant
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8); m_salsa20 = new Salsa20Cipher(m_pbKey, m_pbIV);
} }
else if (a == CrsAlgorithm.ArcFourVariant) else if (a == CrsAlgorithm.ArcFourVariant)
{ {
@@ -159,17 +163,22 @@ namespace KeePassLib.Cryptography
{ {
if (disposing) if (disposing)
{ {
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20) if (m_alg == CrsAlgorithm.ChaCha20)
m_chacha20.Dispose(); m_chacha20.Dispose();
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20) else if (m_alg == CrsAlgorithm.Salsa20)
m_salsa20.Dispose(); m_salsa20.Dispose();
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant) else if (m_alg == CrsAlgorithm.ArcFourVariant)
{ {
MemUtil.ZeroByteArray(m_pbState); MemUtil.ZeroByteArray(m_pbState);
m_i = 0; m_i = 0;
m_j = 0; m_j = 0;
} }
else { Debug.Assert(false); } else { Debug.Assert(false); }
if (m_pbKey != null) MemUtil.ZeroByteArray(m_pbKey);
if (m_pbIV != null) MemUtil.ZeroByteArray(m_pbIV);
m_bDisposed = true;
} }
} }
@@ -180,19 +189,20 @@ namespace KeePassLib.Cryptography
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns> /// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
public byte[] GetRandomBytes(uint uRequestedCount) public byte[] GetRandomBytes(uint uRequestedCount)
{ {
if(uRequestedCount == 0) return MemUtil.EmptyByteArray; if (m_bDisposed) throw new ObjectDisposedException(null);
if (uRequestedCount == 0) return MemUtil.EmptyByteArray;
if (uRequestedCount > (uint)int.MaxValue) if (uRequestedCount > (uint)int.MaxValue)
throw new ArgumentOutOfRangeException("uRequestedCount"); throw new ArgumentOutOfRangeException("uRequestedCount");
int cb = (int)uRequestedCount; int cb = (int)uRequestedCount;
byte[] pbRet = new byte[cb]; byte[] pbRet = new byte[cb];
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20) if (m_alg == CrsAlgorithm.ChaCha20)
m_chacha20.Encrypt(pbRet, 0, cb); m_chacha20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20) else if (m_alg == CrsAlgorithm.Salsa20)
m_salsa20.Encrypt(pbRet, 0, cb); m_salsa20.Encrypt(pbRet, 0, cb);
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant) else if (m_alg == CrsAlgorithm.ArcFourVariant)
{ {
unchecked unchecked
{ {
@@ -221,6 +231,25 @@ namespace KeePassLib.Cryptography
return MemUtil.BytesToUInt64(pb); return MemUtil.BytesToUInt64(pb);
} }
internal ulong GetRandomUInt64(ulong uMaxExcl)
{
if (uMaxExcl == 0) { Debug.Assert(false); throw new ArgumentOutOfRangeException("uMaxExcl"); }
ulong uGen, uRem;
do
{
uGen = GetRandomUInt64();
uRem = uGen % uMaxExcl;
}
while ((uGen - uRem) > (ulong.MaxValue - (uMaxExcl - 1UL)));
// This ensures that the last number of the block (i.e.
// (uGen - uRem) + (uMaxExcl - 1)) is generatable;
// for signed longs, overflow to negative number:
// while((uGen - uRem) + (uMaxExcl - 1) < 0);
return uRem;
}
#if CRSBENCHMARK #if CRSBENCHMARK
public static string Benchmark() public static string Benchmark()
{ {
@@ -237,21 +266,20 @@ namespace KeePassLib.Cryptography
return str; return str;
} }
private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize) private static int BenchTime(CrsAlgorithm a, int nRounds, int cbData)
{ {
byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 }; byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 };
int nStart = Environment.TickCount; int tStart = Environment.TickCount;
for(int i = 0; i < nRounds; ++i) for(int i = 0; i < nRounds; ++i)
{ {
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey)) using(CryptoRandomStream crs = new CryptoRandomStream(a, pbKey))
{ {
c.GetRandomBytes((uint)nDataSize); crs.GetRandomBytes((uint)cbData);
} }
} }
int nEnd = Environment.TickCount;
return (nEnd - nStart); return (Environment.TickCount - tStart);
} }
#endif #endif
} }

View File

@@ -1,65 +0,0 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Diagnostics;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.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());
char[] vGenerated = new char[pwProfile.Length];
PwGenerator.PrepareCharSet(pcs, pwProfile);
for(int nIndex = 0; nIndex < (int)pwProfile.Length; ++nIndex)
{
char ch = PwGenerator.GenerateCharacter(pwProfile, pcs,
crsRandomSource);
if(ch == char.MinValue)
{
MemUtil.ZeroArray<char>(vGenerated);
return PwgError.TooFewCharacters;
}
vGenerated[nIndex] = ch;
}
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated);
psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8);
MemUtil.ZeroArray<char>(vGenerated);
return PwgError.Success;
}
}
}

View File

@@ -1,173 +0,0 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 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.Diagnostics;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.PasswordGenerator
{
internal static class PatternBasedGenerator
{
internal static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
{
psOut = ProtectedString.Empty;
LinkedList<char> vGenerated = 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
{
vGenerated.AddLast('\\');
break;
}
if(bInCharSetDef) pcsCustom.Add(ch);
else
{
vGenerated.AddLast(ch);
pcsUsed.Add(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)
{
vGenerated.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;
vGenerated.AddLast(chGen);
pcsUsed.Add(chGen);
}
ch = csStream.ReadChar();
}
if(vGenerated.Count == 0) return PwgError.Success;
char[] vArray = new char[vGenerated.Count];
vGenerated.CopyTo(vArray, 0);
if(pwProfile.PatternPermutePassword)
PwGenerator.ShufflePassword(vArray, crsRandomSource);
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vArray);
psOut = new ProtectedString(true, pbUtf8);
MemUtil.ZeroByteArray(pbUtf8);
MemUtil.ZeroArray<char>(vArray);
vGenerated.Clear();
return PwgError.Success;
}
private static string ExpandPattern(string strPattern)
{
Debug.Assert(strPattern != null); if(strPattern == null) 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;
}
}
}

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,122 +19,81 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography.PasswordGenerator namespace KeePassLib.Cryptography.PasswordGenerator
{ {
public sealed class PwCharSet public sealed class PwCharSet : IEquatable<PwCharSet>
{ {
public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static readonly string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public const string LowerCase = "abcdefghijklmnopqrstuvwxyz"; public static readonly string LowerCase = "abcdefghijklmnopqrstuvwxyz";
public const string Digits = "0123456789"; public static readonly string Digits = "0123456789";
public const string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ"; public static readonly string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
public const string LowerConsonants = "bcdfghjklmnpqrstvwxyz"; public static readonly string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
public const string UpperVowels = "AEIOU"; public static readonly string UpperVowels = "AEIOU";
public const string LowerVowels = "aeiou"; public static readonly string LowerVowels = "aeiou";
public const string Punctuation = @",.;:"; public static readonly string Punctuation = ",.;:";
public const string Brackets = @"[]{}()<>"; public static readonly string Brackets = @"[]{}()<>";
public const string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; public static readonly string Special = "!\"#$%&'*+,./:;=?@\\^`|~";
public static readonly string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
public const string UpperHex = "0123456789ABCDEF"; public static readonly string UpperHex = "0123456789ABCDEF";
public const string LowerHex = "0123456789abcdef"; public static readonly string LowerHex = "0123456789abcdef";
public const string Invalid = "\t\r\n"; public static readonly string LookAlike = "O0Il1|";
public const string LookAlike = @"O0l1I|";
internal const 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> /// <summary>
/// Create a new, empty character set collection object. /// Latin-1 Supplement except U+00A0 (NBSP) and U+00AD (SHY).
/// </summary>
public static readonly string Latin1S =
"\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7" +
"\u00A8\u00A9\u00AA\u00AB\u00AC\u00AE\u00AF" +
"\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7" +
"\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF" +
"\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7" +
"\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF" +
"\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7" +
"\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF" +
"\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7" +
"\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF" +
"\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7" +
"\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF";
// internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
[Obsolete]
public static string SpecialChars { get { return PwCharSet.Special; } }
[Obsolete]
public static string HighAnsiChars { get { return PwCharSet.Latin1S; } }
private readonly List<char> m_lChars = new List<char>();
private readonly byte[] m_vTab = new byte[0x10000 / 8];
/// <summary>
/// Create a new, empty character set.
/// </summary> /// </summary>
public PwCharSet() public PwCharSet()
{ {
Initialize(true); Debug.Assert(PwCharSet.Latin1S.Length == (16 * 6 - 2));
} }
public PwCharSet(string strCharSet) public PwCharSet(string strCharSet)
{ {
Initialize(true);
Add(strCharSet); 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> /// <summary>
/// Number of characters in this set. /// Number of characters in this set.
/// </summary> /// </summary>
public uint Size public uint Size
{ {
get { return (uint)m_vChars.Count; } get { return (uint)m_lChars.Count; }
} }
/// <summary> /// <summary>
@@ -147,19 +106,39 @@ namespace KeePassLib.Cryptography.PasswordGenerator
{ {
get get
{ {
if(uPos >= (uint)m_vChars.Count) if (uPos >= (uint)m_lChars.Count)
throw new ArgumentOutOfRangeException("uPos"); throw new ArgumentOutOfRangeException("uPos");
return m_vChars[(int)uPos]; return m_lChars[(int)uPos];
} }
} }
public bool Equals(PwCharSet other)
{
if (object.ReferenceEquals(other, this)) return true;
if (object.ReferenceEquals(other, null)) return false;
if (m_lChars.Count != other.m_lChars.Count) return false;
return MemUtil.ArraysEqual(m_vTab, other.m_vTab);
}
public override bool Equals(object obj)
{
return Equals(obj as PwCharSet);
}
public override int GetHashCode()
{
return (int)MemUtil.Hash32(m_vTab, 0, m_vTab.Length);
}
/// <summary> /// <summary>
/// Remove all characters from this set. /// Remove all characters from this set.
/// </summary> /// </summary>
public void Clear() public void Clear()
{ {
m_vChars.Clear(); m_lChars.Clear();
Array.Clear(m_vTab, 0, m_vTab.Length); Array.Clear(m_vTab, 0, m_vTab.Length);
} }
@@ -191,7 +170,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator
if (!Contains(ch)) if (!Contains(ch))
{ {
m_vChars.Add(ch); m_lChars.Add(ch);
m_vTab[ch / 8] |= (byte)(1 << (ch % 8)); m_vTab[ch / 8] |= (byte)(1 << (ch % 8));
} }
} }
@@ -205,8 +184,6 @@ namespace KeePassLib.Cryptography.PasswordGenerator
Debug.Assert(strCharSet != null); Debug.Assert(strCharSet != null);
if (strCharSet == null) throw new ArgumentNullException("strCharSet"); if (strCharSet == null) throw new ArgumentNullException("strCharSet");
m_vChars.Capacity = m_vChars.Count + strCharSet.Length;
foreach (char ch in strCharSet) foreach (char ch in strCharSet)
Add(ch); Add(ch);
} }
@@ -226,8 +203,6 @@ namespace KeePassLib.Cryptography.PasswordGenerator
public void AddRange(char chMin, char chMax) public void AddRange(char chMin, char chMax)
{ {
m_vChars.Capacity = m_vChars.Count + (chMax - chMin) + 1;
for (char ch = chMin; ch < chMax; ++ch) for (char ch = chMin; ch < chMax; ++ch)
Add(ch); Add(ch);
@@ -241,11 +216,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator
switch (chCharSetIdentifier) switch (chCharSetIdentifier)
{ {
case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break; case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break;
case 'A': Add(PwCharSet.LowerCase, PwCharSet.UpperCase, case 'A':
Add(PwCharSet.LowerCase, PwCharSet.UpperCase,
PwCharSet.Digits); break; PwCharSet.Digits); break;
case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break; case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break;
case 'c': Add(PwCharSet.LowerConsonants); break; case 'c': Add(PwCharSet.LowerConsonants); break;
case 'C': Add(PwCharSet.LowerConsonants, case 'C':
Add(PwCharSet.LowerConsonants,
PwCharSet.UpperConsonants); break; PwCharSet.UpperConsonants); break;
case 'z': Add(PwCharSet.UpperConsonants); break; case 'z': Add(PwCharSet.UpperConsonants); break;
case 'd': Add(PwCharSet.Digits); break; // Digit case 'd': Add(PwCharSet.Digits); break; // Digit
@@ -257,12 +234,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator
case 'p': Add(PwCharSet.Punctuation); break; case 'p': Add(PwCharSet.Punctuation); break;
case 'b': Add(PwCharSet.Brackets); break; case 'b': Add(PwCharSet.Brackets); break;
case 's': Add(PwCharSet.PrintableAsciiSpecial); break; case 's': Add(PwCharSet.PrintableAsciiSpecial); break;
case 'S': Add(PwCharSet.UpperCase, PwCharSet.LowerCase); case 'S':
Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break; Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break;
case 'v': Add(PwCharSet.LowerVowels); break; case 'v': Add(PwCharSet.LowerVowels); break;
case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break; case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break;
case 'Z': Add(PwCharSet.UpperVowels); break; case 'Z': Add(PwCharSet.UpperVowels); break;
case 'x': Add(m_strHighAnsi); break; case 'x': Add(PwCharSet.Latin1S); break;
default: bResult = false; break; default: bResult = false; break;
} }
@@ -272,7 +250,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator
public bool Remove(char ch) public bool Remove(char ch)
{ {
m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8))); m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8)));
return m_vChars.Remove(ch); return m_lChars.Remove(ch);
} }
public bool Remove(string strCharacters) public bool Remove(string strCharacters)
@@ -306,8 +284,8 @@ namespace KeePassLib.Cryptography.PasswordGenerator
/// <returns>String containing all character set characters.</returns> /// <returns>String containing all character set characters.</returns>
public override string ToString() public override string ToString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder(m_lChars.Count);
foreach(char ch in m_vChars) foreach (char ch in m_lChars)
sb.Append(ch); sb.Append(ch);
return sb.ToString(); return sb.ToString();
@@ -320,13 +298,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator
sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_'); sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_'); sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_'); sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_');
sb.Append(RemoveIfAllExist(m_strSpecial) ? 'S' : '_'); sb.Append(RemoveIfAllExist(PwCharSet.Special) ? 'S' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_'); sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_');
sb.Append(RemoveIfAllExist(@"-") ? 'm' : '_'); sb.Append(RemoveIfAllExist("-") ? 'm' : '_');
sb.Append(RemoveIfAllExist(@"_") ? 'u' : '_'); sb.Append(RemoveIfAllExist("_") ? 'u' : '_');
sb.Append(RemoveIfAllExist(@" ") ? 's' : '_'); sb.Append(RemoveIfAllExist(" ") ? 's' : '_');
sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_'); sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_');
sb.Append(RemoveIfAllExist(m_strHighAnsi) ? 'H' : '_'); sb.Append(RemoveIfAllExist(PwCharSet.Latin1S) ? 'H' : '_');
return sb.ToString(); return sb.ToString();
} }
@@ -339,13 +317,13 @@ namespace KeePassLib.Cryptography.PasswordGenerator
if (strRanges[0] != '_') Add(PwCharSet.UpperCase); if (strRanges[0] != '_') Add(PwCharSet.UpperCase);
if (strRanges[1] != '_') Add(PwCharSet.LowerCase); if (strRanges[1] != '_') Add(PwCharSet.LowerCase);
if (strRanges[2] != '_') Add(PwCharSet.Digits); if (strRanges[2] != '_') Add(PwCharSet.Digits);
if(strRanges[3] != '_') Add(m_strSpecial); if (strRanges[3] != '_') Add(PwCharSet.Special);
if (strRanges[4] != '_') Add(PwCharSet.Punctuation); if (strRanges[4] != '_') Add(PwCharSet.Punctuation);
if (strRanges[5] != '_') Add('-'); if (strRanges[5] != '_') Add('-');
if (strRanges[6] != '_') Add('_'); if (strRanges[6] != '_') Add('_');
if (strRanges[7] != '_') Add(' '); if (strRanges[7] != '_') Add(' ');
if (strRanges[8] != '_') Add(PwCharSet.Brackets); if (strRanges[8] != '_') Add(PwCharSet.Brackets);
if(strRanges[9] != '_') Add(m_strHighAnsi); if (strRanges[9] != '_') Add(PwCharSet.Latin1S);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,9 +20,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Security.Cryptography;
using System.Text; using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Resources;
using KeePassLib.Security; using KeePassLib.Security;
using KeePassLib.Utility; using KeePassLib.Utility;
@@ -33,95 +37,78 @@ namespace KeePassLib.Cryptography.PasswordGenerator
Success = 0, Success = 0,
Unknown = 1, Unknown = 1,
TooFewCharacters = 2, TooFewCharacters = 2,
UnknownAlgorithm = 3 UnknownAlgorithm = 3,
InvalidCharSet = 4,
InvalidPattern = 5
} }
/// <summary> /// <summary>
/// Utility functions for generating random passwords. /// Password generator.
/// </summary> /// </summary>
public static class PwGenerator public static class PwGenerator
{ {
public static PwgError Generate(out ProtectedString psOut,
PwProfile pwProfile, byte[] pbUserEntropy, private static CryptoRandomStream CreateRandomStream(byte[] pbAdditionalEntropy,
CustomPwGeneratorPool pwAlgorithmPool) out byte[] pbKey)
{ {
Debug.Assert(pwProfile != null); pbKey = CryptoRandom.Instance.GetRandomBytes(128);
if (pwProfile == null) throw new ArgumentNullException("pwProfile");
CryptoRandomStream crs = CreateCryptoStream(pbUserEntropy);
PwgError e = PwgError.Unknown;
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; }
return e;
}
private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy)
{
byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(128);
// Mix in additional entropy // Mix in additional entropy
Debug.Assert(pbKey.Length >= 64); Debug.Assert(pbKey.Length >= 64);
if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0)) if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length != 0))
{ {
using (SHA512Managed h = new SHA512Managed()) using (SHA512Managed h = new SHA512Managed())
{ {
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy); byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length); MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
MemUtil.ZeroByteArray(pbHash);
} }
} }
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey); return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
} }
internal static char GenerateCharacter(PwProfile pwProfile, internal static char GenerateCharacter(PwCharSet pwCharSet,
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource) CryptoRandomStream crsRandomSource)
{ {
if (pwCharSet.Size == 0) return char.MinValue; uint cc = pwCharSet.Size;
if (cc == 0) return char.MinValue;
ulong uIndex = crsRandomSource.GetRandomUInt64(); uint i = (uint)crsRandomSource.GetRandomUInt64(cc);
uIndex %= (ulong)pwCharSet.Size; return pwCharSet[i];
char ch = pwCharSet[(uint)uIndex];
if (pwProfile.NoRepeatingCharacters)
pwCharSet.Remove(ch);
return ch;
} }
internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile) internal static bool PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
{ {
pwCharSet.Remove(PwCharSet.Invalid); uint cc = pwCharSet.Size;
for (uint i = 0; i < cc; ++i)
{
char ch = pwCharSet[i];
if ((ch == char.MinValue) || (ch == '\t') || (ch == '\r') ||
(ch == '\n') || char.IsSurrogate(ch))
return false;
}
if (pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike); if (pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
if (pwProfile.ExcludeCharacters.Length > 0) if (!string.IsNullOrEmpty(pwProfile.ExcludeCharacters))
pwCharSet.Remove(pwProfile.ExcludeCharacters); pwCharSet.Remove(pwProfile.ExcludeCharacters);
return true;
} }
internal static void ShufflePassword(char[] pPassword, internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource)
CryptoRandomStream crsRandomSource)
{ {
Debug.Assert(pPassword != null); if (pPassword == null) return; if (v == null) { Debug.Assert(false); return; }
Debug.Assert(crsRandomSource != null); if (crsRandomSource == null) return; if (crsRandomSource == null) { Debug.Assert(false); return; }
if (pPassword.Length <= 1) return; // Nothing to shuffle for (int i = v.Length - 1; i >= 1; --i)
for (int nSelect = 0; nSelect < pPassword.Length; ++nSelect)
{ {
ulong uRandomIndex = crsRandomSource.GetRandomUInt64(); int j = (int)crsRandomSource.GetRandomUInt64((ulong)(i + 1));
uRandomIndex %= (ulong)(pPassword.Length - nSelect);
char chTemp = pPassword[nSelect]; char t = v[i];
pPassword[nSelect] = pPassword[nSelect + (int)uRandomIndex]; v[i] = v[j];
pPassword[nSelect + (int)uRandomIndex] = chTemp; v[j] = t;
} }
} }
@@ -135,7 +122,7 @@ namespace KeePassLib.Cryptography.PasswordGenerator
if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm; if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
string strID = pwProfile.CustomAlgorithmUuid; string strID = pwProfile.CustomAlgorithmUuid;
if (string.IsNullOrEmpty(strID)) { Debug.Assert(false); return PwgError.UnknownAlgorithm; } if (string.IsNullOrEmpty(strID)) return PwgError.UnknownAlgorithm;
byte[] pbUuid = Convert.FromBase64String(strID); byte[] pbUuid = Convert.FromBase64String(strID);
PwUuid uuid = new PwUuid(pbUuid); PwUuid uuid = new PwUuid(pbUuid);
@@ -148,5 +135,57 @@ namespace KeePassLib.Cryptography.PasswordGenerator
psOut = pwd; psOut = pwd;
return PwgError.Success; return PwgError.Success;
} }
internal static string ErrorToString(PwgError e, bool bHeader)
{
if (e == PwgError.Success) { Debug.Assert(false); return string.Empty; }
if ((e == PwgError.Unknown) && bHeader) return KLRes.PwGenFailed;
string str = KLRes.UnknownError;
switch (e)
{
// case PwgError.Success:
// break;
case PwgError.Unknown:
break;
case PwgError.TooFewCharacters:
str = KLRes.CharSetTooFewChars;
break;
case PwgError.UnknownAlgorithm:
str = KLRes.AlgorithmUnknown;
break;
case PwgError.InvalidCharSet:
str = KLRes.CharSetInvalid;
break;
case PwgError.InvalidPattern:
str = KLRes.PatternInvalid;
break;
default:
Debug.Assert(false);
break;
}
if (bHeader)
str = KLRes.PwGenFailed + MessageService.NewParagraph + str;
return str;
}
internal static string ErrorToString(Exception ex, bool bHeader)
{
string str = ((ex == null) ? KLRes.UnknownError :
StrUtil.FormatException(ex));
if (bHeader)
str = KLRes.PwGenFailed + MessageService.NewParagraph + str;
return str;
}
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using KeePassLib.Utility; using KeePassLib.Utility;
@@ -28,15 +28,17 @@ namespace KeePassLib.Cryptography
{ {
public static class PopularPasswords public static class PopularPasswords
{ {
private static Dictionary<int, Dictionary<string, bool>> m_dicts = private static readonly Dictionary<int, Dictionary<char[], bool>> g_dicts =
new Dictionary<int, Dictionary<string, bool>>(); new Dictionary<int, Dictionary<char[], bool>>();
internal static int MaxLength internal static int MaxLength
{ {
get get
{ {
Debug.Assert(g_dicts.Count > 0); // Should be initialized
int iMaxLen = 0; int iMaxLen = 0;
foreach(int iLen in m_dicts.Keys) foreach (int iLen in g_dicts.Keys)
{ {
if (iLen > iMaxLen) iMaxLen = iLen; if (iLen > iMaxLen) iMaxLen = iLen;
} }
@@ -47,8 +49,8 @@ namespace KeePassLib.Cryptography
internal static bool ContainsLength(int nLength) internal static bool ContainsLength(int nLength)
{ {
Dictionary<string, bool> dDummy; Dictionary<char[], bool> dDummy;
return m_dicts.TryGetValue(nLength, out dDummy); return g_dicts.TryGetValue(nLength, out dDummy);
} }
public static bool IsPopularPassword(char[] vPassword) public static bool IsPopularPassword(char[] vPassword)
@@ -62,28 +64,30 @@ namespace KeePassLib.Cryptography
if (vPassword == null) throw new ArgumentNullException("vPassword"); if (vPassword == null) throw new ArgumentNullException("vPassword");
if (vPassword.Length == 0) { uDictSize = 0; return false; } if (vPassword.Length == 0) { uDictSize = 0; return false; }
string str = new string(vPassword); #if DEBUG
Array.ForEach(vPassword, ch => Debug.Assert(ch == char.ToLower(ch)));
#endif
try { return IsPopularPasswordPriv(str, out uDictSize); } try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
catch (Exception) { Debug.Assert(false); } catch (Exception) { Debug.Assert(false); }
uDictSize = 0; uDictSize = 0;
return false; return false;
} }
private static bool IsPopularPasswordPriv(string str, out ulong uDictSize) private static bool IsPopularPasswordPriv(char[] vPassword, out ulong uDictSize)
{ {
Debug.Assert(m_dicts.Count > 0); // Should be initialized with data Debug.Assert(g_dicts.Count > 0); // Should be initialized with data
Dictionary<string, bool> d; Dictionary<char[], bool> d;
if(!m_dicts.TryGetValue(str.Length, out d)) if (!g_dicts.TryGetValue(vPassword.Length, out d))
{ {
uDictSize = 0; uDictSize = 0;
return false; return false;
} }
uDictSize = (ulong)d.Count; uDictSize = (ulong)d.Count;
return d.ContainsKey(str); return d.ContainsKey(vPassword);
} }
public static void Add(byte[] pbData, bool bGZipped) public static void Add(byte[] pbData, bool bGZipped)
@@ -96,30 +100,27 @@ namespace KeePassLib.Cryptography
string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length); string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length);
if (string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; } if (string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
if(!char.IsWhiteSpace(strData[strData.Length - 1]))
strData += "\n";
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for(int i = 0; i < strData.Length; ++i) for (int i = 0; i <= strData.Length; ++i)
{ {
char ch = strData[i]; char ch = ((i == strData.Length) ? ' ' : strData[i]);
if (char.IsWhiteSpace(ch)) if (char.IsWhiteSpace(ch))
{ {
int cc = sb.Length; int cc = sb.Length;
if (cc > 0) if (cc > 0)
{ {
string strWord = sb.ToString(); char[] vWord = new char[cc];
Debug.Assert(strWord.Length == cc); sb.CopyTo(0, vWord, 0, cc);
Dictionary<string, bool> d; Dictionary<char[], bool> d;
if(!m_dicts.TryGetValue(cc, out d)) if (!g_dicts.TryGetValue(cc, out d))
{ {
d = new Dictionary<string, bool>(); d = new Dictionary<char[], bool>(MemUtil.ArrayHelperExOfChar);
m_dicts[cc] = d; g_dicts[cc] = d;
} }
d[strWord] = true; d[vWord] = true;
sb.Remove(0, cc); sb.Remove(0, cc);
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using KeePassLib.Cryptography.PasswordGenerator; using KeePassLib.Cryptography.PasswordGenerator;
using KeePassLib.Utility; using KeePassLib.Utility;
@@ -35,19 +35,19 @@ namespace KeePassLib.Cryptography
{ {
private static class PatternID private static class PatternID
{ {
public const char LowerAlpha = 'L'; internal const char LowerAlpha = 'L';
public const char UpperAlpha = 'U'; internal const char UpperAlpha = 'U';
public const char Digit = 'D'; internal const char Digit = 'D';
public const char Special = 'S'; internal const char Special = 'S';
public const char High = 'H'; internal const char Latin1S = 'H';
public const char Other = 'X'; internal const char Other = 'X';
public const char Dictionary = 'W'; internal const char Dictionary = 'W';
public const char Repetition = 'R'; internal const char Repetition = 'R';
public const char Number = 'N'; internal const char Number = 'N';
public const char DiffSeq = 'C'; internal const char DiffSeq = 'C';
public const string All = "LUDSHXWRNC"; internal const string All = "LUDSHXWRNC";
} }
// private static class CharDistrib // private static class CharDistrib
@@ -125,7 +125,7 @@ namespace KeePassLib.Cryptography
private sealed class EntropyEncoder private sealed class EntropyEncoder
{ {
private readonly string m_strAlph; private readonly string m_strAlph;
private Dictionary<char, ulong> m_dHisto = new Dictionary<char, ulong>(); private readonly Dictionary<char, ulong> m_dHisto = new Dictionary<char, ulong>();
private readonly ulong m_uBaseWeight; private readonly ulong m_uBaseWeight;
private readonly ulong m_uCharWeight; private readonly ulong m_uCharWeight;
private readonly ulong m_uOccExclThreshold; private readonly ulong m_uOccExclThreshold;
@@ -189,7 +189,7 @@ namespace KeePassLib.Cryptography
private sealed class MultiEntropyEncoder private sealed class MultiEntropyEncoder
{ {
private Dictionary<char, EntropyEncoder> m_dEncs = private readonly Dictionary<char, EntropyEncoder> m_dEncs =
new Dictionary<char, EntropyEncoder>(); new Dictionary<char, EntropyEncoder>();
public MultiEntropyEncoder() public MultiEntropyEncoder()
@@ -281,7 +281,7 @@ namespace KeePassLib.Cryptography
} }
} }
private static object m_objSyncInit = new object(); private static readonly object m_objSyncInit = new object();
private static List<QeCharType> m_lCharTypes = null; private static List<QeCharType> m_lCharTypes = null;
private static void EnsureInitialized() private static void EnsureInitialized()
@@ -292,25 +292,20 @@ namespace KeePassLib.Cryptography
{ {
string strSpecial = PwCharSet.PrintableAsciiSpecial; string strSpecial = PwCharSet.PrintableAsciiSpecial;
if (strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); } if (strSpecial.IndexOf(' ') >= 0) { Debug.Assert(false); }
else strSpecial = strSpecial + " "; else strSpecial += " ";
int nSp = strSpecial.Length; int nSp = strSpecial.Length;
int nHi = PwCharSet.HighAnsiChars.Length; int nL1S = PwCharSet.Latin1S.Length;
m_lCharTypes = new List<QeCharType>(); m_lCharTypes = new List<QeCharType>()
{
m_lCharTypes.Add(new QeCharType(PatternID.LowerAlpha, new QeCharType(PatternID.LowerAlpha, PwCharSet.LowerCase, true),
PwCharSet.LowerCase, true)); new QeCharType(PatternID.UpperAlpha, PwCharSet.UpperCase, true),
m_lCharTypes.Add(new QeCharType(PatternID.UpperAlpha, new QeCharType(PatternID.Digit, PwCharSet.Digits, true),
PwCharSet.UpperCase, true)); new QeCharType(PatternID.Special, strSpecial, false),
m_lCharTypes.Add(new QeCharType(PatternID.Digit, new QeCharType(PatternID.Latin1S, PwCharSet.Latin1S, false),
PwCharSet.Digits, true)); new QeCharType(PatternID.Other, 0x10000 - (2 * 26) - 10 - nSp - nL1S)
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));
} }
} }
} }
@@ -318,30 +313,30 @@ namespace KeePassLib.Cryptography
/// <summary> /// <summary>
/// Estimate the quality of a password. /// Estimate the quality of a password.
/// </summary> /// </summary>
/// <param name="vPasswordChars">Password to check.</param> /// <param name="vPassword">Password to check.</param>
/// <returns>Estimated bit-strength of the password.</returns> /// <returns>Estimated bit-strength of the password.</returns>
public static uint EstimatePasswordBits(char[] vPasswordChars) public static uint EstimatePasswordBits(char[] vPassword)
{ {
if(vPasswordChars == null) { Debug.Assert(false); return 0; } if (vPassword == null) { Debug.Assert(false); return 0; }
if(vPasswordChars.Length == 0) return 0; if (vPassword.Length == 0) return 0;
EnsureInitialized(); EnsureInitialized();
int n = vPasswordChars.Length; int n = vPassword.Length;
List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n]; List<QePatternInstance>[] vPatterns = new List<QePatternInstance>[n];
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i)
{ {
vPatterns[i] = new List<QePatternInstance>(); vPatterns[i] = new List<QePatternInstance>();
QePatternInstance piChar = new QePatternInstance(i, 1, QePatternInstance piChar = new QePatternInstance(i, 1,
GetCharType(vPasswordChars[i])); GetCharType(vPassword[i]));
vPatterns[i].Add(piChar); vPatterns[i].Add(piChar);
} }
FindRepetitions(vPasswordChars, vPatterns); FindRepetitions(vPassword, vPatterns);
FindNumbers(vPasswordChars, vPatterns); FindNumbers(vPassword, vPatterns);
FindDiffSeqs(vPasswordChars, vPatterns); FindDiffSeqs(vPassword, vPatterns);
FindPopularPasswords(vPasswordChars, vPatterns); FindPopularPasswords(vPassword, vPatterns);
// Encoders must not be static, because the entropy estimation // Encoders must not be static, because the entropy estimation
// may run concurrently in multiple threads and the encoders are // may run concurrently in multiple threads and the encoders are
@@ -382,7 +377,7 @@ namespace KeePassLib.Cryptography
{ {
Debug.Assert(s.Position == n); Debug.Assert(s.Position == n);
double dblCost = ComputePathCost(s.Path, vPasswordChars, double dblCost = ComputePathCost(s.Path, vPassword,
ecPattern, mcData); ecPattern, mcData);
if (dblCost < dblMinCost) dblMinCost = dblCost; if (dblCost < dblMinCost) dblMinCost = dblCost;
} }
@@ -420,11 +415,12 @@ namespace KeePassLib.Cryptography
{ {
if (pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; } if (pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8); char[] v = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
uint uResult = EstimatePasswordBits(vChars); uint r;
MemUtil.ZeroArray<char>(vChars); try { r = EstimatePasswordBits(v); }
finally { MemUtil.ZeroArray<char>(v); }
return uResult; return r;
} }
private static QeCharType GetCharType(char ch) private static QeCharType GetCharType(char ch)
@@ -482,7 +478,7 @@ namespace KeePassLib.Cryptography
vLeet[i] = char.ToLower(DecodeLeetChar(ch)); vLeet[i] = char.ToLower(DecodeLeetChar(ch));
} }
char chErased = default(char); char chErased = default(char); // The value that Array.Clear uses
Debug.Assert(chErased == char.MinValue); Debug.Assert(chErased == char.MinValue);
int nMaxLen = Math.Min(n, PopularPasswords.MaxLength); int nMaxLen = Math.Min(n, PopularPasswords.MaxLength);
@@ -515,7 +511,12 @@ namespace KeePassLib.Cryptography
Debug.Assert(vLower[i] == chErased); Debug.Assert(vLower[i] == chErased);
} }
} }
MemUtil.ZeroArray<char>(vSub);
} }
MemUtil.ZeroArray<char>(vLower);
MemUtil.ZeroArray<char>(vLeet);
} }
private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns, private static bool EvalAddPopularPasswordPattern(List<QePatternInstance>[] vPatterns,
@@ -659,6 +660,8 @@ namespace KeePassLib.Cryptography
if (bFoundRep) ErasePart(v, x1, m, ref chErased); if (bFoundRep) ErasePart(v, x1, m, ref chErased);
} }
} }
MemUtil.ZeroArray<char>(v);
} }
private static bool PartsEqual(char[] v, int x1, int x2, int nLength) private static bool PartsEqual(char[] v, int x1, int x2, int nLength)
@@ -685,23 +688,25 @@ namespace KeePassLib.Cryptography
{ {
int n = vPassword.Length; int n = vPassword.Length;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i)
{ {
char ch = vPassword[i]; char ch = vPassword[i];
if ((ch >= '0') && (ch <= '9')) sb.Append(ch); if ((ch >= '0') && (ch <= '9')) sb.Append(ch);
else else
{ {
AddNumberPattern(vPatterns, sb.ToString(), i - sb.Length); AddNumberPattern(vPatterns, sb, i - sb.Length);
sb.Remove(0, sb.Length); sb.Remove(0, sb.Length);
} }
} }
AddNumberPattern(vPatterns, sb.ToString(), n - sb.Length); AddNumberPattern(vPatterns, sb, n - sb.Length);
} }
private static void AddNumberPattern(List<QePatternInstance>[] vPatterns, private static void AddNumberPattern(List<QePatternInstance>[] vPatterns,
string strNumber, int i) StringBuilder sb, int i)
{ {
if(strNumber.Length <= 2) return; if (sb.Length <= 2) return;
string strNumber = sb.ToString();
int nZeros = 0; int nZeros = 0;
for (int j = 0; j < strNumber.Length; ++j) for (int j = 0; j < strNumber.Length; ++j)
@@ -733,17 +738,18 @@ namespace KeePassLib.Cryptography
private static void FindDiffSeqs(char[] vPassword, private static void FindDiffSeqs(char[] vPassword,
List<QePatternInstance>[] vPatterns) List<QePatternInstance>[] vPatterns)
{ {
int d = int.MinValue, p = 0; int n = vPassword.Length;
string str = new string(vPassword) + new string(char.MaxValue, 1); int d = int.MaxValue, p = 0;
for(int i = 1; i < str.Length; ++i) for (int i = 1; i <= n; ++i)
{ {
int dCur = (int)str[i] - (int)str[i - 1]; int dCur = ((i == n) ? int.MinValue :
((int)vPassword[i] - (int)vPassword[i - 1]));
if (dCur != d) if (dCur != d)
{ {
if ((i - p) >= 3) // At least 3 chars involved if ((i - p) >= 3) // At least 3 chars involved
{ {
QeCharType ct = GetCharType(str[p]); QeCharType ct = GetCharType(vPassword[p]);
double dblCost = ct.CharSize + Log2(i - p - 1); double dblCost = ct.CharSize + Log2(i - p - 1);
vPatterns[p].Add(new QePatternInstance(p, vPatterns[p].Add(new QePatternInstance(p,

View File

@@ -46,4 +46,12 @@ namespace KeePassLib.Delegates
public delegate void VoidDelegate(); public delegate void VoidDelegate();
public delegate string StrPwEntryDelegate(string str, PwEntry pe); public delegate string StrPwEntryDelegate(string str, PwEntry pe);
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);
} }

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
#if KeePassLibSD #if KeePassLibSD
@@ -30,81 +31,26 @@ using KeePassLibSD;
using System.IO.Compression; using System.IO.Compression;
#endif #endif
using KeePassLib.Delegates;
namespace KeePassLib.Utility namespace KeePassLib.Utility
{ {
/// <summary> /// <summary>
/// Contains static buffer manipulation and string conversion routines. /// Buffer manipulation and conversion routines.
/// </summary> /// </summary>
public static class MemUtil public static class MemUtil
{ {
public static readonly byte[] EmptyByteArray = new byte[0]; public static readonly byte[] EmptyByteArray = new byte[0];
private static readonly uint[] m_vSBox = new uint[256] { internal static readonly ArrayHelperEx<char> ArrayHelperExOfChar =
0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230, new ArrayHelperEx<char>();
0x3A321712, 0x06403DB1, 0xD2F84B95, 0xDF22A6E4,
0x07CE9E5B, 0x31788A0C, 0xF683F6F4, 0xEA061F49, private const MethodImplOptions MioNoOptimize =
0xFA5C2ACA, 0x4B9E494E, 0xB0AB25BA, 0x767731FC, #if KeePassLibSD
0x261893A7, 0x2B09F2CE, 0x046261E4, 0x41367B4B, MethodImplOptions.NoInlining;
0x18A7F225, 0x8F923C0E, 0x5EF3A325, 0x28D0435E, #else
0x84C22919, 0xED66873C, 0x8CEDE444, 0x7FC47C24, (MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining);
0xFCFC6BA3, 0x676F928D, 0xB4147187, 0xD8FB126E, #endif
0x7D798D17, 0xFF82E424, 0x1712FA5B, 0xABB09DD5,
0x8156BA63, 0x84E4D969, 0xC937FB9A, 0x2F1E5BFC,
0x178ECA11, 0x0E71CD5F, 0x52AAC6F4, 0x71EEFC8F,
0x7090D749, 0x21CACA31, 0x92996378, 0x0939A8A8,
0xE9EE1934, 0xD2718616, 0xF2500543, 0xB911873C,
0xD3CB3EEC, 0x2BA0DBEB, 0xB42D0A27, 0xECE67C0F,
0x302925F0, 0x6114F839, 0xD39E6307, 0xE28970D6,
0xEB982F99, 0x941B4CDF, 0xC540E550, 0x8124FC45,
0x98B025C7, 0xE2BF90EA, 0x4F57C976, 0xCF546FE4,
0x59566DC8, 0xE3F4360D, 0xF5F9D231, 0xD6180B22,
0xB54E088A, 0xB5DFE6A6, 0x3637A36F, 0x056E9284,
0xAFF8FBC5, 0x19E01648, 0x8611F043, 0xDAE44337,
0xF61B6A1C, 0x257ACD9E, 0xDD35F507, 0xEF05CAFA,
0x05EB4A83, 0xFC25CA92, 0x0A4728E6, 0x9CF150EF,
0xAEEF67DE, 0xA9472337, 0x57C81EFE, 0x3E5E009F,
0x02CB03BB, 0x2BA85674, 0xF21DC251, 0x78C34A34,
0xABB1F5BF, 0xB95A2FBD, 0x1FB47777, 0x9A96E8AC,
0x5D2D2838, 0x55AAC92A, 0x99EE324E, 0x10F6214B,
0x58ABDFB1, 0x2008794D, 0xBEC880F0, 0xE75E5341,
0x88015C34, 0x352D8FBF, 0x622B7F6C, 0xF5C59EA2,
0x1F759D8E, 0xADE56159, 0xCC7B4C25, 0x5B8BC48C,
0xB6BD15AF, 0x3C5B5110, 0xE74A7C3D, 0xEE613161,
0x156A1C67, 0x72C06817, 0xEA0A6F69, 0x4CECF993,
0xCA9D554C, 0x8E20361F, 0x42D396B9, 0x595DE578,
0x749D7955, 0xFD1BA5FD, 0x81FC160E, 0xDB97E28C,
0x7CF148F7, 0x0B0B3CF5, 0x534DE605, 0x46421066,
0xD4B68DD1, 0x9E479CE6, 0xAE667A9D, 0xBC082082,
0xB06DD6EF, 0x20F0F23F, 0xB99E1551, 0xF47A2E3A,
0x71DA50C6, 0x67B65779, 0x2A8CB376, 0x1EA71EEE,
0x29ABCD50, 0xB6EB0C6B, 0x23C10511, 0x6F3F2144,
0x6AF23012, 0xF696BD9E, 0xB94099D8, 0xAD5A9C81,
0x7A0794FA, 0x7EDF59D6, 0x1E72E574, 0x8561913C,
0x4E4D568F, 0xEECB9928, 0x9C124D2E, 0x0848B82C,
0xF1CA395F, 0x9DAF43DC, 0xF77EC323, 0x394E9B59,
0x7E200946, 0x8B811D68, 0x16DA3305, 0xAB8DE2C3,
0xE6C53B64, 0x98C2D321, 0x88A97D81, 0xA7106419,
0x8E52F7BF, 0x8ED262AF, 0x7CCA974E, 0xF0933241,
0x040DD437, 0xE143B3D4, 0x3019F56F, 0xB741521D,
0xF1745362, 0x4C435F9F, 0xB4214D0D, 0x0B0C348B,
0x5051D189, 0x4C30447E, 0x7393D722, 0x95CEDD0B,
0xDD994E80, 0xC3D22ED9, 0x739CD900, 0x131EB9C4,
0xEF1062B2, 0x4F0DE436, 0x52920073, 0x9A7F3D80,
0x896E7B1B, 0x2C8BBE5A, 0xBD304F8A, 0xA993E22C,
0x134C41A0, 0xFA989E00, 0x39CE9726, 0xFB89FCCF,
0xE8FBAC97, 0xD4063FFC, 0x935A2B5A, 0x44C8EE83,
0xCB2BC7B6, 0x02989E92, 0x75478BEA, 0x144378D0,
0xD853C087, 0x8897A34E, 0xDD23629D, 0xBDE2A2A2,
0x581D8ECC, 0x5DA8AEE8, 0xFF8AAFD0, 0xBA2BCF6E,
0x4BD98DAC, 0xF2EDB9E4, 0xFA2DC868, 0x47E84661,
0xECEB1C7D, 0x41705CA4, 0x5982E4D4, 0xEB5204A1,
0xD196CAFB, 0x6414804D, 0x3ABD4B46, 0x8B494C26,
0xB432D52B, 0x39C5356B, 0x6EC80BF7, 0x71BE5483,
0xCEC4A509, 0xE9411D61, 0x52F341E5, 0xD2E6197B,
0x4F02826C, 0xA9E48838, 0xD1F8F247, 0xE4957FB3,
0x586CCA99, 0x9A8B6A5B, 0x4998FBEA, 0xF762BE4C,
0x90DFE33C, 0x9731511E, 0x88C6A82F, 0xDD65A4D4
};
/// <summary> /// <summary>
/// Convert a hexadecimal string to a byte array. The input string must be /// Convert a hexadecimal string to a byte array. The input string must be
@@ -143,11 +89,11 @@ namespace KeePassLib.Utility
ch = strHex[i + 1]; ch = strHex[i + 1];
if ((ch >= '0') && (ch <= '9')) if ((ch >= '0') && (ch <= '9'))
bt += (byte)(ch - '0'); bt |= (byte)(ch - '0');
else if ((ch >= 'a') && (ch <= 'f')) else if ((ch >= 'a') && (ch <= 'f'))
bt += (byte)(ch - 'a' + 10); bt |= (byte)(ch - 'a' + 10);
else if ((ch >= 'A') && (ch <= 'F')) else if ((ch >= 'A') && (ch <= 'F'))
bt += (byte)(ch - 'A' + 10); bt |= (byte)(ch - 'A' + 10);
else { Debug.Assert(false); } else { Debug.Assert(false); }
pb[i >> 1] = bt; pb[i >> 1] = bt;
@@ -246,20 +192,26 @@ namespace KeePassLib.Utility
return l.ToArray(); return l.ToArray();
} }
internal static byte[] ParseBase32(string str, bool bAutoPad)
{
if (str == null) { Debug.Assert(false); return null; }
// https://sourceforge.net/p/keepass/discussion/329220/thread/59b61fddea/
if (bAutoPad && ((str.Length % 8) != 0))
str = str.PadRight((str.Length & ~7) + 8, '=');
return ParseBase32(str);
}
/// <summary> /// <summary>
/// Set all bytes in a byte array to zero. /// Set all bytes in a byte array to zero.
/// </summary> /// </summary>
/// <param name="pbArray">Input array. All bytes of this array /// <param name="pbArray">Input array. All bytes of this array
/// will be set to zero.</param> /// will be set to zero.</param>
#if KeePassLibSD [MethodImpl(MioNoOptimize)]
[MethodImpl(MethodImplOptions.NoInlining)]
#else
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
#endif
public static void ZeroByteArray(byte[] pbArray) public static void ZeroByteArray(byte[] pbArray)
{ {
Debug.Assert(pbArray != null); if (pbArray == null) { Debug.Assert(false); return; }
if(pbArray == null) throw new ArgumentNullException("pbArray");
Array.Clear(pbArray, 0, pbArray.Length); Array.Clear(pbArray, 0, pbArray.Length);
} }
@@ -268,18 +220,41 @@ namespace KeePassLib.Utility
/// Set all elements of an array to the default value. /// Set all elements of an array to the default value.
/// </summary> /// </summary>
/// <param name="v">Input array.</param> /// <param name="v">Input array.</param>
#if KeePassLibSD [MethodImpl(MioNoOptimize)]
[MethodImpl(MethodImplOptions.NoInlining)]
#else
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
#endif
public static void ZeroArray<T>(T[] v) public static void ZeroArray<T>(T[] v)
{ {
if(v == null) { Debug.Assert(false); throw new ArgumentNullException("v"); } if (v == null) { Debug.Assert(false); return; }
Array.Clear(v, 0, v.Length); Array.Clear(v, 0, v.Length);
} }
private static byte[] g_pbZero = null;
[MethodImpl(MioNoOptimize)]
public static void ZeroMemory(IntPtr pb, long cb)
{
if (pb == IntPtr.Zero) { Debug.Assert(false); return; }
if (cb < 0) { Debug.Assert(false); return; }
byte[] pbZero = g_pbZero;
if (pbZero == null)
{
pbZero = new byte[4096];
g_pbZero = pbZero;
}
long cbZero = pbZero.Length;
while (cb != 0)
{
long cbBlock = Math.Min(cb, cbZero);
Marshal.Copy(pbZero, 0, pb, (int)cbBlock);
pb = AddPtr(pb, cbBlock);
cb -= cbBlock;
}
}
/// <summary> /// <summary>
/// Convert 2 bytes to a 16-bit unsigned integer (little-endian). /// Convert 2 bytes to a 16-bit unsigned integer (little-endian).
/// </summary> /// </summary>
@@ -396,15 +371,7 @@ namespace KeePassLib.Utility
/// </summary> /// </summary>
public static byte[] UInt16ToBytes(ushort uValue) public static byte[] UInt16ToBytes(ushort uValue)
{ {
byte[] pb = new byte[2]; return new byte[2] { (byte)uValue, (byte)(uValue >> 8) };
unchecked
{
pb[0] = (byte)uValue;
pb[1] = (byte)(uValue >> 8);
}
return pb;
} }
/// <summary> /// <summary>
@@ -412,17 +379,8 @@ namespace KeePassLib.Utility
/// </summary> /// </summary>
public static byte[] UInt32ToBytes(uint uValue) public static byte[] UInt32ToBytes(uint uValue)
{ {
byte[] pb = new byte[4]; return new byte[4] { (byte)uValue, (byte)(uValue >> 8),
(byte)(uValue >> 16), (byte)(uValue >> 24) };
unchecked
{
pb[0] = (byte)uValue;
pb[1] = (byte)(uValue >> 8);
pb[2] = (byte)(uValue >> 16);
pb[3] = (byte)(uValue >> 24);
}
return pb;
} }
/// <summary> /// <summary>
@@ -431,41 +389,27 @@ namespace KeePassLib.Utility
public static void UInt32ToBytesEx(uint uValue, byte[] pb, int iOffset) public static void UInt32ToBytesEx(uint uValue, byte[] pb, int iOffset)
{ {
if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 3) >= pb.Length)) if ((iOffset < 0) || (iOffset >= (pb.Length - 3)))
{ {
Debug.Assert(false); Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset"); throw new ArgumentOutOfRangeException("iOffset");
} }
unchecked
{
pb[iOffset] = (byte)uValue; pb[iOffset] = (byte)uValue;
pb[iOffset + 1] = (byte)(uValue >> 8); pb[iOffset + 1] = (byte)(uValue >> 8);
pb[iOffset + 2] = (byte)(uValue >> 16); pb[iOffset + 2] = (byte)(uValue >> 16);
pb[iOffset + 3] = (byte)(uValue >> 24); pb[iOffset + 3] = (byte)(uValue >> 24);
} }
}
/// <summary> /// <summary>
/// Convert a 64-bit unsigned integer to 8 bytes (little-endian). /// Convert a 64-bit unsigned integer to 8 bytes (little-endian).
/// </summary> /// </summary>
public static byte[] UInt64ToBytes(ulong uValue) public static byte[] UInt64ToBytes(ulong uValue)
{ {
byte[] pb = new byte[8]; return new byte[8] { (byte)uValue, (byte)(uValue >> 8),
(byte)(uValue >> 16), (byte)(uValue >> 24),
unchecked (byte)(uValue >> 32), (byte)(uValue >> 40),
{ (byte)(uValue >> 48), (byte)(uValue >> 56) };
pb[0] = (byte)uValue;
pb[1] = (byte)(uValue >> 8);
pb[2] = (byte)(uValue >> 16);
pb[3] = (byte)(uValue >> 24);
pb[4] = (byte)(uValue >> 32);
pb[5] = (byte)(uValue >> 40);
pb[6] = (byte)(uValue >> 48);
pb[7] = (byte)(uValue >> 56);
}
return pb;
} }
/// <summary> /// <summary>
@@ -474,14 +418,12 @@ namespace KeePassLib.Utility
public static void UInt64ToBytesEx(ulong uValue, byte[] pb, int iOffset) public static void UInt64ToBytesEx(ulong uValue, byte[] pb, int iOffset)
{ {
if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); } if (pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if((iOffset < 0) || ((iOffset + 7) >= pb.Length)) if ((iOffset < 0) || (iOffset >= (pb.Length - 7)))
{ {
Debug.Assert(false); Debug.Assert(false);
throw new ArgumentOutOfRangeException("iOffset"); throw new ArgumentOutOfRangeException("iOffset");
} }
unchecked
{
pb[iOffset] = (byte)uValue; pb[iOffset] = (byte)uValue;
pb[iOffset + 1] = (byte)(uValue >> 8); pb[iOffset + 1] = (byte)(uValue >> 8);
pb[iOffset + 2] = (byte)(uValue >> 16); pb[iOffset + 2] = (byte)(uValue >> 16);
@@ -491,18 +433,27 @@ namespace KeePassLib.Utility
pb[iOffset + 6] = (byte)(uValue >> 48); pb[iOffset + 6] = (byte)(uValue >> 48);
pb[iOffset + 7] = (byte)(uValue >> 56); pb[iOffset + 7] = (byte)(uValue >> 56);
} }
}
public static byte[] Int32ToBytes(int iValue) public static byte[] Int32ToBytes(int iValue)
{ {
return UInt32ToBytes((uint)iValue); return UInt32ToBytes((uint)iValue);
} }
public static void Int32ToBytesEx(int iValue, byte[] pb, int iOffset)
{
UInt32ToBytesEx((uint)iValue, pb, iOffset);
}
public static byte[] Int64ToBytes(long lValue) public static byte[] Int64ToBytes(long lValue)
{ {
return UInt64ToBytes((ulong)lValue); return UInt64ToBytes((ulong)lValue);
} }
public static void Int64ToBytesEx(long lValue, byte[] pb, int iOffset)
{
UInt64ToBytesEx((ulong)lValue, pb, iOffset);
}
public static uint RotateLeft32(uint u, int nBits) public static uint RotateLeft32(uint u, int nBits)
{ {
return ((u << nBits) | (u >> (32 - nBits))); return ((u << nBits) | (u >> (32 - nBits)));
@@ -523,14 +474,35 @@ namespace KeePassLib.Utility
return ((u >> nBits) | (u << (64 - nBits))); return ((u >> nBits) | (u << (64 - nBits)));
} }
private static void AddVersionComponent(ref ulong uVersion, int iValue)
{
if (iValue < 0) iValue = 0;
else if (iValue > 0xFFFF) { Debug.Assert(false); iValue = 0xFFFF; }
uVersion = (uVersion << 16) | (uint)iValue;
}
internal static ulong VersionToUInt64(Version v)
{
if (v == null) { Debug.Assert(false); return 0; }
ulong u = 0;
AddVersionComponent(ref u, v.Major);
AddVersionComponent(ref u, v.Minor);
AddVersionComponent(ref u, v.Build);
AddVersionComponent(ref u, v.Revision);
return u;
}
public static bool ArraysEqual(byte[] x, byte[] y) public static bool ArraysEqual(byte[] x, byte[] y)
{ {
// Return false if one of them is null (not comparable)! // Return false if one of them is null (not comparable)!
if ((x == null) || (y == null)) { Debug.Assert(false); return false; } if ((x == null) || (y == null)) { Debug.Assert(false); return false; }
if(x.Length != y.Length) return false; int cb = x.Length;
if (cb != y.Length) return false;
for(int i = 0; i < x.Length; ++i) for (int i = 0; i < cb; ++i)
{ {
if (x[i] != y[i]) return false; if (x[i] != y[i]) return false;
} }
@@ -556,28 +528,90 @@ namespace KeePassLib.Utility
} }
/// <summary> /// <summary>
/// Fast hash that can be used e.g. for hash tables. /// Fast 32-bit hash (e.g. for hash tables).
/// The algorithm might change in the future; do not store /// The algorithm might change in the future; do not store
/// the hashes for later use. /// the hashes for later use.
/// </summary> /// </summary>
public static uint Hash32(byte[] v, int iStart, int iLength) public static uint Hash32(byte[] pb, int iOffset, int cb)
{ {
uint u = 0x326F637B; const ulong hI = 0x4295DC458269ED9DUL;
const uint hI32 = (uint)(hI >> 32);
if(v == null) { Debug.Assert(false); return u; } if (pb == null) { Debug.Assert(false); return hI32; }
if(iStart < 0) { Debug.Assert(false); return u; } if (iOffset < 0) { Debug.Assert(false); return hI32; }
if(iLength < 0) { Debug.Assert(false); return u; } if (cb < 0) { Debug.Assert(false); return hI32; }
int m = iStart + iLength; int m = iOffset + cb;
if(m > v.Length) { Debug.Assert(false); return u; } if ((m < 0) || (m > pb.Length)) { Debug.Assert(false); return hI32; }
for(int i = iStart; i < m; ++i) int m4 = iOffset + (cb & ~3), cbR = cb & 3;
ulong h = hI;
for (int i = iOffset; i < m4; i += 4)
h = (pb[i] ^ ((ulong)pb[i + 1] << 8) ^ ((ulong)pb[i + 2] << 16) ^
((ulong)pb[i + 3] << 24) ^ h) * 0x5EA4A1E35C8ACDA3UL;
switch (cbR)
{ {
u ^= m_vSBox[v[i]]; case 1:
u *= 3; Debug.Assert(m4 == (m - 1));
h = (pb[m4] ^ h) * 0x54A1CC5970AF27BBUL;
break;
case 2:
Debug.Assert(m4 == (m - 2));
h = (pb[m4] ^ ((ulong)pb[m4 + 1] << 8) ^ h) *
0x6C45CB2537A4271DUL;
break;
case 3:
Debug.Assert(m4 == (m - 3));
h = (pb[m4] ^ ((ulong)pb[m4 + 1] << 8) ^
((ulong)pb[m4 + 2] << 16) ^ h) * 0x59B8E8939E19695DUL;
break;
default:
Debug.Assert(m4 == m);
break;
} }
return u; Debug.Assert((cb != 0) || ((uint)(h >> 32) == hI32));
return (uint)(h >> 32);
}
internal static uint Hash32Ex<T>(T[] v, int iOffset, int c)
{
const ulong hI = 0x4295DC458269ED9DUL;
const uint hI32 = (uint)(hI >> 32);
if (v == null) { Debug.Assert(false); return hI32; }
if (iOffset < 0) { Debug.Assert(false); return hI32; }
if (c < 0) { Debug.Assert(false); return hI32; }
int m = iOffset + c;
if ((m < 0) || (m > v.Length)) { Debug.Assert(false); return hI32; }
ulong h = hI;
for (int i = iOffset; i < m; ++i)
h = (h ^ (uint)v[i].GetHashCode()) * 0x5EA4A1E35C8ACDA3UL;
Debug.Assert((c != 0) || ((uint)(h >> 32) == hI32));
return (uint)(h >> 32);
}
internal static ulong Hash64(int[] v, int iOffset, int ci)
{
ulong h = 0x4295DC458269ED9DUL;
if (v == null) { Debug.Assert(false); return h; }
if (iOffset < 0) { Debug.Assert(false); return h; }
if (ci < 0) { Debug.Assert(false); return h; }
int m = iOffset + ci;
if ((m < 0) || (m > v.Length)) { Debug.Assert(false); return h; }
for (int i = iOffset; i < m; ++i)
h = (h ^ (uint)v[i]) * 0x5EA4A1E35C8ACDA3UL;
return ((h ^ (h >> 32)) * 0x59B8E8939E19695DUL);
} }
public static void CopyStream(Stream sSource, Stream sTarget) public static void CopyStream(Stream sSource, Stream sTarget)
@@ -586,20 +620,31 @@ namespace KeePassLib.Utility
if (sSource == null) throw new ArgumentNullException("sSource"); if (sSource == null) throw new ArgumentNullException("sSource");
if (sTarget == null) throw new ArgumentNullException("sTarget"); if (sTarget == null) throw new ArgumentNullException("sTarget");
const int nBufSize = 4096; const int cbBuf = 4096;
byte[] pbBuf = new byte[nBufSize]; byte[] pbBuf = new byte[cbBuf];
while (true) while (true)
{ {
int nRead = sSource.Read(pbBuf, 0, nBufSize); int cbRead = sSource.Read(pbBuf, 0, cbBuf);
if(nRead == 0) break; if (cbRead == 0) break;
sTarget.Write(pbBuf, 0, nRead); sTarget.Write(pbBuf, 0, cbRead);
} }
// Do not close any of the streams // Do not close any of the streams
} }
public static byte[] Read(Stream s)
{
if (s == null) throw new ArgumentNullException("s");
using (MemoryStream ms = new MemoryStream())
{
CopyStream(s, ms);
return ms.ToArray();
}
}
public static byte[] Read(Stream s, int nCount) public static byte[] Read(Stream s, int nCount)
{ {
if (s == null) throw new ArgumentNullException("s"); if (s == null) throw new ArgumentNullException("s");
@@ -626,6 +671,17 @@ namespace KeePassLib.Utility
return pb; return pb;
} }
internal static string ReadString(Stream s, Encoding enc)
{
if (s == null) throw new ArgumentNullException("s");
if (enc == null) throw new ArgumentNullException("enc");
using (StreamReader sr = new StreamReader(s, enc, true))
{
return sr.ReadToEnd();
}
}
public static void Write(Stream s, byte[] pbData) public static void Write(Stream s, byte[] pbData)
{ {
if (s == null) { Debug.Assert(false); return; } if (s == null) { Debug.Assert(false); return; }
@@ -640,7 +696,6 @@ namespace KeePassLib.Utility
if (pbData == null) throw new ArgumentNullException("pbData"); if (pbData == null) throw new ArgumentNullException("pbData");
if (pbData.Length == 0) return pbData; if (pbData.Length == 0) return pbData;
byte[] pbCompressed;
using (MemoryStream msSource = new MemoryStream(pbData, false)) using (MemoryStream msSource = new MemoryStream(pbData, false))
{ {
using (MemoryStream msCompressed = new MemoryStream()) using (MemoryStream msCompressed = new MemoryStream())
@@ -648,14 +703,12 @@ namespace KeePassLib.Utility
using (GZipStream gz = new GZipStream(msCompressed, using (GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Compress)) CompressionMode.Compress))
{ {
MemUtil.CopyStream(msSource, gz); CopyStream(msSource, gz);
} }
pbCompressed = msCompressed.ToArray(); return msCompressed.ToArray();
} }
} }
return pbCompressed;
} }
public static byte[] Decompress(byte[] pbCompressed) public static byte[] Decompress(byte[] pbCompressed)
@@ -663,7 +716,6 @@ namespace KeePassLib.Utility
if (pbCompressed == null) throw new ArgumentNullException("pbCompressed"); if (pbCompressed == null) throw new ArgumentNullException("pbCompressed");
if (pbCompressed.Length == 0) return pbCompressed; if (pbCompressed.Length == 0) return pbCompressed;
byte[] pbData;
using (MemoryStream msData = new MemoryStream()) using (MemoryStream msData = new MemoryStream())
{ {
using (MemoryStream msCompressed = new MemoryStream(pbCompressed, false)) using (MemoryStream msCompressed = new MemoryStream(pbCompressed, false))
@@ -671,14 +723,12 @@ namespace KeePassLib.Utility
using (GZipStream gz = new GZipStream(msCompressed, using (GZipStream gz = new GZipStream(msCompressed,
CompressionMode.Decompress)) CompressionMode.Decompress))
{ {
MemUtil.CopyStream(gz, msData); CopyStream(gz, msData);
} }
} }
pbData = msData.ToArray(); return msData.ToArray();
} }
return pbData;
} }
public static int IndexOf<T>(T[] vHaystack, T[] vNeedle) public static int IndexOf<T>(T[] vHaystack, T[] vNeedle)
@@ -688,10 +738,13 @@ namespace KeePassLib.Utility
if (vNeedle == null) throw new ArgumentNullException("vNeedle"); if (vNeedle == null) throw new ArgumentNullException("vNeedle");
if (vNeedle.Length == 0) return 0; if (vNeedle.Length == 0) return 0;
for(int i = 0; i <= (vHaystack.Length - vNeedle.Length); ++i) int cN = vNeedle.Length;
int iMax = vHaystack.Length - cN;
for (int i = 0; i <= iMax; ++i)
{ {
bool bFound = true; bool bFound = true;
for(int m = 0; m < vNeedle.Length; ++m) for (int m = 0; m < cN; ++m)
{ {
if (!vHaystack[i + m].Equals(vNeedle[m])) if (!vHaystack[i + m].Equals(vNeedle[m]))
{ {
@@ -786,6 +839,44 @@ namespace KeePassLib.Utility
yield break; yield break;
} }
internal static IEnumerable<T> Distinct<T, TKey>(IEnumerable<T> s,
GFunc<T, TKey> fGetKey, bool bPreferLast)
{
if (s == null) throw new ArgumentNullException("s");
if (fGetKey == null) throw new ArgumentNullException("fGetKey");
Dictionary<TKey, bool> d = new Dictionary<TKey, bool>();
if (bPreferLast)
{
List<T> l = new List<T>(s);
int n = l.Count;
bool[] v = new bool[n];
for (int i = n - 1; i >= 0; --i)
{
TKey k = fGetKey(l[i]);
if (!d.ContainsKey(k)) { d[k] = true; v[i] = true; }
}
for (int i = 0; i < n; ++i)
{
if (v[i]) yield return l[i];
}
}
else
{
foreach (T t in s)
{
TKey k = fGetKey(t);
if (!d.ContainsKey(k)) { d[k] = true; yield return t; }
}
}
yield break;
}
internal static bool ListsEqual<T>(List<T> a, List<T> b) internal static bool ListsEqual<T>(List<T> a, List<T> b)
where T : class, IEquatable<T> where T : class, IEquatable<T>
{ {
@@ -809,5 +900,164 @@ namespace KeePassLib.Utility
return true; return true;
} }
internal static int Count(byte[] pb, byte bt)
{
if (pb == null) { Debug.Assert(false); return 0; }
int cb = pb.Length, r = 0;
for (int i = 0; i < cb; ++i)
{
if (pb[i] == bt) ++r;
}
return r;
}
[MethodImpl(MioNoOptimize)]
internal static void DisposeIfPossible(object o)
{
if (o == null) { Debug.Assert(false); return; }
IDisposable d = (o as IDisposable);
if (d != null) d.Dispose();
}
internal static object GetEnumValue(Type tEnum, string strName)
{
if (tEnum == null) { Debug.Assert(false); return null; }
if (!tEnum.IsEnum) { Debug.Assert(false); return null; }
if (string.IsNullOrEmpty(strName)) { Debug.Assert(false); return null; }
return ((Array.IndexOf<string>(Enum.GetNames(tEnum), strName) >= 0) ?
Enum.Parse(tEnum, strName) : null);
}
internal static T ConvertObject<T>(object o, T tDefault)
{
if (o == null) return tDefault;
try
{
if (o is T) return (T)o;
return (T)Convert.ChangeType(o, typeof(T));
}
catch (Exception) { Debug.Assert(false); }
try { return (T)o; }
catch (Exception) { Debug.Assert(false); }
return tDefault;
}
internal static T BytesToStruct<T>(byte[] pb, int iOffset)
where T : struct
{
if (pb == null) throw new ArgumentNullException("pb");
if (iOffset < 0) throw new ArgumentOutOfRangeException("iOffset");
int cb = Marshal.SizeOf(typeof(T));
if (cb <= 0) { Debug.Assert(false); return default(T); }
if (iOffset > (pb.Length - cb)) throw new ArgumentOutOfRangeException("iOffset");
IntPtr p = Marshal.AllocCoTaskMem(cb);
if (p == IntPtr.Zero) throw new OutOfMemoryException();
object o;
try
{
Marshal.Copy(pb, iOffset, p, cb);
o = Marshal.PtrToStructure(p, typeof(T));
}
finally { Marshal.FreeCoTaskMem(p); }
return (T)o;
}
internal static byte[] StructToBytes<T>(ref T t)
where T : struct
{
int cb = Marshal.SizeOf(typeof(T));
if (cb <= 0) { Debug.Assert(false); return MemUtil.EmptyByteArray; }
byte[] pb = new byte[cb];
IntPtr p = Marshal.AllocCoTaskMem(cb);
if (p == IntPtr.Zero) throw new OutOfMemoryException();
try
{
Marshal.StructureToPtr(t, p, false);
Marshal.Copy(p, pb, 0, cb);
}
finally { Marshal.FreeCoTaskMem(p); }
return pb;
}
internal static IntPtr AddPtr(IntPtr p, long cb)
{
// IntPtr.operator+ and IntPtr.Add are not available in .NET 2.0
if (IntPtr.Size >= 8)
return new IntPtr(unchecked(p.ToInt64() + cb));
return new IntPtr(unchecked(p.ToInt32() + (int)cb));
}
// Cf. Array.Empty<T>() of .NET 4.6
private static class EmptyArrayEx<T>
{
internal static readonly T[] Instance = new T[0];
}
internal static T[] EmptyArray<T>()
{
return EmptyArrayEx<T>.Instance;
}
}
internal sealed class ArrayHelperEx<T> : IEqualityComparer<T[]>, IComparer<T[]>
where T : IEquatable<T>, IComparable<T>
{
public int GetHashCode(T[] obj)
{
if (obj == null) { Debug.Assert(false); throw new ArgumentNullException("obj"); }
return (int)MemUtil.Hash32Ex<T>(obj, 0, obj.Length);
}
public bool Equals(T[] x, T[] y)
{
if (object.ReferenceEquals(x, y)) return true;
if ((x == null) || (y == null)) return false;
int n = x.Length;
if (n != y.Length) return false;
for (int i = 0; i < n; ++i)
{
if (!x[i].Equals(y[i])) return false;
}
return true;
}
public int Compare(T[] x, T[] y)
{
if (object.ReferenceEquals(x, y)) return 0;
if (x == null) return -1;
if (y == null) return 1;
int n = x.Length, m = y.Length;
if (n != m) return ((n < m) ? -1 : 1);
for (int i = 0; i < n; ++i)
{
T tX = x[i], tY = y[i];
if (!tX.Equals(tY)) return tX.CompareTo(tY);
}
return 0;
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1546,10 +1546,10 @@ namespace keepass2android
string url = _stringViews[urlFieldKey].Text; string url = _stringViews[urlFieldKey].Text;
if (url == null) return false; if (url == null) return false;
// Default http:// if no protocol specified // Default https:// if no protocol specified
if ((!url.Contains(":") || (url.StartsWith("www.")))) if ((!url.Contains(":") || (url.StartsWith("www."))))
{ {
url = "http://" + url; url = "https://" + url;
} }
try try

View File

@@ -23,6 +23,7 @@ using Android.App;
using Android.App.Admin; using Android.App.Admin;
using Android.Content; using Android.Content;
using Android.Content.PM; using Android.Content.PM;
using Android.Content.Res;
using Android.Graphics; using Android.Graphics;
using Android.OS; using Android.OS;
using Android.Preferences; using Android.Preferences;
@@ -66,8 +67,13 @@ namespace keepass2android
Resource.Id.cb_exclude_lookalike Resource.Id.cb_exclude_lookalike
}; };
PasswordFont _passwordFont = new PasswordFont(); PasswordFont _passwordFont = new PasswordFont();
private static object _popularPasswordsLock = new object();
private static bool _popularPasswordsInitialized = false;
private ActivityDesign _design; private ActivityDesign _design;
public GeneratePasswordActivity() public GeneratePasswordActivity()
@@ -302,6 +308,10 @@ namespace keepass2android
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit); EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
txtPasswordToSet.TextChanged += (sender, args) =>
{
Task.Run(() => UpdatePasswordStrengthEstimate(txtPasswordToSet.Text));
};
_passwordFont.ApplyTo(txtPasswordToSet); _passwordFont.ApplyTo(txtPasswordToSet);
@@ -467,17 +477,48 @@ namespace keepass2android
return; return;
String password = ""; String password = "";
uint passwordBits = 0;
Task.Run(() => Task.Run(() =>
{ {
password = GeneratePassword(); password = GeneratePassword();
passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray());
RunOnUiThread(() => RunOnUiThread(() =>
{ {
EditText txtPassword = (EditText)FindViewById(Resource.Id.password_edit); EditText txtPassword = (EditText)FindViewById(Resource.Id.password_edit);
txtPassword.Text = password; txtPassword.Text = password;
UpdateProfileSpinnerSelection();
});
});
}
private void UpdatePasswordStrengthEstimate(string password)
{
lock (_popularPasswordsLock)
{
if (!_popularPasswordsInitialized)
{
using (StreamReader sr = new StreamReader(Assets.Open("MostPopularPasswords.txt")))
{
var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
sr.BaseStream.CopyTo(memstream);
bytes = memstream.ToArray();
}
PopularPasswords.Add(bytes, false);
}
}
}
uint passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray());
RunOnUiThread(() =>
{
var progressBar = FindViewById<ProgressBar>(Resource.Id.pb_password_strength); var progressBar = FindViewById<ProgressBar>(Resource.Id.pb_password_strength);
progressBar.Progress = (int)passwordBits; progressBar.Progress = (int)passwordBits;
@@ -503,13 +544,7 @@ namespace keepass2android
PorterDuff.Mode.SrcIn)); PorterDuff.Mode.SrcIn));
FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits"; FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits";
UpdateProfileSpinnerSelection();
}); });
});
} }
private void UpdateProfileSpinnerSelection() private void UpdateProfileSpinnerSelection()

View File

@@ -1423,6 +1423,8 @@ namespace keepass2android
if (cbQuickUnlock == null) if (cbQuickUnlock == null)
throw new NullPointerException("cpQuickUnlock"); throw new NullPointerException("cpQuickUnlock");
App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked); App.Kp2a.SetQuickUnlockEnabled(cbQuickUnlock.Checked);
App.Kp2a.ScreenLockWasEnabledWhenOpeningDatabase =
(((KeyguardManager)GetSystemService(Context.KeyguardService)!)!).IsDeviceSecure;
if ((_loadDbFileTask != null) && (App.Kp2a.OfflineMode != _loadDbTaskOffline)) if ((_loadDbFileTask != null) && (App.Kp2a.OfflineMode != _loadDbTaskOffline))
{ {

View File

@@ -25,6 +25,7 @@ using Android.Widget;
using Android.Content.PM; using Android.Content.PM;
using KeePassLib.Keys; using KeePassLib.Keys;
using Android.Preferences; using Android.Preferences;
using Android.Provider;
using Android.Runtime; using Android.Runtime;
using Android.Views.InputMethods; using Android.Views.InputMethods;
@@ -162,6 +163,29 @@ namespace keepass2android
if (bundle != null) if (bundle != null)
numFailedAttempts = bundle.GetInt(NumFailedAttemptsKey, 0); numFailedAttempts = bundle.GetInt(NumFailedAttemptsKey, 0);
FindViewById(Resource.Id.QuickUnlock_buttonEnableLock).Click += (object sender, EventArgs e) =>
{
Intent intent = new Intent(Settings.ActionSecuritySettings);
StartActivity(intent);
};
FindViewById(Resource.Id.QuickUnlock_buttonCloseDb).Click += (object sender, EventArgs e) =>
{
App.Kp2a.Lock(false);
};
if (App.Kp2a.ScreenLockWasEnabledWhenOpeningDatabase == false)
{
FindViewById(Resource.Id.QuickUnlockForm).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.QuickUnlockBlocked).Visibility = ViewStates.Visible;
}
else
{
FindViewById(Resource.Id.QuickUnlockForm).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.QuickUnlockBlocked).Visibility = ViewStates.Gone;
}
} }

View File

@@ -79,6 +79,12 @@ android:paddingRight="16dp"
android:paddingTop="16dp"> android:paddingTop="16dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/QuickUnlockForm">
<TextView <TextView
android:id="@+id/QuickUnlock_label" android:id="@+id/QuickUnlock_label"
android:text="@string/QuickUnlock_label" android:text="@string/QuickUnlock_label"
@@ -88,11 +94,6 @@ android:paddingRight="16dp"
android:textSize="14sp" android:textSize="14sp"
/> />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText <EditText
android:inputType="textPassword" android:inputType="textPassword"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -121,6 +122,60 @@ android:paddingRight="16dp"
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/md_theme_secondaryContainer"
android:id="@+id/QuickUnlockBlocked"
android:padding="16dp"
android:layout_gravity="center">
<TextView
android:id="@+id/quick_unlock_blocked_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/password_based_quick_unlock_not_available"
android:textSize="16sp"
android:textStyle="bold"
android:gravity="center"
android:paddingBottom="8dp"/>
<TextView
android:id="@+id/alert_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/password_based_quick_unlock_not_available_text"
android:textSize="16sp"
android:paddingBottom="8dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/QuickUnlock_buttonEnableLock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="@color/md_theme_secondary"
android:textColor="@android:color/white"
android:text="@string/enable_screen_lock"
android:fontFamily="sans-serif-medium" />
<Button
android:id="@+id/QuickUnlock_buttonCloseDb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:backgroundTint="@color/md_theme_secondary"
android:textColor="@android:color/white"
android:fontFamily="sans-serif-medium"
android:text="@string/QuickUnlock_lockButton" />
</LinearLayout>
</LinearLayout>
<View <View
android:id="@+id/spacing" android:id="@+id/spacing"

View File

@@ -93,6 +93,8 @@
<string name="disable_fingerprint_unlock">Disable Biometric Unlock</string> <string name="disable_fingerprint_unlock">Disable Biometric Unlock</string>
<string name="enable_fingerprint_unlock">Enable full Biometric Unlock</string> <string name="enable_fingerprint_unlock">Enable full Biometric Unlock</string>
<string name="enable_fingerprint_quickunlock">Enable Biometric Unlock for QuickUnlock</string> <string name="enable_fingerprint_quickunlock">Enable Biometric Unlock for QuickUnlock</string>
<string name="password_based_quick_unlock_not_available">Password-based QuickUnlock not available</string>
<string name="password_based_quick_unlock_not_available_text">QuickUnlock using a part of your password is blocked because screen lock is not activated on your device. This behavior is to protect you in case somebody watched you entering your QuickUnlock key.</string>
<string name="fingerprint_unlock_failed">Biometric Unlock failed. Decryption key was invalidated by Android OS. This usually happens if a biometric authentication or security settings were changed. </string> <string name="fingerprint_unlock_failed">Biometric Unlock failed. Decryption key was invalidated by Android OS. This usually happens if a biometric authentication or security settings were changed. </string>
<string name="fingerprint_disabled_wrong_masterkey">Unlocking the database failed: Invalid composite key. Biometric Unlock was disabled because apparently the stored master password is no longer valid. </string> <string name="fingerprint_disabled_wrong_masterkey">Unlocking the database failed: Invalid composite key. Biometric Unlock was disabled because apparently the stored master password is no longer valid. </string>
<string name="fingerprint_reenable">Please re-enable Biometric Unlock for the new master password.</string> <string name="fingerprint_reenable">Please re-enable Biometric Unlock for the new master password.</string>
@@ -319,6 +321,7 @@
<string name="QuickUnlock_label_secure">Enter QuickUnlock code:</string> <string name="QuickUnlock_label_secure">Enter QuickUnlock code:</string>
<string name="QuickUnlock_button">QuickUnlock!</string> <string name="QuickUnlock_button">QuickUnlock!</string>
<string name="QuickUnlock_lockButton">Close database</string> <string name="QuickUnlock_lockButton">Close database</string>
<string name="enable_screen_lock">Enable screen lock</string>
<string name="QuickUnlockDefaultEnabled_title">Enable QuickUnlock by default</string> <string name="QuickUnlockDefaultEnabled_title">Enable QuickUnlock by default</string>
<string name="QuickUnlockDefaultEnabled_summary">Defines whether QuickUnlock is enabled by default or not.</string> <string name="QuickUnlockDefaultEnabled_summary">Defines whether QuickUnlock is enabled by default or not.</string>
<string name="ViewDatabaseSecure_title">Protect database display</string> <string name="ViewDatabaseSecure_title">Protect database display</string>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<network-security-config> <network-security-config>
<base-config cleartextTrafficPermitted="true"> <base-config>
<trust-anchors> <trust-anchors>
<certificates src="system" /> <certificates src="system" />
<certificates src="user" /> <certificates src="user" />

View File

@@ -351,6 +351,9 @@ namespace keepass2android
QuickUnlockEnabled = enabled; QuickUnlockEnabled = enabled;
} }
public bool ScreenLockWasEnabledWhenOpeningDatabase { get; set; }
public bool QuickUnlockEnabled { get; private set; } public bool QuickUnlockEnabled { get; private set; }
public int QuickUnlockKeyLength { get; private set; } public int QuickUnlockKeyLength { get; private set; }