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.ComponentModel;
23 using System.Diagnostics;
24 using System.Drawing;
25 using System.IO;
26 using System.Text;
27 using System.Windows.Forms;
28 
29 using KeePass.App;
30 using KeePass.Resources;
31 using KeePass.UI;
32 using KeePass.Util;
33 
34 using KeePassLib;
35 using KeePassLib.Cryptography;
36 using KeePassLib.Keys;
37 using KeePassLib.Native;
38 using KeePassLib.Resources;
39 using KeePassLib.Security;
40 using KeePassLib.Serialization;
41 using KeePassLib.Utility;
42 
43 namespace KeePass.Forms
44 {
45 	public partial class KeyCreationForm : Form
46 	{
47 		private CompositeKey m_pKey = null;
48 		private bool m_bCreatingNew = false;
49 		private IOConnectionInfo m_ioInfo = new IOConnectionInfo();
50 
51 		private PwInputControlGroup m_icgPassword = new PwInputControlGroup();
52 		private Image m_imgKeyFileWarning = null;
53 		private Image m_imgAccWarning = null;
54 		// private uint m_uBlockUpdate = 0;
55 
56 		public CompositeKey CompositeKey
57 		{
58 			get
59 			{
60 				Debug.Assert(m_pKey != null);
61 				return m_pKey;
62 			}
63 		}
64 
KeyCreationForm()65 		public KeyCreationForm()
66 		{
67 			InitializeComponent();
68 
69 			SecureTextBoxEx.InitEx(ref m_tbPassword);
70 			SecureTextBoxEx.InitEx(ref m_tbRepeatPassword);
71 
72 			GlobalWindowManager.InitializeForm(this);
73 		}
74 
InitEx(IOConnectionInfo ioInfo, bool bCreatingNew)75 		public void InitEx(IOConnectionInfo ioInfo, bool bCreatingNew)
76 		{
77 			if(ioInfo != null) m_ioInfo = ioInfo;
78 
79 			m_bCreatingNew = bCreatingNew;
80 		}
81 
OnFormLoad(object sender, EventArgs e)82 		private void OnFormLoad(object sender, EventArgs e)
83 		{
84 			// The password text box should not be focused by default
85 			// in order to avoid a Caps Lock warning tooltip bug;
86 			// https://sourceforge.net/p/keepass/bugs/1807/
87 			Debug.Assert((m_tbPassword.TabIndex >= 2) && !m_tbPassword.Focused);
88 
89 			GlobalWindowManager.AddWindow(this);
90 
91 			BannerFactory.CreateBannerEx(this, m_bannerImage,
92 				Properties.Resources.B48x48_KGPG_Sign, KPRes.CreateMasterKey,
93 				m_ioInfo.GetDisplayName());
94 			this.Icon = AppIcons.Default;
95 			this.Text = KPRes.CreateMasterKey;
96 
97 			FontUtil.SetDefaultFont(m_cbPassword);
98 			FontUtil.AssignDefaultBold(m_cbPassword);
99 			FontUtil.AssignDefaultBold(m_cbKeyFile);
100 			FontUtil.AssignDefaultBold(m_cbUserAccount);
101 
102 			m_imgKeyFileWarning = UIUtil.IconToBitmap(SystemIcons.Warning,
103 				DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
104 			m_imgAccWarning = (Image)m_imgKeyFileWarning.Clone();
105 			m_picKeyFileWarning.Image = m_imgKeyFileWarning;
106 			m_picAccWarning.Image = m_imgAccWarning;
107 
108 			UIUtil.ConfigureToolTip(m_ttRect);
109 			UIUtil.SetToolTip(m_ttRect, m_tbRepeatPassword, KPRes.PasswordRepeatHint, false);
110 			UIUtil.SetToolTip(m_ttRect, m_btnSaveKeyFile, KPRes.KeyFileCreate, false);
111 			UIUtil.SetToolTip(m_ttRect, m_btnOpenKeyFile, KPRes.KeyFileUseExisting, false);
112 
113 			UIUtil.AccSetName(m_tbPassword, m_cbPassword);
114 			UIUtil.AccSetName(m_cmbKeyFile, m_cbKeyFile);
115 			UIUtil.AccSetName(m_picKeyFileWarning, KPRes.Warning);
116 			UIUtil.AccSetName(m_picAccWarning, KPRes.Warning);
117 
118 			Debug.Assert(!m_lblIntro.AutoSize); // For RTL support
119 			if(!m_bCreatingNew)
120 				m_lblIntro.Text = KPRes.ChangeMasterKeyIntroShort;
121 
122 			m_icgPassword.Attach(m_tbPassword, m_cbHidePassword, m_lblRepeatPassword,
123 				m_tbRepeatPassword, m_lblEstimatedQuality, m_pbPasswordQuality,
124 				m_lblQualityInfo, m_ttRect, this, true, false);
125 
126 			m_cmbKeyFile.Items.Add(KPRes.NoKeyFileSpecifiedMeta);
127 			foreach(KeyProvider prov in Program.KeyProviderPool)
128 				m_cmbKeyFile.Items.Add(prov.Name);
129 
130 			m_cmbKeyFile.SelectedIndex = 0;
131 
132 			m_cbPassword.Checked = true;
133 			UIUtil.ApplyKeyUIFlags(Program.Config.UI.KeyCreationFlags,
134 				m_cbPassword, m_cbKeyFile, m_cbUserAccount, m_cbHidePassword);
135 
136 			if(WinUtil.IsWindows9x || NativeLib.IsUnix())
137 			{
138 				UIUtil.SetChecked(m_cbUserAccount, false);
139 				UIUtil.SetEnabled(m_cbUserAccount, false);
140 				UIUtil.SetEnabled(m_lblWindowsAccDesc, false);
141 				UIUtil.SetEnabled(m_lblWindowsAccDesc2, false);
142 			}
143 
144 			EnableUserControls();
145 			// UIUtil.SetFocus(m_tbPassword, this); // See OnFormShown
146 		}
147 
OnFormShown(object sender, EventArgs e)148 		private void OnFormShown(object sender, EventArgs e)
149 		{
150 			// Focusing doesn't always work in OnFormLoad;
151 			// https://sourceforge.net/p/keepass/feature-requests/1735/
152 			if(m_tbPassword.CanFocus) UIUtil.ResetFocus(m_tbPassword, this, true);
153 			else if(m_cmbKeyFile.CanFocus) UIUtil.SetFocus(m_cmbKeyFile, this, true);
154 			else if(m_btnCreate.CanFocus) UIUtil.SetFocus(m_btnCreate, this, true);
155 			else { Debug.Assert(false); }
156 		}
157 
CleanUpEx()158 		private void CleanUpEx()
159 		{
160 			if(m_imgKeyFileWarning != null)
161 			{
162 				m_picKeyFileWarning.Image = null;
163 				m_imgKeyFileWarning.Dispose();
164 				m_imgKeyFileWarning = null;
165 			}
166 
167 			if(m_imgAccWarning != null)
168 			{
169 				m_picAccWarning.Image = null;
170 				m_imgAccWarning.Dispose();
171 				m_imgAccWarning = null;
172 			}
173 
174 			m_icgPassword.Release();
175 		}
176 
CreateCompositeKey()177 		private bool CreateCompositeKey()
178 		{
179 			m_pKey = new CompositeKey();
180 
181 			if(m_cbPassword.Checked) // Use a password
182 			{
183 				if(!m_icgPassword.ValidateData(true)) return false;
184 
185 				byte[] pb = m_icgPassword.GetPasswordUtf8();
186 				try
187 				{
188 					uint uPwLen = (uint)m_tbPassword.TextLength;
189 					uint uPwBits = QualityEstimation.EstimatePasswordBits(pb);
190 
191 					uint uMinLen = Program.Config.Security.MasterPassword.MinimumLength;
192 					if(uPwLen < uMinLen)
193 					{
194 						string strML = KPRes.MasterPasswordMinLengthFailed;
195 						strML = strML.Replace(@"{PARAM}", uMinLen.ToString());
196 						MessageService.ShowWarning(strML);
197 						return false;
198 					}
199 
200 					uint uMinQual = Program.Config.Security.MasterPassword.MinimumQuality;
201 					if(uPwBits < uMinQual)
202 					{
203 						string strMQ = KPRes.MasterPasswordMinQualityFailed;
204 						strMQ = strMQ.Replace(@"{PARAM}", uMinQual.ToString());
205 						MessageService.ShowWarning(strMQ);
206 						return false;
207 					}
208 
209 					string strValRes = Program.KeyValidatorPool.Validate(pb,
210 						KeyValidationType.MasterPassword);
211 					if(strValRes != null)
212 					{
213 						MessageService.ShowWarning(strValRes);
214 						return false;
215 					}
216 
217 					if(uPwLen == 0)
218 					{
219 						if(!MessageService.AskYesNo(KPRes.EmptyMasterPw +
220 							MessageService.NewParagraph + KPRes.EmptyMasterPwHint +
221 							MessageService.NewParagraph + KPRes.EmptyMasterPwQuestion,
222 							null, false))
223 							return false;
224 					}
225 
226 					if(uPwBits <= PwDefs.QualityBitsWeak)
227 					{
228 						string strMQ = KPRes.MasterPasswordWeak + MessageService.NewParagraph +
229 							KPRes.MasterPasswordConfirm;
230 						if(!MessageService.AskYesNo(strMQ, null, false,
231 							MessageBoxIcon.Warning))
232 							return false;
233 					}
234 
235 					m_pKey.AddUserKey(new KcpPassword(pb,
236 						Program.Config.Security.MasterPassword.RememberWhileOpen));
237 				}
238 				finally { MemUtil.ZeroByteArray(pb); }
239 			}
240 
241 			string strKeyFile = m_cmbKeyFile.Text;
242 			bool bIsKeyProv = Program.KeyProviderPool.IsKeyProvider(strKeyFile);
243 
244 			if(m_cbKeyFile.Checked && (!strKeyFile.Equals(KPRes.NoKeyFileSpecifiedMeta)) &&
245 				!bIsKeyProv)
246 			{
247 				try { m_pKey.AddUserKey(new KcpKeyFile(strKeyFile, true)); }
248 				catch(Exception exKF)
249 				{
250 					MessageService.ShowWarning(strKeyFile, KLRes.FileLoadFailed, exKF);
251 					return false;
252 				}
253 			}
254 			else if(m_cbKeyFile.Checked && (!strKeyFile.Equals(KPRes.NoKeyFileSpecifiedMeta)) &&
255 				bIsKeyProv)
256 			{
257 				KeyProviderQueryContext ctxKP = new KeyProviderQueryContext(
258 					m_ioInfo, true, false);
259 
260 				bool bPerformHash;
261 				byte[] pbCustomKey = Program.KeyProviderPool.GetKey(strKeyFile, ctxKP,
262 					out bPerformHash);
263 				if((pbCustomKey != null) && (pbCustomKey.Length > 0))
264 				{
265 					try { m_pKey.AddUserKey(new KcpCustomKey(strKeyFile, pbCustomKey, bPerformHash)); }
266 					catch(Exception exCKP)
267 					{
268 						MessageService.ShowWarning(exCKP);
269 						return false;
270 					}
271 
272 					MemUtil.ZeroByteArray(pbCustomKey);
273 				}
274 				else return false; // Provider has shown error message
275 			}
276 
277 			if(m_cbUserAccount.Checked)
278 			{
279 				try { m_pKey.AddUserKey(new KcpUserAccount()); }
280 				catch(Exception exUA)
281 				{
282 					MessageService.ShowWarning(exUA);
283 					return false;
284 				}
285 			}
286 
287 			return true;
288 		}
289 
EnableUserControls()290 		private void EnableUserControls()
291 		{
292 			// Must support recursive call, see m_cbExpert.Checked
293 			// if(m_uBlockUpdate != 0) return;
294 			// ++m_uBlockUpdate;
295 
296 			m_icgPassword.Enabled = m_cbPassword.Checked;
297 
298 			bool bKeyFile = m_cbKeyFile.Checked;
299 			m_cmbKeyFile.Enabled = bKeyFile;
300 
301 			string strKeyFile = m_cmbKeyFile.Text;
302 			bool bKeyProv = (!strKeyFile.Equals(KPRes.NoKeyFileSpecifiedMeta) &&
303 				Program.KeyProviderPool.IsKeyProvider(strKeyFile));
304 			m_btnOpenKeyFile.Enabled = m_btnSaveKeyFile.Enabled =
305 				(bKeyFile && !bKeyProv);
306 
307 			bool bUserAccount = m_cbUserAccount.Checked;
308 			if(!m_cbPassword.Checked && !bKeyFile && !bUserAccount)
309 				m_btnCreate.Enabled = false;
310 			else if(bKeyFile && strKeyFile.Equals(KPRes.NoKeyFileSpecifiedMeta))
311 				m_btnCreate.Enabled = false;
312 			else m_btnCreate.Enabled = true;
313 
314 			UIUtil.SetToolTip(m_ttRect, m_cmbKeyFile, strKeyFile, false);
315 
316 			bool bExpert = m_cbExpert.Checked;
317 			bool bShowKF = (bExpert || bKeyFile);
318 			bool bShowUA = (bExpert || bUserAccount);
319 
320 			Control[] vKeyFile = new Control[] {
321 				m_cbKeyFile, m_cmbKeyFile, m_btnOpenKeyFile, m_btnSaveKeyFile,
322 				m_lblKeyFileInfo, m_picKeyFileWarning, m_lblKeyFileWarning,
323 				m_lnkKeyFile
324 			};
325 			foreach(Control c in vKeyFile) c.Visible = bShowKF;
326 
327 			Control[] vUserAccount = new Control[] {
328 				m_cbUserAccount, m_lblWindowsAccDesc, m_picAccWarning,
329 				m_lblWindowsAccDesc2, m_lnkUserAccount
330 			};
331 			foreach(Control c in vUserAccount) c.Visible = bShowUA;
332 
333 			if(bKeyFile || bUserAccount)
334 			{
335 				if(!m_cbExpert.Checked)
336 					m_cbExpert.Checked = true; // Recursive, once
337 
338 				m_cbExpert.Enabled = false;
339 			}
340 			else m_cbExpert.Enabled = true;
341 
342 			// --m_uBlockUpdate;
343 		}
344 
OnCheckedPassword(object sender, EventArgs e)345 		private void OnCheckedPassword(object sender, EventArgs e)
346 		{
347 			EnableUserControls();
348 
349 			if(m_cbPassword.Checked) UIUtil.SetFocus(m_tbPassword, this);
350 		}
351 
OnCheckedKeyFile(object sender, EventArgs e)352 		private void OnCheckedKeyFile(object sender, EventArgs e)
353 		{
354 			EnableUserControls();
355 		}
356 
OnBtnOK(object sender, EventArgs e)357 		private void OnBtnOK(object sender, EventArgs e)
358 		{
359 			if(!CreateCompositeKey()) this.DialogResult = DialogResult.None;
360 		}
361 
OnBtnCancel(object sender, EventArgs e)362 		private void OnBtnCancel(object sender, EventArgs e)
363 		{
364 			m_pKey = null;
365 		}
366 
OnClickKeyFileCreate(object sender, EventArgs e)367 		private void OnClickKeyFileCreate(object sender, EventArgs e)
368 		{
369 			KeyFileCreationForm dlg = new KeyFileCreationForm();
370 			dlg.InitEx(m_ioInfo);
371 
372 			if(dlg.ShowDialog() == DialogResult.OK)
373 			{
374 				string strFile = dlg.ResultFile;
375 				if(!string.IsNullOrEmpty(strFile))
376 				{
377 					m_cmbKeyFile.Items.Add(strFile);
378 					m_cmbKeyFile.SelectedIndex = m_cmbKeyFile.Items.Count - 1;
379 				}
380 				else { Debug.Assert(false); }
381 			}
382 
383 			UIUtil.DestroyForm(dlg);
384 			EnableUserControls();
385 		}
386 
OnClickKeyFileBrowse(object sender, EventArgs e)387 		private void OnClickKeyFileBrowse(object sender, EventArgs e)
388 		{
389 			string strFilter = AppDefs.GetKeyFileFilter();
390 			OpenFileDialogEx ofd = UIUtil.CreateOpenFileDialog(KPRes.KeyFileUseExisting,
391 				strFilter, 1, null, false, AppDefs.FileDialogContext.KeyFile);
392 
393 			if(ofd.ShowDialog() != DialogResult.OK) return;
394 
395 			string strFile = ofd.FileName;
396 
397 			try
398 			{
399 				// Test whether we can read the file
400 				IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
401 				IOConnection.OpenRead(ioc).Close();
402 
403 				// Check the file size?
404 			}
405 			catch(Exception ex) { MessageService.ShowWarning(ex); return; }
406 
407 			if(!KfxFile.CanLoad(strFile))
408 			{
409 				if(!MessageService.AskYesNo(strFile + MessageService.NewParagraph +
410 					KPRes.KeyFileNoXml + MessageService.NewParagraph +
411 					KPRes.KeyFileUseAnywayQ, null, false))
412 					return;
413 			}
414 
415 			m_cmbKeyFile.Items.Add(strFile);
416 			m_cmbKeyFile.SelectedIndex = m_cmbKeyFile.Items.Count - 1;
417 
418 			EnableUserControls();
419 		}
420 
OnWinUserCheckedChanged(object sender, EventArgs e)421 		private void OnWinUserCheckedChanged(object sender, EventArgs e)
422 		{
423 			EnableUserControls();
424 		}
425 
OnFormClosed(object sender, FormClosedEventArgs e)426 		private void OnFormClosed(object sender, FormClosedEventArgs e)
427 		{
428 			GlobalWindowManager.RemoveWindow(this);
429 		}
430 
OnBtnHelp(object sender, EventArgs e)431 		private void OnBtnHelp(object sender, EventArgs e)
432 		{
433 			AppHelp.ShowHelp(AppDefs.HelpTopics.KeySources, null);
434 		}
435 
OnKeyFileSelectedIndexChanged(object sender, EventArgs e)436 		private void OnKeyFileSelectedIndexChanged(object sender, EventArgs e)
437 		{
438 			EnableUserControls();
439 		}
440 
OnFormClosing(object sender, FormClosingEventArgs e)441 		private void OnFormClosing(object sender, FormClosingEventArgs e)
442 		{
443 			CleanUpEx();
444 		}
445 
OnKeyFileLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)446 		private void OnKeyFileLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
447 		{
448 			AppHelp.ShowHelp(AppDefs.HelpTopics.KeySources,
449 				AppDefs.HelpTopics.KeySourcesKeyFile);
450 		}
451 
OnUserAccountLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)452 		private void OnUserAccountLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
453 		{
454 			AppHelp.ShowHelp(AppDefs.HelpTopics.KeySources,
455 				AppDefs.HelpTopics.KeySourcesUserAccount);
456 		}
457 
OnExpertCheckedChanged(object sender, EventArgs e)458 		private void OnExpertCheckedChanged(object sender, EventArgs e)
459 		{
460 			EnableUserControls();
461 		}
462 	}
463 }
464