/src/sleuthkit/tsk/util/detect_encryption.c
Line | Count | Source |
1 | | /* |
2 | | ** The Sleuth Kit |
3 | | ** |
4 | | ** Copyright (c) 2021 Basis Technology Corp. All rights reserved |
5 | | ** Contact: Brian Carrier [carrier <at> sleuthkit [dot] org] |
6 | | ** |
7 | | ** This software is distributed under the Common Public License 1.0 |
8 | | ** |
9 | | */ |
10 | | |
11 | | #include "detect_encryption.h" |
12 | | |
13 | | // Scans the buffer and returns 1 if the given signature is found, 0 otherwise. |
14 | | // Looks for the signature starting at each byte from startingOffset to endingOffset. |
15 | | int |
16 | 0 | detectSignature(const char * signature, size_t signatureLen, size_t startingOffset, size_t endingOffset, const char * buf, size_t bufLen) { |
17 | |
|
18 | 0 | for (size_t offset = startingOffset; offset <= endingOffset; offset++) { |
19 | 0 | if (offset + signatureLen >= bufLen) { |
20 | 0 | return 0; |
21 | 0 | } |
22 | | |
23 | 0 | if (memcmp(signature, buf + offset, signatureLen) == 0) { |
24 | 0 | return 1; |
25 | 0 | } |
26 | 0 | } |
27 | 0 | return 0; |
28 | 0 | } |
29 | | |
30 | | // Returns 1 if LUKS signature is found, 0 otherwise |
31 | | int |
32 | 0 | detectLUKS(const char * buf, size_t len) { |
33 | 0 | const char * signature = "LUKS\xba\xbe"; |
34 | 0 | return detectSignature(signature, strlen(signature), 0, 0, buf, len); |
35 | 0 | } |
36 | | |
37 | | // Returns 1 if BitLocker signature is found, 0 otherwise |
38 | | int |
39 | 0 | detectBitLocker(const char * buf, size_t len) { |
40 | | |
41 | | // Look for the signature near the beginning of the buffer |
42 | 0 | const char * signature = "-FVE-FS-"; |
43 | 0 | return detectSignature(signature, strlen(signature), 0, 16, buf, len); |
44 | 0 | } |
45 | | |
46 | | // Returns 1 if FileVault signature is found, 0 otherwise |
47 | | int |
48 | 0 | detectFileVault(const char * buf, size_t len) { |
49 | 0 | const char * signature = "encrdsa"; |
50 | 0 | return detectSignature(signature, strlen(signature), 0, 0, buf, len); |
51 | 0 | } |
52 | | |
53 | | // Returns 1 if Check Point signature is found, 0 otherwise |
54 | | int |
55 | 0 | detectCheckPoint(const char * buf, size_t len) { |
56 | | // Look for the signature near the beginning of the buffer |
57 | 0 | const char * signature = "Protect"; |
58 | 0 | return detectSignature(signature, strlen(signature), 80, 100, buf, len); |
59 | 0 | } |
60 | | |
61 | | // Returns 1 if McAfee Safeboot signature is found, 0 otherwise |
62 | | int |
63 | 0 | detectMcAfee(const char * buf, size_t len) { |
64 | | // Look for the signature near the beginning of the buffer. Check two capitalizations. |
65 | 0 | const char * signature = "Safeboot"; |
66 | 0 | const char * altSignature = "SafeBoot"; |
67 | 0 | return (detectSignature(signature, strlen(signature), 0, 32, buf, len) |
68 | 0 | | detectSignature(altSignature, strlen(altSignature), 0, 32, buf, len)); |
69 | 0 | } |
70 | | |
71 | | // Returns 1 if Guardian Edge signature is found, 0 otherwise |
72 | | int |
73 | 0 | detectGuardianEdge(const char * buf, size_t len) { |
74 | | // Look for the signature near the beginning of the buffer |
75 | 0 | const char * signature = "PCGM"; |
76 | 0 | return detectSignature(signature, strlen(signature), 0, 32, buf, len); |
77 | 0 | } |
78 | | |
79 | | // Returns 1 if Sophos Safeguard signature is found, 0 otherwise |
80 | | int |
81 | 0 | detectSophos(const char * buf, size_t len) { |
82 | | // Look for the signature near the beginning of the buffer |
83 | 0 | const char * signature = "SGM400"; |
84 | 0 | const char * altSignature = "SGE400"; |
85 | 0 | return (detectSignature(signature, strlen(signature), 110, 150, buf, len) |
86 | 0 | | detectSignature(altSignature, strlen(altSignature), 110, 150, buf, len)); |
87 | 0 | } |
88 | | |
89 | | // Returns 1 if WinMagic SecureDoc signature is found, 0 otherwise |
90 | | int |
91 | 0 | detectWinMagic(const char * buf, size_t len) { |
92 | | // Look for the signature near the beginning of the buffer |
93 | 0 | const char * signature = "WMSD"; |
94 | 0 | return detectSignature(signature, strlen(signature), 236, 256, buf, len); |
95 | 0 | } |
96 | | |
97 | | // Returns 1 if Symantec PGP signature is found, 0 otherwise |
98 | | int |
99 | 0 | detectSymantecPGP(const char * buf, size_t len) { |
100 | | // Look for the signature near the beginning of the buffer |
101 | 0 | const char * signature = "\xeb\x48\x90PGPGUARD"; |
102 | 0 | return detectSignature(signature, strlen(signature), 0, 32, buf, len); |
103 | 0 | } |
104 | | |
105 | | // Returns the entropy of the beginning of the image. |
106 | | double |
107 | 0 | calculateEntropy(TSK_IMG_INFO * img_info, TSK_DADDR_T offset) { |
108 | | |
109 | | // Initialize frequency counts |
110 | 0 | int byteCounts[256]; |
111 | 0 | for (int i = 0; i < 256; i++) { |
112 | 0 | byteCounts[i] = 0; |
113 | 0 | } |
114 | | |
115 | | // Read in blocks of 65536 bytes, skipping the first one that is more likely to contain header data. |
116 | 0 | size_t bufLen = 65536; |
117 | 0 | char buf[65536]; |
118 | 0 | size_t bytesRead = 0; |
119 | 0 | for (uint64_t i = 1; i < 100; i++) { |
120 | 0 | if ((i + 1) * bufLen > (uint64_t)img_info->size - offset) { |
121 | 0 | break; |
122 | 0 | } |
123 | | |
124 | 0 | if (tsk_img_read(img_info, offset + i * bufLen, buf, bufLen) != (ssize_t) bufLen) { |
125 | 0 | break; |
126 | 0 | } |
127 | | |
128 | 0 | for (size_t j = 0; j < bufLen; j++) { |
129 | 0 | unsigned char b = buf[j] & 0xff; |
130 | 0 | byteCounts[b]++; |
131 | 0 | } |
132 | 0 | bytesRead += bufLen; |
133 | 0 | } |
134 | | |
135 | | // Calculate entropy |
136 | 0 | double entropy = 0.0; |
137 | 0 | double log2 = log(2); |
138 | 0 | for (int i = 0; i < 256; i++) { |
139 | 0 | if (byteCounts[i] > 0) { |
140 | 0 | double p = (double)(byteCounts[i]) / bytesRead; |
141 | 0 | entropy -= p * log(p) / log2; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | return entropy; |
145 | 0 | } |
146 | | |
147 | | /** |
148 | | * Detect volume-type encryption in the image starting at the given offset. |
149 | | * May return null on error. Note that client is responsible for freeing the result. |
150 | | * |
151 | | * @param img_info The open image |
152 | | * @param offset The offset for the beginning of the volume |
153 | | * |
154 | | * @return encryption_detected_result containing the result of the check. null for certain types of errors. |
155 | | */ |
156 | | encryption_detected_result* |
157 | 0 | detectVolumeEncryption(TSK_IMG_INFO * img_info, TSK_DADDR_T offset) { |
158 | |
|
159 | 0 | encryption_detected_result* result = (encryption_detected_result*)tsk_malloc(sizeof(encryption_detected_result)); |
160 | 0 | if (result == NULL) { |
161 | 0 | return result; |
162 | 0 | } |
163 | 0 | result->encryptionType = ENCRYPTION_DETECTED_NONE; |
164 | 0 | result->desc[0] = '\0'; |
165 | |
|
166 | 0 | if (img_info == NULL) { |
167 | 0 | return result; |
168 | 0 | } |
169 | 0 | if (offset > (uint64_t)img_info->size) { |
170 | 0 | return result; |
171 | 0 | } |
172 | | |
173 | | // Read the beginning of the image. There should be room for all the signature searches. |
174 | 0 | size_t len = 1024; |
175 | 0 | char* buf = (char*)tsk_malloc(len); |
176 | 0 | if (buf == NULL) { |
177 | 0 | return result; |
178 | 0 | } |
179 | 0 | if (tsk_img_read(img_info, offset, buf, len) != (ssize_t)len) { |
180 | 0 | free(buf); |
181 | 0 | return result; |
182 | 0 | } |
183 | | |
184 | | // Look for BitLocker signature |
185 | 0 | if (detectBitLocker(buf, len)) { |
186 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
187 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "BitLocker"); |
188 | 0 | free(buf); |
189 | 0 | return result; |
190 | 0 | } |
191 | | |
192 | | // Look for Linux Unified Key Setup (LUKS) signature |
193 | 0 | if (detectLUKS(buf, len)) { |
194 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
195 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "LUKS"); |
196 | 0 | free(buf); |
197 | 0 | return result; |
198 | 0 | } |
199 | | |
200 | | // Look for FileVault |
201 | 0 | if (detectFileVault(buf, len)) { |
202 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
203 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "FileVault"); |
204 | 0 | free(buf); |
205 | 0 | return result; |
206 | 0 | } |
207 | | |
208 | 0 | free(buf); |
209 | | |
210 | | // Final test - check entropy |
211 | 0 | double entropy = calculateEntropy(img_info, offset); |
212 | 0 | if (entropy > 7.5) { |
213 | 0 | result->encryptionType = ENCRYPTION_DETECTED_ENTROPY; |
214 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "High entropy (%1.2lf)", entropy); |
215 | 0 | return result; |
216 | 0 | } |
217 | | |
218 | 0 | return result; |
219 | 0 | } |
220 | | |
221 | | /** |
222 | | * Detect full disk encryption in the image starting at the given offset. |
223 | | * May return null on error. Note that client is responsible for freeing the result. |
224 | | * |
225 | | * @param img_info The open image |
226 | | * @param offset The offset for the beginning of the image TODO TODO do we need this?? |
227 | | * |
228 | | * @return encryption_detected_result containing the result of the check. null for certain types of errors. |
229 | | */ |
230 | | encryption_detected_result* |
231 | 0 | detectDiskEncryption(TSK_IMG_INFO * img_info, TSK_DADDR_T offset) { |
232 | |
|
233 | 0 | encryption_detected_result* result = (encryption_detected_result*)tsk_malloc(sizeof(encryption_detected_result)); |
234 | 0 | if (result == NULL) { |
235 | 0 | return result; |
236 | 0 | } |
237 | 0 | result->encryptionType = ENCRYPTION_DETECTED_NONE; |
238 | 0 | result->desc[0] = '\0'; |
239 | |
|
240 | 0 | if (img_info == NULL) { |
241 | 0 | return result; |
242 | 0 | } |
243 | 0 | if (offset > (uint64_t)img_info->size) { |
244 | 0 | return result; |
245 | 0 | } |
246 | | |
247 | | // Read the beginning of the image. There should be room for all the signature searches. |
248 | 0 | size_t len = 1024; |
249 | 0 | char* buf = (char*)tsk_malloc(len); |
250 | 0 | if (buf == NULL) { |
251 | 0 | return result; |
252 | 0 | } |
253 | 0 | if (tsk_img_read(img_info, offset, buf, len) != (ssize_t)len) { |
254 | 0 | free(buf); |
255 | 0 | return result; |
256 | 0 | } |
257 | | |
258 | | // Look for Symatec PGP signature |
259 | 0 | if (detectSymantecPGP(buf, len)) { |
260 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
261 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "Symantec PGP"); |
262 | 0 | free(buf); |
263 | 0 | return result; |
264 | 0 | } |
265 | | |
266 | | // Look for McAfee Safeboot signature |
267 | 0 | if (detectMcAfee(buf, len)) { |
268 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
269 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "McAfee Safeboot"); |
270 | 0 | free(buf); |
271 | 0 | return result; |
272 | 0 | } |
273 | | |
274 | | // Look for Sophos Safeguard |
275 | 0 | if (detectSophos(buf, len)) { |
276 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
277 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "Sophos Safeguard"); |
278 | 0 | free(buf); |
279 | 0 | return result; |
280 | 0 | } |
281 | | |
282 | | // Look for Guardian Edge signature |
283 | 0 | if (detectGuardianEdge(buf, len)) { |
284 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
285 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "Guardian Edge"); |
286 | 0 | free(buf); |
287 | 0 | return result; |
288 | 0 | } |
289 | | |
290 | | // Look for Check Point signature |
291 | 0 | if (detectCheckPoint(buf, len)) { |
292 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
293 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "Check Point"); |
294 | 0 | free(buf); |
295 | 0 | return result; |
296 | 0 | } |
297 | | |
298 | | // Look for WinMagic SecureDoc signature |
299 | 0 | if (detectWinMagic(buf, len)) { |
300 | 0 | result->encryptionType = ENCRYPTION_DETECTED_SIGNATURE; |
301 | 0 | snprintf(result->desc, TSK_ERROR_STRING_MAX_LENGTH, "WinMagic SecureDoc"); |
302 | 0 | free(buf); |
303 | 0 | return result; |
304 | 0 | } |
305 | 0 | free(buf); |
306 | 0 | return result; |
307 | 0 | } |
308 | | |
309 | | |