/KeePassLib/Cryptography/PasswordGenerator/PwGenerator.cs

https://bitbucket.org/dlech/keepass8 · C# · 146 lines · 96 code · 28 blank · 22 comment · 37 complexity · 2aed755fcae1a63e60c14b3c9c9f2c5c MD5 · raw file

  1. /*
  2. KeePass Password Safe - The Open-Source Password Manager
  3. Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  15. */
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Text;
  19. using System.Diagnostics;
  20. using KeePassLib.Security;
  21. namespace KeePassLib.Cryptography.PasswordGenerator
  22. {
  23. public enum PwgError
  24. {
  25. Success = 0,
  26. Unknown = 1,
  27. TooFewCharacters = 2,
  28. UnknownAlgorithm = 3
  29. }
  30. /// <summary>
  31. /// Utility functions for generating random passwords.
  32. /// </summary>
  33. public static class PwGenerator
  34. {
  35. public static PwgError Generate(out ProtectedString psOut,
  36. PwProfile pwProfile, byte[] pbUserEntropy,
  37. CustomPwGeneratorPool pwAlgorithmPool)
  38. {
  39. Debug.Assert(pwProfile != null);
  40. if(pwProfile == null) throw new ArgumentNullException("pwProfile");
  41. CryptoRandomStream crs = CreateCryptoStream(pbUserEntropy);
  42. PwgError e = PwgError.Unknown;
  43. if(pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
  44. e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
  45. else if(pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
  46. e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
  47. else if(pwProfile.GeneratorType == PasswordGeneratorType.Custom)
  48. e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
  49. else { Debug.Assert(false); psOut = ProtectedString.Empty; }
  50. return e;
  51. }
  52. private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy)
  53. {
  54. byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(256);
  55. // Mix in additional entropy
  56. if((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
  57. {
  58. for(int nKeyPos = 0; nKeyPos < pbKey.Length; ++nKeyPos)
  59. pbKey[nKeyPos] ^= pbAdditionalEntropy[nKeyPos % pbAdditionalEntropy.Length];
  60. }
  61. return new CryptoRandomStream(CrsAlgorithm.Salsa20, pbKey);
  62. }
  63. internal static char GenerateCharacter(PwProfile pwProfile,
  64. PwCharSet pwCharSet, CryptoRandomStream crsRandomSource)
  65. {
  66. if(pwCharSet.Size == 0) return char.MinValue;
  67. ulong uIndex = crsRandomSource.GetRandomUInt64();
  68. uIndex %= (ulong)pwCharSet.Size;
  69. char ch = pwCharSet[(uint)uIndex];
  70. if(pwProfile.NoRepeatingCharacters)
  71. pwCharSet.Remove(ch);
  72. return ch;
  73. }
  74. internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
  75. {
  76. pwCharSet.Remove(PwCharSet.Invalid);
  77. if(pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
  78. if(pwProfile.ExcludeCharacters.Length > 0)
  79. pwCharSet.Remove(pwProfile.ExcludeCharacters);
  80. }
  81. internal static void ShufflePassword(char[] pPassword,
  82. CryptoRandomStream crsRandomSource)
  83. {
  84. Debug.Assert(pPassword != null); if(pPassword == null) return;
  85. Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) return;
  86. if(pPassword.Length <= 1) return; // Nothing to shuffle
  87. for(int nSelect = 0; nSelect < pPassword.Length; ++nSelect)
  88. {
  89. ulong uRandomIndex = crsRandomSource.GetRandomUInt64();
  90. uRandomIndex %= (ulong)(pPassword.Length - nSelect);
  91. char chTemp = pPassword[nSelect];
  92. pPassword[nSelect] = pPassword[nSelect + (int)uRandomIndex];
  93. pPassword[nSelect + (int)uRandomIndex] = chTemp;
  94. }
  95. }
  96. private static PwgError GenerateCustom(out ProtectedString psOut,
  97. PwProfile pwProfile, CryptoRandomStream crs,
  98. CustomPwGeneratorPool pwAlgorithmPool)
  99. {
  100. psOut = ProtectedString.Empty;
  101. Debug.Assert(pwProfile.GeneratorType == PasswordGeneratorType.Custom);
  102. if(pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
  103. string strID = pwProfile.CustomAlgorithmUuid;
  104. if(string.IsNullOrEmpty(strID)) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
  105. byte[] pbUuid = Convert.FromBase64String(strID);
  106. PwUuid uuid = new PwUuid(pbUuid);
  107. CustomPwGenerator pwg = pwAlgorithmPool.Find(uuid);
  108. if(pwg == null) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
  109. ProtectedString pwd = pwg.Generate(pwProfile.CloneDeep(), crs);
  110. if(pwd == null) return PwgError.Unknown;
  111. psOut = pwd;
  112. return PwgError.Success;
  113. }
  114. }
  115. }