Coverage Report

Created: 2025-07-01 06:58

/src/sleuthkit/tsk/fs/encryptionHelper.cpp
Line
Count
Source (jump to first uncovered line)
1
2
/*
3
 ** The Sleuth Kit
4
 **
5
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
6
 ** Copyright (c) 2024 Sleuth Kit Labs, LLC. All Rights reserved
7
 ** Copyright (c) 2010-2021 Brian Carrier.  All Rights reserved
8
 **
9
 ** This software is distributed under the Common Public License 1.0
10
 */
11
12
// Methods to handle volume encryption (currently only BitLocker is supported)
13
14
#include "encryptionHelper.h"
15
16
#ifdef HAVE_LIBMBEDTLS
17
#include "tsk/util/Bitlocker/BitlockerParser.h"
18
#endif
19
20
/**
21
* Test whether the volume is encrypted with BitLocker and initialize the parser and other fields if it is.
22
*
23
* The theory behind the return values is that we want to get the wrong password / needs password messages back
24
* to the user, which means we don't want to overwrite it with any other error codes.
25
*
26
* @param a_fs_info  The TSK_FS_INFO object. Should have the img_info and volume offset set but can otherwise be uninitialized.
27
*                      Will be updated if we find an successfully initialize BitLocker.
28
* @param a_pass     The password or recovery password to use for decryption. May be empty. If the password is not needed
29
*                      (for example if we have clear key) it will be ignored.
30
*
31
* @return 0 if:
32
* - We didn't find the Bitlocker signature
33
* - We found encryption and did all the initialization successfully
34
* - We found encryption but had an unspecified error in initialization
35
* Returns -1 if:
36
* - We got far enough to be confident that it's Bitlocker and have a specific error message to get back to the user
37
*/
38
#ifdef HAVE_LIBMBEDTLS
39
int handleBitlocker(TSK_FS_INFO* a_fs_info, const char* a_pass) {
40
  BitlockerParser* bitlockerParser = new BitlockerParser();
41
  BITLOCKER_STATUS status = bitlockerParser->initialize(a_fs_info->img_info, a_fs_info->offset, a_pass);
42
  if (status == BITLOCKER_STATUS::NOT_BITLOCKER) {
43
    delete bitlockerParser;
44
    return 0;
45
  }
46
47
  if (status != BITLOCKER_STATUS::SUCCESS) {
48
49
    // If we have some specific error cases we want to get that information back to the user
50
    if (status == BITLOCKER_STATUS::WRONG_PASSWORD) {
51
      tsk_error_reset();
52
      tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
53
      string errStr = "Incorrect password entered " + bitlockerParser->getRecoveryKeyIdStr();
54
      tsk_error_set_errstr(errStr.c_str());
55
      delete bitlockerParser;
56
      return -1;
57
58
    } else if (status == BITLOCKER_STATUS::NEED_PASSWORD) {
59
      tsk_error_reset();
60
      tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
61
      string errStr = "Password required to decrypt volume " + bitlockerParser->getRecoveryKeyIdStr();
62
      tsk_error_set_errstr(errStr.c_str());
63
      delete bitlockerParser;
64
      return -1;
65
    }
66
    else if (status == BITLOCKER_STATUS::UNSUPPORTED_KEY_PROTECTION_TYPE) {
67
      string message = "Unsupported key protection type(s): " + bitlockerParser->getUnsupportedProtectionTypes();
68
      tsk_error_reset();
69
      tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
70
      tsk_error_set_errstr(message.c_str());
71
      delete bitlockerParser;
72
      return -1;
73
    }
74
75
    // It's unlikely we're going to be able to open the file system (we found at least one BitLocker header) but it's safer to try
76
    delete bitlockerParser;
77
    return 0;
78
  }
79
80
  // Store the BitLocker data to use when reading the volume
81
  a_fs_info->encryption_type = TSK_FS_ENCRYPTION_TYPE_BITLOCKER;
82
  a_fs_info->encryption_data = (void*)bitlockerParser;
83
  a_fs_info->flags = (TSK_FS_INFO_FLAG_ENUM)(a_fs_info->flags | TSK_FS_INFO_FLAG_ENCRYPTED);
84
  a_fs_info->block_size = bitlockerParser->getSectorSize();
85
  // We don't set a_fs_info->decrypt_block here because Bitlocker needs to handle both reading in the block
86
  // and doing the decryption since some sectors may have been relocated
87
  return 0;
88
}
89
#else
90
int handleBitlocker(
91
  [[maybe_unused]] TSK_FS_INFO* a_fs_info,
92
  [[maybe_unused]] const char* a_pass)
