Coverage Report

Created: 2025-10-10 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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