1 /* 2 KeePass Password Safe - The Open-Source Password Manager 3 Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 using System; 21 using System.Collections.Generic; 22 using System.Diagnostics; 23 using System.Text; 24 25 using KeePass.Resources; 26 27 #if !KeePassUAP 28 using KeePass.Util.Spr; 29 #endif 30 31 using KeePassLib; 32 using KeePassLib.Cryptography.PasswordGenerator; 33 using KeePassLib.Security; 34 using KeePassLib.Utility; 35 36 namespace KeePass.Util 37 { 38 public static class PwGeneratorUtil 39 { 40 private static string m_strBuiltInSuffix = null; 41 internal static string BuiltInSuffix 42 { 43 get 44 { 45 if(m_strBuiltInSuffix == null) 46 m_strBuiltInSuffix = " (" + KPRes.BuiltIn + ")"; 47 return m_strBuiltInSuffix; 48 } 49 } 50 51 private static List<PwProfile> m_lBuiltIn = null; 52 public static List<PwProfile> BuiltInProfiles 53 { 54 get 55 { 56 if(m_lBuiltIn == null) AllocStandardProfiles(); 57 return m_lBuiltIn; 58 } 59 } 60 AllocStandardProfiles()61 private static void AllocStandardProfiles() 62 { 63 m_lBuiltIn = new List<PwProfile>(); 64 65 AddStdPattern(KPRes.RandomMacAddress, @"HH\-HH\-HH\-HH\-HH\-HH"); 66 67 string strHex = KPRes.HexKeyEx; 68 AddStdPattern(strHex.Replace(@"{PARAM}", "40"), @"h{10}"); 69 AddStdPattern(strHex.Replace(@"{PARAM}", "128"), @"h{32}"); 70 AddStdPattern(strHex.Replace(@"{PARAM}", "256"), @"h{64}"); 71 } 72 AddStdPattern(string strName, string strPattern)73 private static void AddStdPattern(string strName, string strPattern) 74 { 75 PwProfile p = new PwProfile(); 76 77 p.Name = strName + PwGeneratorUtil.BuiltInSuffix; 78 p.CollectUserEntropy = false; 79 p.GeneratorType = PasswordGeneratorType.Pattern; 80 p.Pattern = strPattern; 81 82 m_lBuiltIn.Add(p); 83 } 84 85 /// <summary> 86 /// Get a list of all password generator profiles (built-in 87 /// and user-defined ones). 88 /// </summary> GetAllProfiles(bool bSort)89 public static List<PwProfile> GetAllProfiles(bool bSort) 90 { 91 List<PwProfile> lUser = Program.Config.PasswordGenerator.UserProfiles; 92 93 // Sort it in the configuration file 94 if(bSort) lUser.Sort(PwGeneratorUtil.CompareProfilesByName); 95 96 // Remove old built-in profiles by KeePass <= 2.17 97 for(int i = lUser.Count - 1; i >= 0; --i) 98 { 99 if(IsBuiltInProfile(lUser[i].Name)) lUser.RemoveAt(i); 100 } 101 102 List<PwProfile> l = new List<PwProfile>(); 103 l.AddRange(PwGeneratorUtil.BuiltInProfiles); 104 l.AddRange(lUser); 105 if(bSort) l.Sort(PwGeneratorUtil.CompareProfilesByName); 106 return l; 107 } 108 IsBuiltInProfile(string strName)109 public static bool IsBuiltInProfile(string strName) 110 { 111 if(strName == null) { Debug.Assert(false); return false; } 112 113 string strWithSuffix = strName + PwGeneratorUtil.BuiltInSuffix; 114 foreach(PwProfile p in PwGeneratorUtil.BuiltInProfiles) 115 { 116 if(p.Name.Equals(strName, StrUtil.CaseIgnoreCmp) || 117 p.Name.Equals(strWithSuffix, StrUtil.CaseIgnoreCmp)) 118 return true; 119 } 120 121 return false; 122 } 123 CompareProfilesByName(PwProfile a, PwProfile b)124 public static int CompareProfilesByName(PwProfile a, PwProfile b) 125 { 126 if(a == b) return 0; 127 if(a == null) { Debug.Assert(false); return -1; } 128 if(b == null) { Debug.Assert(false); return 1; } 129 130 return StrUtil.CompareNaturally(a.Name, b.Name); 131 } 132 133 #if !KeePassUAP GenerateAcceptable(PwProfile prf, byte[] pbUserEntropy, PwEntry peOptCtx, PwDatabase pdOptCtx, bool bShowErrorUI)134 internal static ProtectedString GenerateAcceptable(PwProfile prf, 135 byte[] pbUserEntropy, PwEntry peOptCtx, PwDatabase pdOptCtx, 136 bool bShowErrorUI) 137 { 138 bool bAcceptAlways = false; 139 string strError; 140 return GenerateAcceptable(prf, pbUserEntropy, peOptCtx, pdOptCtx, 141 bShowErrorUI, ref bAcceptAlways, out strError); 142 } 143 GenerateAcceptable(PwProfile prf, byte[] pbUserEntropy, PwEntry peOptCtx, PwDatabase pdOptCtx, bool bShowErrorUI, ref bool bAcceptAlways, out string strError)144 internal static ProtectedString GenerateAcceptable(PwProfile prf, 145 byte[] pbUserEntropy, PwEntry peOptCtx, PwDatabase pdOptCtx, 146 bool bShowErrorUI, ref bool bAcceptAlways, out string strError) 147 { 148 strError = null; 149 150 ProtectedString ps = ProtectedString.Empty; 151 SprContext ctx = new SprContext(peOptCtx, pdOptCtx, 152 SprCompileFlags.NonActive, false, false); 153 154 while(true) 155 { 156 try 157 { 158 PwgError e = PwGenerator.Generate(out ps, prf, pbUserEntropy, 159 Program.PwGeneratorPool); 160 161 if(e != PwgError.Success) 162 { 163 strError = PwGenerator.ErrorToString(e, true); 164 break; 165 } 166 } 167 catch(Exception ex) 168 { 169 strError = PwGenerator.ErrorToString(ex, true); 170 break; 171 } 172 finally 173 { 174 if(ps == null) { Debug.Assert(false); ps = ProtectedString.Empty; } 175 } 176 177 if(bAcceptAlways) break; 178 179 string str = ps.ReadString(); 180 string strCmp = SprEngine.Compile(str, ctx); 181 182 if(str != strCmp) 183 { 184 if(prf.GeneratorType == PasswordGeneratorType.CharSet) 185 continue; // Silently try again 186 187 string strText = str + MessageService.NewParagraph + 188 KPRes.GenPwSprVariant + MessageService.NewParagraph + 189 KPRes.GenPwAccept; 190 191 if(!MessageService.AskYesNo(strText, null, false)) 192 continue; 193 bAcceptAlways = true; 194 } 195 196 break; 197 } 198 199 if(!string.IsNullOrEmpty(strError)) 200 { 201 ps = ProtectedString.Empty; 202 if(bShowErrorUI) MessageService.ShowWarning(strError); 203 } 204 205 return ps; 206 } 207 GenerateAuto(PwEntry pe, PwDatabase pd)208 internal static void GenerateAuto(PwEntry pe, PwDatabase pd) 209 { 210 if(pe == null) { Debug.Assert(false); return; } 211 if(pd == null) { Debug.Assert(false); return; } 212 213 ProtectedString ps = GenerateAcceptable( 214 Program.Config.PasswordGenerator.AutoGeneratedPasswordsProfile, 215 null, pe, pd, false); 216 pe.Strings.Set(PwDefs.PasswordField, ps.WithProtection( 217 pd.MemoryProtection.ProtectPassword)); 218 } 219 #endif 220 } 221 } 222