93
0
{
94
0
  return 0;
95
0
}
96
#endif
97
98
/**
99
* Check if the volume appears to be encrypted and attempt to initialize the encryption object.
100
*
101
* @return 0 if:
102
* - There was no encryption found
103
* - We found encryption and did all the initialization successfully
104
* - We found encryption but had an unspecified error in initialization
105
* Returns -1 if:
106
* - We found encryption and got far enough that we're confident we should not continue trying to parse the file system and
107
*     have potentially useful feedback to give the user (like that the password was incorrect)
108
*/
109
#ifdef HAVE_LIBMBEDTLS
110
int handleVolumeEncryption(TSK_FS_INFO* a_fs_info, const char* a_pass) {
111
  return handleBitlocker(a_fs_info, a_pass);
112
}
113
#else
114
int handleVolumeEncryption(
115
  [[maybe_unused]] TSK_FS_INFO* a_fs_info,
116
  [[maybe_unused]] const char* a_pass)
117
0
{
118
0
  return 0;
119
0
}
120
#endif
121
122
/**
123
* Reads and decrypts one or more sectors starting at the given offset.
124
* The offset is expected to be sector-aligned and the length should be a multiple of the sector size.
125
*
126
* @param a_fs_info        The TSK_FS_INFO object
127
* @param offsetInVolume   Offset to start reading at (relative to the start of the volume)
128
* @param len              Number of bytes to read
129
* @param data             Will hold decrypted data
130
*
131
* @return Number of bytes read or -1 on error
132
*/
133
#ifdef HAVE_LIBMBEDTLS
134
ssize_t read_and_decrypt_bitlocker_blocks(TSK_FS_INFO* a_fs_info, TSK_DADDR_T offsetInVolume, size_t len, void* data) {
135
136
  if (a_fs_info->encryption_type != TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
137
    || a_fs_info->encryption_data == NULL
138
    || data == NULL) {
139
140
    return -1;
141
  }
142
143
  if (len == 0) {
144
    return 0;
145
  }
146
147
  BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
148
  return parser->readAndDecryptSectors(offsetInVolume, len, (uint8_t*)data);
149
}
150
#endif
151
152
#ifdef HAVE_LIBMBEDTLS
153
void fillEncryptionDescription(
154
  TSK_FS_INFO* a_fs_info,
155
  char* a_desc,
156
  size_t a_descLen)
157
{
158
  if (a_fs_info->encryption_type == TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
159
    && a_fs_info->encryption_data != NULL) {
160
161
    BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
162
    string descStr = parser->getDescription();
163
    strncpy(a_desc, descStr.c_str(), a_descLen - 1);
164
  }
165
}
166
#else
167
void fillEncryptionDescription(
168
  [[maybe_unused]] TSK_FS_INFO* a_fs_info,
169
  [[maybe_unused]] char* a_desc,
170
  [[maybe_unused]] size_t a_descLen)
171
0
{
172
0
}
173
#endif
174
175
/**
176
* Copys a summary of the encryption algoritm to a_desc. Expected size of description is under 100 characters.
177
*
178
* @param a_fs_info  TSK_FS_INFO object
179
* @param a_desc     Output buffer for description
180
* @param a_descLen  Size of output buffer (recommended - 256 bytes)
181
*/
182
0
void tsk_fs_get_encryption_description(TSK_FS_INFO* a_fs_info, char* a_desc, size_t a_descLen) {
183
0
  if (a_descLen == 0) {
184
0
    return;
185
0
  }
186
187
0
  memset(a_desc, 0, a_descLen);
188
0
  fillEncryptionDescription(a_fs_info, a_desc, a_descLen);
189
0
}
190
191
/**
192
* Free any memory being held by encryption objects
193
*
194
* @param a_fs_info The TSK_FS_INFO object
195
*/
196
#ifdef HAVE_LIBMBEDTLS
197
void freeEncryptionData(TSK_FS_INFO* a_fs_info) {
198
  if (a_fs_info->encryption_type == TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
199
    && a_fs_info->encryption_data != NULL) {
200
201
    BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
202
    delete parser;
203
    a_fs_info->encryption_data = NULL;
204
  }
205
  a_fs_info->encryption_type = TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_NONE;
206
}
207
#else
208
0
void freeEncryptionData([[maybe_unused]] TSK_FS_INFO* a_fs_info) {}
209
#endif