mirror of
https://github.com/wismna/ModernKeePassLib.git
synced 2025-10-03 23:50:20 -04:00
164 lines
4.7 KiB
C#
164 lines
4.7 KiB
C#
![]() |
/*
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|