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 
33 using KeePassLib.Keys;
34 using KeePassLib.Serialization;
35 using KeePassLib.Utility;
36 
37 namespace KeePass.Forms
38 {
39 	public partial class KeyFileCreationForm : Form
40 	{
41 		private sealed class KfcfInfo
42 		{
43 			public readonly ulong Version;
44 			public readonly string Name;
45 
KfcfInfo(ulong uVersion, string strName)46 			public KfcfInfo(ulong uVersion, string strName)
47 			{
48 				this.Version = uVersion;
49 				this.Name = strName;
50 			}
51 		}
52 
53 		private IOConnectionInfo m_ioInfo = new IOConnectionInfo();
54 
55 		private int m_cBlockUIUpdate = 0;
56 
57 		private readonly KfcfInfo[] m_vNewFormat = new KfcfInfo[] {
58 			new KfcfInfo(0x0002000000000000, "2.0 (" + KPRes.Recommended + ")"),
59 			new KfcfInfo(0x0001000000000000, "1.0 (" + KPRes.CompatWithOldVer + ")")
60 		};
61 		private readonly KfcfInfo[] m_vRecFormat = new KfcfInfo[] {
62 			new KfcfInfo(0x0002000000000000, "2.0"),
63 			new KfcfInfo(0x0001000000000000, "1.0")
64 		};
65 
66 		private string m_strResultFile = null;
67 		public string ResultFile
68 		{
69 			get { return m_strResultFile; }
70 		}
71 
KeyFileCreationForm()72 		public KeyFileCreationForm()
73 		{
74 			InitializeComponent();
75 			GlobalWindowManager.InitializeForm(this);
76 		}
77 
InitEx(IOConnectionInfo ioInfo)78 		public void InitEx(IOConnectionInfo ioInfo)
79 		{
80 			if(ioInfo != null) m_ioInfo = ioInfo;
81 		}
82 
OnFormLoad(object sender, EventArgs e)83 		private void OnFormLoad(object sender, EventArgs e)
84 		{
85 			++m_cBlockUIUpdate;
86 
87 			GlobalWindowManager.AddWindow(this);
88 
89 			BannerFactory.CreateBannerEx(this, m_bannerImage,
90 				KeePass.Properties.Resources.B48x48_KGPG_Gen,
91 				KPRes.KeyFileCreateTitle, KPRes.KeyFileCreate + ".");
92 			this.Icon = AppIcons.Default;
93 			this.Text = KPRes.KeyFileCreateTitle;
94 
95 			FontUtil.AssignDefaultBold(m_rbCreate);
96 			FontUtil.AssignDefaultBold(m_rbRecreate);
97 			FontUtil.AssignDefaultMono(m_tbRecKeyHash, false);
98 			FontUtil.AssignDefaultMono(m_tbRecKey, false);
99 
100 			m_rbCreate.Checked = true;
101 			m_cbNewEntropy.Checked = true;
102 
103 			Debug.Assert(!m_cmbNewFormat.Sorted);
104 			foreach(KfcfInfo kfi in m_vNewFormat)
105 				m_cmbNewFormat.Items.Add(kfi.Name);
106 			m_cmbNewFormat.SelectedIndex = 0;
107 
108 			Debug.Assert(!m_cmbRecFormat.Sorted);
109 			foreach(KfcfInfo kfi in m_vRecFormat)
110 				m_cmbRecFormat.Items.Add(kfi.Name);
111 			m_cmbRecFormat.SelectedIndex = 0;
112 
113 			m_rbCreate.CheckedChanged += this.OnShouldUpdateUIState;
114 			m_rbRecreate.CheckedChanged += this.OnShouldUpdateUIState;
115 			m_cmbRecFormat.SelectedIndexChanged += this.OnShouldUpdateUIState;
116 			m_tbRecKey.TextChanged += this.OnShouldUpdateUIState;
117 
118 			--m_cBlockUIUpdate;
119 			UpdateUIState();
120 		}
121 
OnFormClosed(object sender, FormClosedEventArgs e)122 		private void OnFormClosed(object sender, FormClosedEventArgs e)
123 		{
124 			GlobalWindowManager.RemoveWindow(this);
125 		}
126 
UpdateUIState()127 		private void UpdateUIState()
128 		{
129 			if(m_cBlockUIUpdate > 0) return;
130 
131 			bool bCreate = m_rbCreate.Checked;
132 			bool bRecreate = m_rbRecreate.Checked;
133 			KfcfInfo kfiRec = m_vRecFormat[m_cmbRecFormat.SelectedIndex];
134 
135 			UIUtil.SetEnabledFast(bCreate, m_lblNewFormat, m_cmbNewFormat,
136 				m_cbNewEntropy);
137 
138 			UIUtil.SetEnabledFast(bRecreate, m_lblRecFormat, m_cmbRecFormat,
139 				m_lblRecKey, m_tbRecKey);
140 			UIUtil.SetEnabledFast(bRecreate && (kfiRec.Version == 0x0002000000000000),
141 				m_lblRecKeyHash, m_tbRecKeyHash);
142 
143 			UIUtil.SetEnabledFast(bCreate || (bRecreate &&
144 				(m_tbRecKey.Text.Trim().Length != 0)), m_btnOK);
145 		}
146 
OnShouldUpdateUIState(object sender, EventArgs e)147 		private void OnShouldUpdateUIState(object sender, EventArgs e)
148 		{
149 			UpdateUIState();
150 		}
151 
OnBtnOK(object sender, EventArgs e)152 		private void OnBtnOK(object sender, EventArgs e)
153 		{
154 			string strResultFile = null;
155 
156 			try
157 			{
158 				if(m_rbCreate.Checked)
159 					strResultFile = CreateKeyFile();
160 				else if(m_rbRecreate.Checked)
161 					strResultFile = RecreateKeyFile();
162 				else { Debug.Assert(false); throw new NotSupportedException(); }
163 			}
164 			catch(Exception ex) { MessageService.ShowWarning(ex); }
165 
166 			if(string.IsNullOrEmpty(strResultFile))
167 				this.DialogResult = DialogResult.None;
168 			else m_strResultFile = strResultFile;
169 		}
170 
GetKeyFilePath()171 		private string GetKeyFilePath()
172 		{
173 			string strExt = AppDefs.FileExtension.KeyFile;
174 			string strFilter = AppDefs.GetKeyFileFilter();
175 
176 			string strName = UrlUtil.StripExtension(UrlUtil.GetFileName(m_ioInfo.Path));
177 			if(string.IsNullOrEmpty(strName)) strName = KPRes.KeyFileSafe;
178 
179 			SaveFileDialogEx sfd = UIUtil.CreateSaveFileDialog(KPRes.KeyFileCreateTitle,
180 				strName + "." + strExt, strFilter, 1, strExt, AppDefs.FileDialogContext.KeyFile);
181 
182 			if(sfd.ShowDialog() == DialogResult.OK) return sfd.FileName;
183 			return null;
184 		}
185 
CreateKeyFile()186 		private string CreateKeyFile()
187 		{
188 			byte[] pbEntropy = null;
189 			if(m_cbNewEntropy.Checked)
190 			{
191 				EntropyForm dlg = new EntropyForm();
192 				if(dlg.ShowDialog() == DialogResult.OK)
193 					pbEntropy = dlg.GeneratedEntropy;
194 				UIUtil.DestroyForm(dlg);
195 
196 				if(pbEntropy == null) return null;
197 			}
198 
199 			string strFilePath = GetKeyFilePath();
200 			if(string.IsNullOrEmpty(strFilePath)) return null;
201 
202 			KcpKeyFile.Create(strFilePath, pbEntropy, m_vNewFormat[
203 				m_cmbNewFormat.SelectedIndex].Version);
204 			return strFilePath;
205 		}
206 
RecreateKeyFile()207 		private string RecreateKeyFile()
208 		{
209 			ulong uVersion = m_vRecFormat[m_cmbRecFormat.SelectedIndex].Version;
210 
211 			string strHash = StrUtil.RemoveWhiteSpace(m_tbRecKeyHash.Text);
212 			// If the hash is empty, set it to null in order to generate one
213 			if(strHash.Length == 0) strHash = null;
214 
215 			KfxFile kf = KfxFile.Create(uVersion, m_tbRecKey.Text, strHash);
216 
217 			// Ask for the file path after verifying the key hash
218 			string strFilePath = GetKeyFilePath();
219 			if(string.IsNullOrEmpty(strFilePath)) return null;
220 
221 			IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
222 			using(Stream s = IOConnection.OpenWrite(ioc))
223 			{
224 				kf.Save(s);
225 			}
226 
227 			return strFilePath;
228 		}
229 	}
230 }
231