File: encryptionHelper.cpp

package info (click to toggle)
sleuthkit 4.14.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,268 kB
  • sloc: ansic: 143,839; cpp: 54,644; java: 39,009; xml: 2,417; python: 1,085; perl: 874; makefile: 451; sh: 196
file content (182 lines) | stat: -rw-r--r-- 6,711 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

/*
 ** The Sleuth Kit
 **
 ** Brian Carrier [carrier <at> sleuthkit [dot] org]
 ** Copyright (c) 2024 Sleuth Kit Labs, LLC. All Rights reserved
 ** Copyright (c) 2010-2021 Brian Carrier.  All Rights reserved
 **
 ** This software is distributed under the Common Public License 1.0
 */

// Methods to handle volume encryption (currently only BitLocker is supported)

#include "encryptionHelper.h"

#ifdef HAVE_LIBMBEDTLS
#include "tsk/util/Bitlocker/BitlockerParser.h"
#endif

/**
* Test whether the volume is encrypted with BitLocker and initialize the parser and other fields if it is.
* 
* The theory behind the return values is that we want to get the wrong password / needs password messages back
* to the user, which means we don't want to overwrite it with any other error codes.
* 
* @param a_fs_info  The TSK_FS_INFO object. Should have the img_info and volume offset set but can otherwise be uninitialized.
*                      Will be updated if we find an successfully initialize BitLocker.
* @param a_pass     The password or recovery password to use for decryption. May be empty. If the password is not needed
*                      (for example if we have clear key) it will be ignored.
* 
* @return 0 if:
* - We didn't find the Bitlocker signature
* - We found encryption and did all the initialization successfully
* - We found encryption but had an unspecified error in initialization
* Returns -1 if:
* - We got far enough to be confident that it's Bitlocker and have a specific error message to get back to the user
*/
int handleBitlocker(TSK_FS_INFO* a_fs_info, const char* a_pass) {
#ifdef HAVE_LIBMBEDTLS
	BitlockerParser* bitlockerParser = new BitlockerParser();
	BITLOCKER_STATUS status = bitlockerParser->initialize(a_fs_info->img_info, a_fs_info->offset, a_pass);
	if (status == BITLOCKER_STATUS::NOT_BITLOCKER) {
		delete bitlockerParser;
		return 0;
	}

	if (status != BITLOCKER_STATUS::SUCCESS) {

		// If we have some specific error cases we want to get that information back to the user
		if (status == BITLOCKER_STATUS::WRONG_PASSWORD) {
			tsk_error_reset();
			tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
			string errStr = "Incorrect password entered " + bitlockerParser->getRecoveryKeyIdStr();
			tsk_error_set_errstr(errStr.c_str());
			delete bitlockerParser;
			return -1;

		} else if (status == BITLOCKER_STATUS::NEED_PASSWORD) {
			tsk_error_reset();
			tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
			string errStr = "Password required to decrypt volume " + bitlockerParser->getRecoveryKeyIdStr();
			tsk_error_set_errstr(errStr.c_str());
			delete bitlockerParser;
			return -1;
		}
		else if (status == BITLOCKER_STATUS::UNSUPPORTED_KEY_PROTECTION_TYPE) {
			string message = "Unsupported key protection type(s): " + bitlockerParser->getUnsupportedProtectionTypes();
			tsk_error_reset();
			tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
			tsk_error_set_errstr(message.c_str());
			delete bitlockerParser;
			return -1;
		}

		// 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
		delete bitlockerParser;
		return 0;
	}

	// Store the BitLocker data to use when reading the volume
	a_fs_info->encryption_type = TSK_FS_ENCRYPTION_TYPE_BITLOCKER;
	a_fs_info->encryption_data = (void*)bitlockerParser;
	a_fs_info->flags = (TSK_FS_INFO_FLAG_ENUM)(a_fs_info->flags | TSK_FS_INFO_FLAG_ENCRYPTED);
	a_fs_info->block_size = bitlockerParser->getSectorSize();
	// We don't set a_fs_info->decrypt_block here because Bitlocker needs to handle both reading in the block
	// and doing the decryption since some sectors may have been relocated
#endif
	return 0;
}

/**
* Check if the volume appears to be encrypted and attempt to initialize the encryption object.
* 
* @return 0 if:
* - There was no encryption found
* - We found encryption and did all the initialization successfully
* - We found encryption but had an unspecified error in initialization  
* Returns -1 if:
* - We found encryption and got far enough that we're confident we should not continue trying to parse the file system and
*     have potentially useful feedback to give the user (like that the password was incorrect)
*/
int handleVolumeEncryption(TSK_FS_INFO* a_fs_info, const char* a_pass) {
	int ret = 0;
#ifdef HAVE_LIBMBEDTLS
	ret = handleBitlocker(a_fs_info, a_pass);
#endif

	return ret;
}

/**
* Reads and decrypts one or more sectors starting at the given offset.
* The offset is expected to be sector-aligned and the length should be a multiple of the sector size.
*
* @param a_fs_info        The TSK_FS_INFO object
* @param offsetInVolume   Offset to start reading at (relative to the start of the volume)
* @param len              Number of bytes to read
* @param data             Will hold decrypted data
*
* @return Number of bytes read or -1 on error
*/
#ifdef HAVE_LIBMBEDTLS
ssize_t read_and_decrypt_bitlocker_blocks(TSK_FS_INFO* a_fs_info, TSK_DADDR_T offsetInVolume, size_t len, void* data) {

	if (a_fs_info->encryption_type != TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
		|| a_fs_info->encryption_data == NULL
		|| data == NULL) {

		return -1;
	}

	if (len == 0) {
		return 0;
	}

	BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
	return parser->readAndDecryptSectors(offsetInVolume, len, (uint8_t*)data);
}
#endif

/**
* Copys a summary of the encryption algoritm to a_desc. Expected size of description is under 100 characters.
* 
* @param a_fs_info  TSK_FS_INFO object
* @param a_desc     Output buffer for description
* @param a_descLen  Size of output buffer (recommended - 256 bytes)
*/
void tsk_fs_get_encryption_description(TSK_FS_INFO* a_fs_info, char* a_desc, size_t a_descLen) {
	if (a_descLen <= 0) {
		return;
	}

	memset(a_desc, 0, a_descLen);

#ifdef HAVE_LIBMBEDTLS
	if (a_fs_info->encryption_type == TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
		&& a_fs_info->encryption_data != NULL) {

		BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
		string descStr = parser->getDescription();
		strncpy(a_desc, descStr.c_str(), a_descLen - 1);
	}
#endif
}

/**
* Free any memory being held by encryption objects
* 
* @param a_fs_info The TSK_FS_INFO object
*/
void freeEncryptionData(TSK_FS_INFO* a_fs_info) {
#ifdef HAVE_LIBMBEDTLS
	if (a_fs_info->encryption_type == TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
		&& a_fs_info->encryption_data != NULL) {

		BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
		delete parser;
		a_fs_info->encryption_data = NULL;
	}
	a_fs_info->encryption_type = TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_NONE;
#endif
}