Files
keepass2android/src/keepass2android-app/password/PasswordGenerator.cs
2025-01-07 11:20:08 +01:00

369 lines
12 KiB
C#

/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
Keepass2Android 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 3 of the License, or
(at your option) any later version.
Keepass2Android 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 Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Android.App;
using Android.Content;
namespace keepass2android
{
public static class StringExtension
{
public static string ToUpperFirstLetter(this string source)
{
if (string.IsNullOrEmpty(source))
return string.Empty;
// convert to char array of the string
char[] letters = source.ToCharArray();
// upper case the first char
letters[0] = char.ToUpper(letters[0]);
// return the array made of the new char array
return new string(letters);
}
}
/// <summary>
/// Password generator
/// </summary>
public class PasswordGenerator {
private const String UpperCaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private const String LowerCaseChars = "abcdefghijklmnopqrstuvwxyz";
private const String DigitChars = "0123456789";
private const String MinusChars = "-";
private const String UnderlineChars = "_";
private const String SpaceChars = " ";
private const String SpecialChars = "!\"#$%&'*+,./:;=?@\\^`";
private const String ExtendedChars = "§©®¢°±¹²³¼½×÷«âéïñù¡¿»¦Ø";
private const String BracketChars = "[]{}()<>";
private readonly Context _cxt;
public sealed class SecureRandom : Random
{
private readonly RandomNumberGenerator _rng = new RNGCryptoServiceProvider();
public override int Next()
{
var data = new byte[sizeof(int)];
_rng.GetBytes(data);
return BitConverter.ToInt32(data, 0) & (Int32.MaxValue - 1);
}
public override int Next(int maxValue)
{
return Next(0, maxValue);
}
public override int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
{
throw new ArgumentOutOfRangeException();
}
return (int)Math.Floor((minValue + (maxValue - minValue) * NextDouble()));
}
public override double NextDouble()
{
var data = new byte[sizeof(uint)];
_rng.GetBytes(data);
var randUint = BitConverter.ToUInt32(data, 0);
return randUint / (UInt32.MaxValue + 1.0);
}
public override void NextBytes(byte[] data)
{
_rng.GetBytes(data);
}
}
public PasswordGenerator(Context cxt) {
_cxt = cxt;
}
public class CombinedKeyOptions
{
protected bool Equals(CombinedKeyOptions other)
{
return Equals(PassphraseGenerationOptions, other.PassphraseGenerationOptions) && Equals(PasswordGenerationOptions, other.PasswordGenerationOptions);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((CombinedKeyOptions) obj);
}
public override int GetHashCode()
{
return HashCode.Combine(PassphraseGenerationOptions, PasswordGenerationOptions);
}
public PassphraseGenerationOptions PassphraseGenerationOptions { get; set; }
public PasswordGenerationOptions PasswordGenerationOptions { get; set; }
}
public class PasswordGenerationOptions
{
protected bool Equals(PasswordGenerationOptions other)
{
return Length == other.Length && UpperCase == other.UpperCase && LowerCase == other.LowerCase && Digits == other.Digits && Minus == other.Minus && Underline == other.Underline && Space == other.Space && Specials == other.Specials && SpecialsExtended == other.SpecialsExtended && Brackets == other.Brackets && ExcludeLookAlike == other.ExcludeLookAlike && AtLeastOneFromEachGroup == other.AtLeastOneFromEachGroup;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((PasswordGenerationOptions) obj);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add(Length);
hashCode.Add(UpperCase);
hashCode.Add(LowerCase);
hashCode.Add(Digits);
hashCode.Add(Minus);
hashCode.Add(Underline);
hashCode.Add(Space);
hashCode.Add(Specials);
hashCode.Add(SpecialsExtended);
hashCode.Add(Brackets);
hashCode.Add(ExcludeLookAlike);
hashCode.Add(AtLeastOneFromEachGroup);
return hashCode.ToHashCode();
}
public int Length { get; set; }
public bool UpperCase { get; set; }
public bool LowerCase { get; set; }
public bool Digits { get; set; }
public bool Minus { get; set; }
public bool Underline { get; set; }
public bool Space { get; set; }
public bool Specials { get; set; }
public bool SpecialsExtended { get; set; }
public bool Brackets { get; set; }
public bool ExcludeLookAlike { get; set; }
public bool AtLeastOneFromEachGroup { get; set; }
}
public class PassphraseGenerationOptions
{
protected bool Equals(PassphraseGenerationOptions other)
{
return CaseMode == other.CaseMode && Separator == other.Separator && WordCount == other.WordCount;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((PassphraseGenerationOptions) obj);
}
public override int GetHashCode()
{
return HashCode.Combine((int) CaseMode, Separator, WordCount);
}
public enum PassphraseCaseMode
{
Lowercase,
Uppercase,
PascalCase
};
public PassphraseCaseMode CaseMode { get; set; }
public string Separator { get; set; }
public int WordCount { get; set; }
}
public String GeneratePassword(CombinedKeyOptions options)
{
if ((options.PassphraseGenerationOptions== null || options.PassphraseGenerationOptions.WordCount == 0)
&& (options.PasswordGenerationOptions == null || options.PasswordGenerationOptions.Length == 0))
{
throw new Exception("Bad options");
}
string key = "";
Random random = new SecureRandom();
var passwordOptions = options.PasswordGenerationOptions;
var passphraseOptions = options.PassphraseGenerationOptions;
if (passphraseOptions != null && passphraseOptions.WordCount > 0)
{
var wl = new Wordlist();
string passphrase = "";
for (int i = 0; i < passphraseOptions.WordCount; i++)
{
string word = wl.GetWord(random);
if (passphraseOptions.CaseMode == PassphraseGenerationOptions.PassphraseCaseMode.Uppercase)
{
word = word.ToUpper();
}
else if (passphraseOptions.CaseMode == PassphraseGenerationOptions.PassphraseCaseMode.Lowercase)
{
word = word.ToLower();
}
else if (passphraseOptions.CaseMode == PassphraseGenerationOptions.PassphraseCaseMode.PascalCase)
{
word = word.ToUpperFirstLetter();
}
passphrase += word;
if (i < passphraseOptions.WordCount - 1 || passwordOptions != null)
passphrase += passphraseOptions.Separator;
}
key += passphrase;
}
if (passwordOptions != null)
{
var groups = GetCharacterGroups(passwordOptions);
String characterSet = GetCharacterSet(passwordOptions, groups);
if (characterSet.Length == 0)
throw new Exception("Bad options");
int size = characterSet.Length;
StringBuilder buffer = new StringBuilder();
if (passwordOptions.AtLeastOneFromEachGroup)
{
foreach (var g in groups)
{
if (g.Length > 0)
{
buffer.Append(g[random.Next(g.Length)]);
}
}
}
if (size > 0)
{
while (buffer.Length < passwordOptions.Length)
{
buffer.Append(characterSet[random.Next(size)]);
}
}
var password = buffer.ToString();
if (passwordOptions.AtLeastOneFromEachGroup)
{
//shuffle
StringBuilder sb = new StringBuilder(password);
for (int i = (password.Length - 1); i >= 1; i--)
{
int j = random.Next(i + 1);
var tmp = sb[i];
sb[i] = sb[j];
sb[j] = tmp;
}
password = sb.ToString();
}
key += password;
}
return key;
}
public string GetCharacterSet(PasswordGenerationOptions options, List<string> groups)
{
var characterSet = String.Join("", groups);
return characterSet;
}
private static List<string> GetCharacterGroups(PasswordGenerationOptions options)
{
List<string> groups = new List<string>();
if (options.UpperCase)
groups.Add(UpperCaseChars);
if (options.LowerCase)
groups.Add(LowerCaseChars);
if (options.Digits)
groups.Add(DigitChars);
if (options.Minus)
groups.Add(MinusChars);
if (options.Underline)
groups.Add(UnderlineChars);
if (options.Space)
groups.Add(SpaceChars);
if (options.Specials)
groups.Add(SpecialChars);
if (options.SpecialsExtended)
groups.Add(ExtendedChars);
if (options.Brackets)
groups.Add(BracketChars);
if (options.ExcludeLookAlike)
{
for (int i = 0; i < groups.Count; i++)
{
groups[i] = String.Join("", groups[i].Except("Il1|8B6GO0"));
}
}
return groups;
}
}
}