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.IO; 24 using System.Reflection; 25 using System.Text; 26 27 #if !KeePassUAP 28 using System.Security.Cryptography; 29 #endif 30 31 using KeePassLib.Native; 32 using KeePassLib.Utility; 33 34 namespace KeePassLib.Cryptography 35 { 36 public static class CryptoUtil 37 { 38 private static bool? g_obProtData = null; 39 public static bool IsProtectedDataSupported 40 { 41 get 42 { 43 if(g_obProtData.HasValue) return g_obProtData.Value; 44 45 bool b = false; 46 try 47 { 48 Random r = CryptoRandom.NewWeakRandom(); 49 50 byte[] pbData = new byte[137]; 51 r.NextBytes(pbData); 52 53 byte[] pbEnt = new byte[41]; 54 r.NextBytes(pbEnt); 55 56 byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt, 57 DataProtectionScope.CurrentUser); 58 if((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData)) 59 { 60 byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt, 61 DataProtectionScope.CurrentUser); 62 if((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData)) 63 b = true; 64 } 65 } 66 catch(Exception) { Debug.Assert(false); } 67 68 Debug.Assert(b); // Should be supported on all systems 69 g_obProtData = b; 70 return b; 71 } 72 } 73 HashSha256(byte[] pbData)74 public static byte[] HashSha256(byte[] pbData) 75 { 76 if(pbData == null) throw new ArgumentNullException("pbData"); 77 78 return HashSha256(pbData, 0, pbData.Length); 79 } 80 HashSha256(byte[] pbData, int iOffset, int cbCount)81 public static byte[] HashSha256(byte[] pbData, int iOffset, int cbCount) 82 { 83 if(pbData == null) throw new ArgumentNullException("pbData"); 84 85 #if DEBUG 86 byte[] pbCopy = new byte[pbData.Length]; 87 Array.Copy(pbData, pbCopy, pbData.Length); 88 #endif 89 90 byte[] pbHash; 91 using(SHA256Managed h = new SHA256Managed()) 92 { 93 pbHash = h.ComputeHash(pbData, iOffset, cbCount); 94 } 95 96 #if DEBUG 97 // Ensure the data has not been modified 98 Debug.Assert(MemUtil.ArraysEqual(pbData, pbCopy)); 99 100 Debug.Assert((pbHash != null) && (pbHash.Length == 32)); 101 byte[] pbZero = new byte[32]; 102 Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero)); 103 #endif 104 105 return pbHash; 106 } 107 HashSha256(string strFilePath)108 internal static byte[] HashSha256(string strFilePath) 109 { 110 byte[] pbHash = null; 111 112 using(FileStream fs = new FileStream(strFilePath, FileMode.Open, 113 FileAccess.Read, FileShare.Read)) 114 { 115 using(SHA256Managed h = new SHA256Managed()) 116 { 117 pbHash = h.ComputeHash(fs); 118 } 119 } 120 121 return pbHash; 122 } 123 124 /// <summary> 125 /// Create a cryptographic key of length <paramref name="cbOut" /> 126 /// (in bytes) from <paramref name="pbIn" />. 127 /// </summary> ResizeKey(byte[] pbIn, int iInOffset, int cbIn, int cbOut)128 public static byte[] ResizeKey(byte[] pbIn, int iInOffset, 129 int cbIn, int cbOut) 130 { 131 if(pbIn == null) throw new ArgumentNullException("pbIn"); 132 if(cbOut < 0) throw new ArgumentOutOfRangeException("cbOut"); 133 134 if(cbOut == 0) return MemUtil.EmptyByteArray; 135 136 byte[] pbHash; 137 if(cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn); 138 else 139 { 140 using(SHA512Managed h = new SHA512Managed()) 141 { 142 pbHash = h.ComputeHash(pbIn, iInOffset, cbIn); 143 } 144 } 145 146 if(cbOut == pbHash.Length) return pbHash; 147 148 byte[] pbRet = new byte[cbOut]; 149 if(cbOut < pbHash.Length) 150 Array.Copy(pbHash, pbRet, cbOut); 151 else 152 { 153 int iPos = 0; 154 ulong r = 0; 155 while(iPos < cbOut) 156 { 157 Debug.Assert(pbHash.Length == 64); 158 using(HMACSHA256 h = new HMACSHA256(pbHash)) 159 { 160 byte[] pbR = MemUtil.UInt64ToBytes(r); 161 byte[] pbPart = h.ComputeHash(pbR); 162 163 int cbCopy = Math.Min(cbOut - iPos, pbPart.Length); 164 Debug.Assert(cbCopy > 0); 165 166 Array.Copy(pbPart, 0, pbRet, iPos, cbCopy); 167 iPos += cbCopy; 168 ++r; 169 170 MemUtil.ZeroByteArray(pbPart); 171 } 172 } 173 Debug.Assert(iPos == cbOut); 174 } 175 176 #if DEBUG 177 byte[] pbZero = new byte[pbHash.Length]; 178 Debug.Assert(!MemUtil.ArraysEqual(pbHash, pbZero)); 179 #endif 180 MemUtil.ZeroByteArray(pbHash); 181 return pbRet; 182 } 183 184 #if !KeePassUAP 185 private static bool? g_obAesCsp = null; CreateAes()186 internal static SymmetricAlgorithm CreateAes() 187 { 188 if(g_obAesCsp.HasValue) 189 return (g_obAesCsp.Value ? CreateAesCsp() : new RijndaelManaged()); 190 191 SymmetricAlgorithm a = CreateAesCsp(); 192 g_obAesCsp = (a != null); 193 return (a ?? new RijndaelManaged()); 194 } 195 CreateAesCsp()196 private static SymmetricAlgorithm CreateAesCsp() 197 { 198 try 199 { 200 // On Windows, the CSP implementation is only minimally 201 // faster (and for key derivations it's not used anyway, 202 // as KeePass uses a native implementation based on 203 // CNG/BCrypt, which is much faster) 204 if(!NativeLib.IsUnix()) return null; 205 206 string strFqn = Assembly.CreateQualifiedName( 207 "System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 208 "System.Security.Cryptography.AesCryptoServiceProvider"); 209 210 Type t = Type.GetType(strFqn); 211 if(t == null) return null; 212 213 return (Activator.CreateInstance(t) as SymmetricAlgorithm); 214 } 215 catch(Exception) { Debug.Assert(false); } 216 217 return null; 218 } 219 #endif 220 ProtectData(byte[] pb, byte[] pbOptEntropy, DataProtectionScope s)221 public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy, 222 DataProtectionScope s) 223 { 224 return ProtectDataPriv(pb, true, pbOptEntropy, s); 225 } 226 UnprotectData(byte[] pb, byte[] pbOptEntropy, DataProtectionScope s)227 public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy, 228 DataProtectionScope s) 229 { 230 return ProtectDataPriv(pb, false, pbOptEntropy, s); 231 } 232 ProtectDataPriv(byte[] pb, bool bProtect, byte[] pbOptEntropy, DataProtectionScope s)233 private static byte[] ProtectDataPriv(byte[] pb, bool bProtect, 234 byte[] pbOptEntropy, DataProtectionScope s) 235 { 236 if(pb == null) throw new ArgumentNullException("pb"); 237 238 if((pbOptEntropy != null) && (pbOptEntropy.Length == 0)) 239 pbOptEntropy = null; 240 241 if(CryptoUtil.IsProtectedDataSupported) 242 { 243 if(bProtect) 244 return ProtectedData.Protect(pb, pbOptEntropy, s); 245 return ProtectedData.Unprotect(pb, pbOptEntropy, s); 246 } 247 248 Debug.Assert(false); 249 byte[] pbCopy = new byte[pb.Length]; 250 Array.Copy(pb, pbCopy, pb.Length); 251 return pbCopy; 252 } 253 } 254 } 255