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