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.Diagnostics;
22 using System.IO;
23 using System.Security;
24 using System.Text;
25 
26 using KeePassLib.Cryptography;
27 using KeePassLib.Resources;
28 using KeePassLib.Security;
29 using KeePassLib.Serialization;
30 using KeePassLib.Utility;
31 
32 namespace KeePassLib.Keys
33 {
34 	public sealed class KcpKeyFile : IUserKey
35 	{
36 		private readonly string m_strPath;
37 		private readonly ProtectedBinary m_pbKeyData;
38 
39 		public string Path
40 		{
41 			get { return m_strPath; }
42 		}
43 
44 		public ProtectedBinary KeyData
45 		{
46 			get { return m_pbKeyData; }
47 		}
48 
KcpKeyFile(string strKeyFile)49 		public KcpKeyFile(string strKeyFile) :
50 			this(IOConnectionInfo.FromPath(strKeyFile), false)
51 		{
52 		}
53 
KcpKeyFile(string strKeyFile, bool bThrowIfDbFile)54 		public KcpKeyFile(string strKeyFile, bool bThrowIfDbFile) :
55 			this(IOConnectionInfo.FromPath(strKeyFile), bThrowIfDbFile)
56 		{
57 		}
58 
KcpKeyFile(IOConnectionInfo iocKeyFile)59 		public KcpKeyFile(IOConnectionInfo iocKeyFile) :
60 			this(iocKeyFile, false)
61 		{
62 		}
63 
KcpKeyFile(IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)64 		public KcpKeyFile(IOConnectionInfo iocKeyFile, bool bThrowIfDbFile)
65 		{
66 			if(iocKeyFile == null) throw new ArgumentNullException("iocKeyFile");
67 
68 			byte[] pbFileData;
69 			using(Stream s = IOConnection.OpenRead(iocKeyFile))
70 			{
71 				pbFileData = MemUtil.Read(s);
72 			}
73 			if(pbFileData == null) throw new IOException();
74 
75 			if(bThrowIfDbFile && (pbFileData.Length >= 8))
76 			{
77 				uint uSig1 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 0, 4));
78 				uint uSig2 = MemUtil.BytesToUInt32(MemUtil.Mid(pbFileData, 4, 4));
79 
80 				if(((uSig1 == KdbxFile.FileSignature1) &&
81 					(uSig2 == KdbxFile.FileSignature2)) ||
82 					((uSig1 == KdbxFile.FileSignaturePreRelease1) &&
83 					(uSig2 == KdbxFile.FileSignaturePreRelease2)) ||
84 					((uSig1 == KdbxFile.FileSignatureOld1) &&
85 					(uSig2 == KdbxFile.FileSignatureOld2)))
86 #if KeePassLibSD
87 					throw new Exception(KLRes.KeyFileDbSel);
88 #else
89 					throw new InvalidDataException(KLRes.KeyFileDbSel);
90 #endif
91 			}
92 
93 			byte[] pbKey = LoadKeyFile(pbFileData);
94 
95 			m_strPath = iocKeyFile.Path;
96 			m_pbKeyData = new ProtectedBinary(true, pbKey);
97 
98 			MemUtil.ZeroByteArray(pbKey);
99 			MemUtil.ZeroByteArray(pbFileData);
100 		}
101 
LoadKeyFile(byte[] pbFileData)102 		private static byte[] LoadKeyFile(byte[] pbFileData)
103 		{
104 			if(pbFileData == null) throw new ArgumentNullException("pbFileData");
105 
106 			byte[] pbKey = LoadKeyFileXml(pbFileData);
107 			if(pbKey != null) return pbKey;
108 
109 			int cb = pbFileData.Length;
110 			if(cb == 32) return pbFileData;
111 
112 			if(cb == 64)
113 			{
114 				pbKey = LoadKeyFileHex(pbFileData);
115 				if(pbKey != null) return pbKey;
116 			}
117 
118 			return CryptoUtil.HashSha256(pbFileData);
119 		}
120 
LoadKeyFileXml(byte[] pbFileData)121 		private static byte[] LoadKeyFileXml(byte[] pbFileData)
122 		{
123 			KfxFile kf;
124 			try
125 			{
126 				using(MemoryStream ms = new MemoryStream(pbFileData, false))
127 				{
128 					kf = KfxFile.Load(ms);
129 				}
130 			}
131 			catch(Exception) { return null; }
132 
133 			// We have a syntactically valid XML key file;
134 			// failing to verify the key should throw an exception
135 			return ((kf != null) ? kf.GetKey() : null);
136 		}
137 
LoadKeyFileHex(byte[] pbFileData)138 		private static byte[] LoadKeyFileHex(byte[] pbFileData)
139 		{
140 			if(pbFileData == null) { Debug.Assert(false); return null; }
141 
142 			try
143 			{
144 				int cc = pbFileData.Length;
145 				if((cc & 1) != 0) { Debug.Assert(false); return null; }
146 
147 				if(!StrUtil.IsHexString(pbFileData, true)) return null;
148 
149 				string strHex = StrUtil.Utf8.GetString(pbFileData);
150 				return MemUtil.HexStringToByteArray(strHex);
151 			}
152 			catch(Exception) { Debug.Assert(false); }
153 
154 			return null;
155 		}
156 
Create(string strFilePath, byte[] pbAdditionalEntropy)157 		public static void Create(string strFilePath, byte[] pbAdditionalEntropy)
158 		{
159 			Create(strFilePath, pbAdditionalEntropy, 0);
160 		}
161 
Create(string strFilePath, byte[] pbAdditionalEntropy, ulong uVersion)162 		internal static void Create(string strFilePath, byte[] pbAdditionalEntropy,
163 			ulong uVersion)
164 		{
165 			byte[] pbRandom = CryptoRandom.Instance.GetRandomBytes(32);
166 			if((pbRandom == null) || (pbRandom.Length != 32))
167 				throw new SecurityException();
168 
169 			byte[] pbKey;
170 			if((pbAdditionalEntropy == null) || (pbAdditionalEntropy.Length == 0))
171 				pbKey = pbRandom;
172 			else
173 			{
174 				int cbAdd = pbAdditionalEntropy.Length;
175 				int cbRnd = pbRandom.Length;
176 
177 				byte[] pbCmp = new byte[cbAdd + cbRnd];
178 				Array.Copy(pbAdditionalEntropy, 0, pbCmp, 0, cbAdd);
179 				Array.Copy(pbRandom, 0, pbCmp, cbAdd, cbRnd);
180 
181 				pbKey = CryptoUtil.HashSha256(pbCmp);
182 
183 				MemUtil.ZeroByteArray(pbCmp);
184 			}
185 
186 			KfxFile kf = KfxFile.Create(uVersion, pbKey, null);
187 
188 			IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
189 			using(Stream s = IOConnection.OpenWrite(ioc))
190 			{
191 				kf.Save(s);
192 			}
193 
194 			MemUtil.ZeroByteArray(pbKey);
195 			MemUtil.ZeroByteArray(pbRandom);
196 		}
197 	}
198 }
199